UIAlertController customization - ios

I'm trying to customize my UIAlertController with a dark theme.
I'm aiming for something like this
I'm trying different things, including the suggestion I found here https://stackoverflow.com/a/29122883/1817873, but for some reason only the first button gets colored while the cancel button keeps staying white.
Here is my code:
UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
UIAlertAction *deleteAction = [UIAlertAction actionWithTitle:#"Confirm" style:UIAlertActionStyleDefault handler:nil];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:#"Cancel" style:UIAlertActionStyleCancel handler:nil];
[alert addAction:deleteAction];
[alert addAction:cancelAction];
UIView *firstView = alert.view.subviews.firstObject;
UIView *nextView = firstView.subviews.firstObject;
nextView.backgroundColor = [UIColor blackColor];
Any ideas?

Any ideas?
Yes. Give up. An alert controller is very limited, and you should stick to what it does. But it is just a presented view controller, and nothing stops you from making your own presented view controller that looks and acts just like an alert, and since it is your view controller, it's your view and you can do whatever you like.

If you're still struggling with this, I have a library that may be of help. It lets you create custom action sheets. It has a bunch of built-in types and can be extended and restyled as well.

Related

Objective C - I would like UIAlertController to not be removed when button clicked

I would like to present a UIAlertController with 2 buttons.
One button should close the alert and the second should perform an action but remain the alert on screen. Is there some configuration that can be done to action that it won't close the alert?
UIAlertController *alert = [UIAlertController alertControllerWithTitle:#"title"
message:#"message"
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:#"Do Something"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
//Pressing this button, should not remove alert from screen
}]];
[alert addAction:[UIAlertAction actionWithTitle:#"Close"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
//Regular functionality- pressing removes alert from screen
}]];
[alert show];
This (Prevent UIAlertController to dismiss) was suggested as possible answer, but the question deals with text field. I need one of the action buttons to not close the alert when pressed.
You can't do this.
Only one solution is to create a custom view controller that will look like native UIAlertController.
You can't do it with default UIAlertViewController, if you want to do this you need to create custom view controller whose look like UIAlertController.
You can use this custom view controller.
https://github.com/nealyoung/NYAlertViewController
Speaking from a user-perspective, a button press that doesn't follow through with an action would make me wonder if something was broken. If you're trying to use this as an opportunity to get further information, or some detail about the button press intention, I think a solution that adheres to user-expectation (though maybe a little annoying?) is to simply present a second dialog box after closing out the first. Basically, a "one-dialog-box-per-question" way of handling it.
Otherwise, I'll agree with the other suggestions and say that you need a custom view here, not a stock UIKit solution.
Try this code.
UIAlertController *alert = [UIAlertController alertControllerWithTitle:#"title"
message:#"message"
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:#"Do Something"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
NSLog(#"Do Something Tapped");
}]];
[alert addAction:[UIAlertAction actionWithTitle:#"Close"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
NSLog(#"Close Tapped");
}]];
[self presentViewController:alert animated:YES completion:nil];
How about this code:
UIAlertController *alert = [UIAlertController alertControllerWithTitle:#"title"
message:#"message"
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *doSomethingAction = [UIAlertAction actionWithTitle:#"Do Something"
style:UIAlertActionStyleDefault
handler:nil];
doSomethingAction.enabled = NO;
[alert addAction:doSomethingAction];
[alert addAction:[UIAlertAction actionWithTitle:#"Close"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
//Regular functionality- pressing removes alert from screen
}]];
[self presentViewController:alert animated:true completion:nil];
set NO to enabled property of UIAlertAction. It works well.

Show UIAlertController from a common utility class

I am working on a project that uses UIAlertView, now the problem is I have to replace all of the UIAlertView's with UIAlertController's and there are around 1250 of them in the code. I am planning to uses the existing Utility class to create a function that does this, following is what I am planning to do:
+(void)showAlert:(NSString *)title errorMessage:(NSString *)msg {
UIAlertController *alertController = [UIAlertController
alertControllerWithTitle:title message:msg preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:[UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault handler:nil]];
[self presentViewController:alertController animated:YES completion:nil];
}
I have the following questions before doing this:
1 - Is this worth the efforts (Using UIAlertController instead of UIAlertView) ?
2 - How do I handle UIAlertView's that had tags and different delegate implementation across hundreds of files ?
3 - The fourth line the function above gives an error : No known class method for selector 'presentViewController:animated:completion:'
Any help is greatly appreciated.
You've to use UIAlertController as UIAlertView is deprecated.
Apple Doc says:
In apps that run in versions of iOS prior to iOS 8, use the
UIAlertView class to display an alert message to the user. An alert
view functions similar to but differs in appearance from an action
sheet (an instance of UIActionSheet). UIAlertView is deprecated in iOS
8. (Note that UIAlertViewDelegate is also deprecated.) To create and manage alerts in iOS 8 and later, instead use UIAlertController with a
preferredStyle of UIAlertControllerStyleAlert. Availability
Alternatively, you can do this way. In your Utils Class, do this:
+ (void)showAlertWithTitle:(NSString *)title message:(NSString *)msg controller:(id)controller {
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:msg preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault handler:nil];
[alertController addAction:okAction];
[controller presentViewController:alertController animated:YES completion:nil];
}
Usage from your ViewController class:
[Utils showAlertWithTitle:#"Camera" message:#"It seems that your device doesn't support camera. " controller: self];
If your existing UIAlertView is having tags then you've to use the UIAlertViewController class instead of the Util method.
Hope it helps.
You can use a common alertcontroller class method. Here is an example how you can solve the no.4 error. Basically you pass also the viewController that wants to call the alertview.
+(UIImage *)showAlert:(NSString *)title errorMessage:(NSString *)msg inViewController:(id)vc {
UIAlertController *alertController = [UIAlertController
alertControllerWithTitle:title message:msg preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:[UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault handler:nil]];
[vc presentViewController:alertController animated:YES completion:nil];
}
On the issue of alertviews with tags that require user input, to handle those inputs you might want to put the handler in and not let it nil and turn your method into a completionBlock method:
+(UIImage *)showAlert:(NSString *)title message:(NSString *)msg inViewController:(id)vc completedWithBtnStr:(void(^)(NSString* btnString))completedWithBtnStr {
UIAlertController *alertController = [UIAlertController
alertControllerWithTitle:title message:msg preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:[UIAlertAction actionWithTitle:#"Yes" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
completedWithBtnStr(#"Yes");
}]];
[alertController addAction:[UIAlertAction actionWithTitle:#"No" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
completedWithBtnStr(#"No");
}]];
[vc presentViewController:alertController animated:YES completion:nil];
}
An example of calling this is
[YourClass showAlert:#"Sure?" message:#"Delete this record?" inViewController:self completedWithBtnStr:^(NSString* btnString) {
if ([btnString isEqualToString:#"Yes"]) {
// delete record
}
}];
The parameter btnString can be anything really. The code is not tested so if you found error, do inform me.
Answering your questions:
It's definitely worth doing that. You have to use UIAlertController anyway in near future as of UIAlertview is deprecated.
Use blocks or protocol to get a callback when delegate methods in utility class are fired.
Looks like you are presenting that on NSObject. Present on view class. In this case, you can use window rootViewController to present on
+(void) showAlert
{
UIWindow* topWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
topWindow.rootViewController = [UIViewController new];
topWindow.windowLevel = UIWindowLevelAlert + 1;
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:#"title" message:#"message" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* okButton = [UIAlertAction
actionWithTitle:#"OK"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
//Handle your yes please button action here
topWindow.hidden = YES;
//your ok button action handling
}];
[alertController addAction:okButton];
[topWindow makeKeyAndVisible];
[topWindow.rootViewController presentViewController:alertController animated:YES completion:nil];
}

How can i Change the frame of uialertaction in uialertcontroller in objective c

I am adding the alertView in whole application. and i want to change the size of ok or cancel button in alertView so that i can set the small image of actions.
Anyone please help me for this.
You cannot change the frame of default Alert view for this you have to use custom alert view. Please refer the below links for custom alert views
https://github.com/dogo/SCLAlertView
https://github.com/vikmeup/SCLAlertView-Swift (Swift)
https://github.com/mtonio91/AMSmoothAlert
You can not change the size of ok or cancel button in alertView.
The only solution that I could figure is to make a custom view with UIVisualEffect and show it like UIActionSheet
Welcome if other solution is there :)
if you want to do this for adding image, then try following way:
UIAlertController * view= [UIAlertController
alertControllerWithTitle:#"Add Image"
message:#"Image Added successfully"
preferredStyle:UIAlertControllerStyleActionSheet];
UIAlertAction* add = [UIAlertAction
actionWithTitle:#"add"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
//Do some thing here
[view dismissViewControllerAnimated:YES completion:nil];
}];
[add setValue:[[UIImage imageNamed:#"add.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:#"image"];
[view addAction:add];
[self presentViewController:view animated:YES completion:nil];

UIAlertControllerStyleActionSheet in modal view

I am trying to show a UIAlertControllerStyleActionSheet anchored to a toolbar button. I have searched the web and found out I have to do this:
alertController.popoverPresentationController.barButtonItem = myButton;
however this does not work for me. The UIAlertController is shown as on iPhone on the iPad. Centered in the bottom middle of the window.
Here is my code:
// create action sheet
UIAlertController *alertController = [UIAlertController
alertControllerWithTitle:#"test title" message:nil
preferredStyle:UIAlertControllerStyleActionSheet];
// Delete Button
UIAlertAction *actionDelete = [UIAlertAction
actionWithTitle:#"button"
style:UIAlertActionStyleDestructive handler:^(UIAlertAction *action) {
// Delete
}];
// Cancel Button
UIAlertAction *actionCancel = [UIAlertAction
actionWithTitle:#"Avbryt"
style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
// Cancel code
}];
// Add Cancel action
[alertController addAction:actionCancel];
[alertController addAction:actionDelete];
// show action sheet
alertController.popoverPresentationController.barButtonItem = self.downloadAllButton;
alertController.popoverPresentationController.permittedArrowDirections = UIPopoverArrowDirectionAny;
[alertController setModalPresentationStyle:UIModalPresentationPopover];
[self presentViewController:alertController animated:YES completion:nil];
By the way I am doing this in a Document Provider extension in the Documentpicker. So the UIAlertControllerStyleActionSheet is in a modal view already. I gues this is my problem, but I can not figure out how to change this for the iPad. This drives me crazy!!! Please Help!!
Regards,
Markus

UIAlertController takes 15 seconds to display when adding text field

I am having a new weird error. The project is super simple, just a UINavigationController with a root view controller.
My problem is that when the user clicks on a toolbar item, it calls the following code:
- (IBAction)configurePressed:(id)sender
{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:#"Capture Area"
message:#"Enter the name of the area that is being captured."
preferredStyle:UIAlertControllerStyleAlert];
[alertController addTextFieldWithConfigurationHandler:nil];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:#"Cancel"
style:UIAlertActionStyleCancel
handler:nil];
UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:#"Set"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action)
{
UITextField *captureArea = alertController.textFields.firstObject;
_captureArea = captureArea.text;
}];
[alertController addAction:cancelAction];
[alertController addAction:defaultAction];
[self presentViewController:alertController animated:true completion:nil];
}
Super simple right? Well, it takes 15 seconds to display the first time, then instantly on subsequent times. What is even weirder is that if I remove the line:
[alertController addTextFieldWithConfigurationHandler:nil];
so there is no text input box, it displays instantaneous. I have also confirmed that it is being performed on the main thread.
UPDATE #1: New observation, when it is waiting to display, the main thread seems to be blocked. The AlertController is being displayed over an MKMapView.
UPDATE #2: This does not happen on the simulator.
Anyone else experience this?

Resources