preface

Since UIAlertView was deprecated in iOS 9, we found UIAlertController to do the popup function instead.

UIAlertController

In iOS8, UIAlertController is functionally the same as UIAlertView and UIActionSheet, and UIAlertController replaces them in a modular way. Whether to use a dialog (Alert) or a pull-up menu (Action Sheet) depends on how you set the preferred style when creating the controller.

Default dialog

You can compare two different types of dialog creation code, the code to create the base UIAlertController is very similar to the code to create UIAlertView:

Objective – C version:

UIAlertController *alertController = [UIAlertController alertControllerWithTitle:
  @"Title" message:@"This is the default UIAlertController."
  preferredStyle:UIAlertControllerStyleAlert];
Copy the code

Swift version:

var alertController = UIAlertController(title:"Title", message:
  "This is the default UIAlertController.", preferredStyle: 
  UIAlertControllerStyle.Alert) 
Copy the code

In contrast to creating UIAlertView, we don’t need to specify a proxy, and we don’t need to specify a button during initialization. Pay special attention to the third parameter, though, and make sure you select a dialog or a pull-up menu style. You can add action buttons to the controller by creating an instance of UIAlertAction. UIAlertAction consists of a title string, a style, and a block of code that runs when the user selects the action. With UIAlertActionStyle, you can select three action styles: General (Default), Cancel (Cancel) and DeStruective (alert). To achieve the button effect that we created when we created UIAlertView, we just create these two action buttons and add them to our controller. Objective – C version:

UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel"
  style:UIAlertActionStyleCancel handler:nil];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"Good"
  style:UIAlertActionStyleDefault handler:nil];

[alertController addAction:cancelAction];
[alertController addAction:okAction];
Copy the code

Swift version:

varcancelAction = UIAlertAction(title:"Cancel", 
  style: UIAlertActionStyle.Cancel, handler: nil)
varokAction = UIAlertAction(title:"Good", 
  style: UIAlertActionStyle.Default, handler: nil)
alertController.addAction(cancelAction)
alertController.addAction(okAction)
Copy the code

Finally, we just need to display this dialog view controller:

Objective – C version:

[self presentViewController:alertController animated:YES completion:nil];
Copy the code

Swift version:

self.presentViewController(alertController, animated:true, completion: nil)
Copy the code

The order in which buttons are displayed depends on the order in which they are added to the dialog controller. In general, according to Apple’s official iOS User Interface Guidelines, you should place the cancel button on the left side of a two-button dialog. Note that the cancel button is unique, and if you add a second cancel button, you get one of the following runtime exceptions:

* Terminating app due to uncaught exception NSInternalInconsistencyException, reason: UIAlertController can only have one action with a style of UIAlertActionStyleCancelCopy the code

Alert style

What is an “alert” style? Before we rush to answer that question, let’s look at a simple example of an “alert” style. In this example, we replace the “OK” button in the previous example with the “reset” button. Objective – C version:

UIAlertAction *resetAction = [UIAlertAction actionWithTitle:@"Reset"
  style:UIAlertActionStyleDestructive handler:nil];
[alertController addAction:resetAction];
Copy the code

Swift version:

var resetAction = UIAlertAction(title:"Reset", 
  style: UIAlertActionStyle.Destructive, handler: nil)
alertController.addAction(resetAction)
Copy the code

As you can see, our new “reset” button has turned red. According to Apple’s official definition, an “alert” style button is used for actions that may alter or delete data. Therefore, a bold red logo was used to warn users.


Text dialog

The great flexibility of UIAlertController means you don’t have to stick to built-in styles. Where previously we could only choose from the default view, text box view, password box view, login and password input box view, now we can add any number of UITextField objects to the dialog box and use all of the UITextField features. When you add a text box to a dialog box controller, you need to specify a block of code to configure the text box. For example, to recreate the original login and password style dialog, we can add two text boxes to it, configure them with appropriate placeholders, and finally set the password entry box to use secure text input. Objective – C version:

UIAlertController *alertController = [UIAlertController alertControllerWithTitle:
  @Text dialog boxmessage:@"Sample Login and Password dialog box"
  preferredStyle:UIAlertControllerStyleAlert];
[alertController addTextFieldWithConfigurationHandler:
  ^(UITextField *textField){
    textField.placeholder = @"Login";
}];

[alertController addTextFieldWithConfigurationHandler:
  ^(UITextField *textField) {
    textField.placeholder = @"Password";
    textField.secureTextEntry = YES;
}];
Copy the code

Swift version:

alertController.addTextFieldWithConfigurationHandler {
  (textField: UITextField!) -> Voidin
  textField.placeholder ="Login"
}

alertController.addTextFieldWithConfigurationHandler {
  (textField: UITextField!) -> Voidin
  textField.placeholder ="Password"
  textField.secureTextEntry =true
}
Copy the code

When the “OK” button is pressed, we have the program read the value in the text box. Objective – C version:

UIAlertAction *okAction = [UIAlertAction actionWithTitle:
  @"Good"style:UIAlertActionStyleDefault handler: ^(UIAlertAction *action) { UITextField *login = alertController.textFields.firstObject; UITextField *password = alertController.textFields.lastObject; . }];Copy the code

Swift version:

var okAction = UIAlertAction(title:"Good", style: UIAlertActionStyle.Default) { (action: UIAlertAction!) -> Voidin var login = alertController.textFields? .first as UITextField var password = alertController.textFields? .last as UITextField }Copy the code

If we want to achieve in the UIAlertView delegate alertViewShouldEnableOtherButton: there may be some complex method. Suppose we need at least 3 characters in the Login text box to activate the OK button. Unfortunately, there is no corresponding delegate method in UIAlertController, so we need to add an Observer to the Login text box. The Observer pattern defines a one-to-many dependency between objects, where all dependent objects are notified and automatically updated when an object’s state changes. We can do this by adding the following code snippet to the build code block.

Objective – C version:

[alertController addTextFieldWithConfigurationHandler:
  ^(UITextField *textField){
    ...
    [[NSNotificationCenter defaultCenter] addObserver:self
      selector:@selector(alertTextFieldDidChange:) name:
      UITextFieldTextDidChangeNotification object:textField];
}];
Copy the code

Swift version:

alertController.addTextFieldWithConfigurationHandler {
  (textField: UITextField!) -> Void in. NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("alertTextFieldDidChange:"), name: 
    UITextFieldTextDidChangeNotification, object: textField)
}
Copy the code

We need to remove the Observer when the view controller is released, and we do this by adding appropriate code to the handler code block for each button action (and wherever else the view controller might be released). For example, in the okAction button action:

Objective – C version:

UIAlertAction *okAction = [UIAlertAction actionWithTitle:
  @"Good"style:UIAlertActionStyleDefault handler:
  ^(UIAlertAction *action) {
    ...
    [[NSNotificationCenter defaultCenter] removeObserver:self 
    name:UITextFieldTextDidChangeNotification object:nil];
}];
Copy the code

Swift version:

var okAction = UIAlertAction(title:"Good", style: UIAlertActionStyle.Default) {
  (action: UIAlertAction!) -> Void in. NSNotificationCenter.defaultCenter().removeObserver(self, name: UITextFieldTextDidChangeNotification, object: nil) }Copy the code

Before we display the dialog, we want to freeze the OK button

Objective – C version:

okAction.enabled = NO;
Copy the code

Swift version:

okAction.enabled =false
Copy the code

Next, in the Notification Observer, we need to check the contents of the Login text box before activating the button state.

Objective – C version:

- (void)alertTextFieldDidChange:(NSNotification *)notification{
  UIAlertController *alertController = 
    (UIAlertController *)self.presentedViewController;
  if(alertController) { UITextField *login = alertController.textFields.firstObject; UIAlertAction *okAction = alertController.actions.lastObject; okAction.enabled = login.text.length > 2; }}Copy the code

Swift version:

func alertTextFieldDidChange(notification: NSNotification){
  var alertController = self.presentedViewController as UIAlertController?
  if(alertController ! = nil) { var login = alertController! .textFields? .first as UITextField var okAction = alertController! .actions.last as UIAlertAction okAction.enabled = countElements(login.text) > 2 } }Copy the code

Ok, now the “OK” button of the dialog box is frozen, and you can click “OK” by typing more than 3 characters into the “Login” text box.

The drop-down menu

Pull-up menus come in handy when the user needs to be presented with a range of options (choose the phobe-killer). Unlike dialogs, pull-up menus are presented in different ways depending on the size of the device. On the iPhone (narrow width), the pull-up menu rises from the bottom of the screen. On the iPad (regular width), the pull-up menu appears as a pop-up box. The way you create a drop-down menu is very similar to the way you create a dialog box, the only difference being their form. Objective – C version:

UIAlertController *alertController = [UIAlertController alertControllerWithTitle:
  @"Save or delete data"message:@"Deleted data will not be recoverable"
  preferredStyle: UIAlertControllerStyleActionSheet];
Copy the code

Swift version:

varalertController = UIAlertController(title:"Save or delete data", 
  message:"Deleted data will not be recoverable", 
  preferredStyle: UIAlertControllerStyle.ActionSheet)
Copy the code

Button actions are added in the same way as dialog boxes. Objective – C version:

UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel"
  style:UIAlertActionStyleCancel handler:nil];
UIAlertAction *deleteAction = [UIAlertAction actionWithTitle:@"Delete"
  style:UIAlertActionStyleDestructive handler:nil];
UIAlertAction *archiveAction = [UIAlertAction actionWithTitle:@"Save"
  style:UIAlertActionStyleDefault handler:nil];

[alertController addAction:cancelAction];
[alertController addAction:deleteAction];
[alertController addAction:archiveAction];
Copy the code

Swift version:

varcancelAction = UIAlertAction(title:"Cancel", 
  style: UIAlertActionStyle.Cancel, handler: nil)
vardeleteAction = UIAlertAction(title:"Delete", 
  style: UIAlertActionStyle.Destructive, handler: nil)
vararchiveAction = UIAlertAction(title:"Save", 
  style: UIAlertActionStyle.Default, handler: nil)

alertController.addAction(cancelAction)
alertController.addAction(deleteAction)
alertController.addAction(archiveAction)
Copy the code

You can’t add a text box to the drop-down menu, and if you do, you’ll be lucky to get a runtime exception:

* Terminating app due to uncaught exception NSInternalInconsistencyException, reason: 'the Text fields can only be added to the an alert controller of style UIAlertControllerStyleAlert'Copy the code

Also, we won’t go into the simple exception specification. And then we were able to show it on the iPhone or some other device with a smaller width, and as we expected, it worked.

Objective – C version:

[self presentViewController:alertController animated:YES completion:nil];
Copy the code

Swift version:

self.presentViewController(alertController, animated:true, completion: nil)
Copy the code

If there is a “cancel” button in the drop-down menu, it will always appear at the bottom of the menu, regardless of the order in which it was added. The other buttons will be displayed from top to bottom in the order they were added. The iOS UI Guide requires that all “destroy” buttons be # 1 (red list, easy to understand, right?) . Don’t get too excited. We still have a very serious problem, and it’s hidden. When we use an iPad or other normal-width device, we get a runtime exception:

Terminating app due to uncaught exception 'NSGenericException', reason: 'UIPopoverPresentationController (< _uialertcontrolleractionsheetregularpresentationcontroller: 0 x7fc619588110 ="">) should have a non-nil sourceView or barButtonItem
setBefore the presentation occurs. 'Copy the code

As we mentioned earlier, pull-up menus are presented as pop-up boxes on normal-width devices. The pop-up box must have an Anchor point that can be used as a source view or bar button item. Since in this case we are using the regular UIButton to trigger the pull-up menu, we use it as a stroke point. In iOS 8 we no longer need to carefully calculate the size of the popup, UIAlertController will adapt the popup size to the device size. And it’s going to return nil on an iPhone or a constricted width device. The code for configuring this popup is as follows:

Objective – C version:

UIPopoverPresentationController *popover =
  alertController.popoverPresentationController;
if(popover){
  popover.sourceView = sender;
  popover.sourceRect = sender.bounds;
  popover.permittedArrowDirections = UIPopoverArrowDirectionAny;
}
Copy the code

Swift version:

var popover = alertController.popoverPresentationController
if(popover ! = nil){ popover? .sourceView = sender popover? .sourceRect = sender.bounds popover? .permittedArrowDirections = UIPopoverArrowDirection.Any }Copy the code

UIPopoverPresentationController classes is also eight new iOS, used to replace the UIPopoverController. At this point the pull-up menu is displayed in the form of a pop-up box fixed to the source button. Notice that UIAlertController automatically removes the cancel button when using the popup. The user cancels by clicking on the outside of the pop-up box, so the cancel button is no longer necessary.


Release the dialog controller

Normally, the dialog controller will release itself when the user selects an action. But you can still release it programmatically if you need to, just like any other view controller. You should remove the dialog box or pull-up menu when the application runs in the background. Assume that we are listening UIApplicationDidEnterBackgroundNotification notification messages, we can show up in the observer to release any view controller. See the example code for setting up an observer in the viewDidLoad method.

Objective – C version:

- (void)didEnterBackground:(NSNotification *)notification {
  [[NSNotificationCenter defaultCenter] removeObserver:self 
name:UITextFieldTextDidChangeNotification object:nil];
  [self.presentedViewController dismissViewControllerAnimated:NO 
completion:nil];
}
Copy the code

Swift version:

func didEnterackground(notification: NSNotification){
  NSNotificationCenter.defaultCenter().removeObserver(self, 
name: UITextFieldTextDidChangeNotification, object: nil)
  self.presentedViewController?.dismissViewControllerAnimated(false, 
completion: nil)
}
Copy the code

Note that to ensure operational safety we also need to ensure that all text box observers are removed.


Attached is some of the functionality we did with UIAlertView earlier: UIAlertView initializes and displays a dialog view with “Cancel” and “OK” buttons. Objective – C version:

UIAlertView *alertview = [[UIAlertView alloc] initWithTitle:@"Title"
  message:@"This is the default UIAlertView."delegate:self cancelButtonTitle:@"Cancel"
  otherButtonTitles:@"Good", nil];
[alertview show];
Copy the code

The Swift version differs from the Objective-C version in that the initialization of alertView only allows the creation of a dialog view with a cancel button. You might see an init method with otherButtonTitles, but unfortunately that method won’t compile.

var alertView = UIAlertView(title:"Title", message:"This is the default UIAlertView.", 
  delegate: self, cancelButtonTitle:"Cancel")
alertView.show()
Copy the code

To be able to create the same dialog view as the Objective-C version above, we could take the curve to save the country, which is a little bit more cumbersome, but we can do whatever it takes to get there, right?

varalertView = UIAlertView()
alertView.delegate = self
alertView.title ="Title"
alertView.message ="This is the default UIAlertView."
alertView.addButtonWithTitle("Cancel")
alertView.addButtonWithTitle("Good")
alertView.show()
Copy the code

You can also change the alertViewStyle property of UIAlertView to achieve the effect of entering text, password, and even login fields.

The UIAlertViewDelegate protocol has callback methods that respond to the button action of a dialog view. And when the content of the text box changes, call alertViewShouldEnableOtherButton: ways to make buttons dynamically available or unavailable. The above UIAlertView is for reference only and is not recommended to be used.