-
Notifications
You must be signed in to change notification settings - Fork 203
UIAlertController的使用
UIAlertController
继承自UIViewController
,用以向用户显示提醒信息。自iOS 8开始,Apple用UIAlertController
替换了UIAlertView
和UIAlertSheet
。
警报控制器(UIAlertController
)有警告框(Alert View
)和操作表(Action Sheet
)两种形式。Alert View
和Action Sheet
虽然呈现样式不同,但创建步骤一样。步骤如下:
- 创建
UIAlertContorller
- 向警报控制器添加按钮
- 显示
UIAlertContorller
下面通过创建一个demo来学习一下。
打开Xcode,创建新的工程。选择iOS一栏下的Application中的Single View Application模板,点击Next。在Product Name中填写AlertController,点击Next,选择文件存放位置,点击Create创建工程。
打开刚创建工程的storyboard,在storyboard中自上而下依次添加以下控件,内容为UIAlertControllerStyleAlert
的UILabel
,标题为Show Alert
的UIButton
,标题为Login Alert
的UIButton
,内容为UIAlertControllerStyleActionSheet
的UILabel
,标题为Action Sheet
的UIButton
。如下图:
把UIButton
拖拽到ViewController.m
的接口部分,类型为IBAction
。完成后代码如下:
#import "ViewController.h"
@interface ViewController ()
- (IBAction)showAlertView:(UIButton *)sender;
- (IBAction)showLoginAlertView:(UIButton *)sender;
- (IBAction)showActionSheet:(UIButton *)sender;
@end
创建UIAlertContorller
非常简单,不需要设置代理、不需要指定按钮。下面先在showAlertView:
方法中,创建UIAlertContorller
。
- (IBAction)showAlertView:(UIButton *)sender
{
// 1.创建UIAlertController
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Alert Title"
message:@"The message is ..."
preferredStyle:UIAlertControllerStyleAlert];
}
这里的preferredStyle:
参数有UIAlertControllerStyleAlert
和UIAlertControllerStyleActionSheet
两种,这里我们要创建的是Alert View
所以使用第一种。
使用actionWithTitle: style: handler:
方法创建UIAlertAction
对象,之后把对象添加到警报控制器。
UIAlertAction
对象由标题、样式和用户单击该按钮时运行的代码块三部分组成。UIAlertActionStyle
有三种样式,样式一UIAlertActionStyleCancel
,用于取消操作、不作任何修改,就是常见的取消按钮;样式二UIAlertActionStyleDefault
,按钮的默认样式;第三种是UIAlertActionStyleDestructive
,用于对数据进行更改或删除的操作,这种样式的按钮标题会自动使用红色显示。
在showAlertView:
方法中添加Cancel按钮和OK按钮。
- (IBAction)showAlertView:(UIButton *)sender
{
...
// 2.创建并添加按钮
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
NSLog(@"OK Action");
}];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
NSLog(@"Cancel Action");
}];
[alertController addAction:okAction]; // A
[alertController addAction:cancelAction]; // B
}
显示UIAlertContorller
。
- (IBAction)showAlertView:(UIButton *)sender
{
...
// 3.呈现UIAlertContorller
[self presentViewController:alertController animated:YES completion:nil];
}
在视图控制器中显示如下:
改变上面addAction:
方法顺序,运行app,你会发现Alert View
中按钮顺序不变。当Alert View
样式中有Cancel按钮时,Cancel按钮总是显示在左侧,与添加按钮的顺序无关。
在showAlertView:
方法中再添加一个UIAlertActionStyleDestructive
样式的Reset按钮。
- (IBAction)showAlertView:(UIButton *)sender
{
...
UIAlertAction *resetAction = [UIAlertAction actionWithTitle:@"Reset" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
NSLog(@"Reset Action");
}];
[alertController addAction:resetAction]; // C
[alertController addAction:okAction]; // A
[alertController addAction:cancelAction]; // B
...
}
在视图控制器中显示如下:
当Alert View
中存在一个或两个按钮时,按钮会水平排布;按钮大于两个时会像Action Sheet
那样竖列展示。把上面addAction:
顺序改变为B、A、C,运行app,视图控制器显示如下:
可以看到上面只要有UIAlertActionStyleCancel
样式的按钮,该按钮总是在最底部,其他按钮顺序由添加顺序决定。如果包含UIAlertActionStyleDestructive
样式的按钮,一般先添加,以便在第一个位置显示。每一个警报控制器只能包含一个Cancel按钮,如果你添加了两个或多个,在运行时会抛出NSInternalInconsistencyException
的异常。
UIAlerController
的另一个用途是我们可以向警报控制器中添加任意数量的UITextField
作为警报控制器内容视图中的一部分。比如常见的登陆框。
为了创建一个上图中的登录框,我们需要为警报控制器添加两个文本框。每一个文本框添加合适的占位符以提示需要输入文本信息,并为需要输入密码的文本框启用安全文本,以确保密码安全。更新后的showLoginAlertView:
代码如下:
- (IBAction)showLoginAlertView:(UIButton *)sender
{
// 1.创建UIAlertController
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Login"
message:@"Enter Your Account Info Below"
preferredStyle:UIAlertControllerStyleAlert];
// 2.1 添加文本框
[alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.placeholder = @"username";
}];
[alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.placeholder = @"password";
textField.secureTextEntry = YES;
}];
}
继续在showLoginAlertView:
方法中添加Cancel按钮和Login按钮,在点击Login按钮时获取文本框中的账号和密码并输出到控制台。
- (IBAction)showLoginAlertView:(UIButton *)sender
{
...
// 2.2 创建Cancel Login按钮
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
NSLog(@"Cancel Action");
}];
UIAlertAction *loginAction = [UIAlertAction actionWithTitle:@"Login" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
UITextField *userName = alertController.textFields.firstObject;
UITextField *password = alertController.textFields.lastObject;
// 输出用户名 密码到控制台
NSLog(@"username is %@, password is %@",userName.text,password.text);
}];
}
上面的代码中,警报控制器中的UITextField
的顺序由添加顺序决定。
最后添加按钮,显示警报控制器。
- (IBAction)showLoginAlertView:(UIButton *)sender
{
...
// 2.3 添加按钮
[alertController addAction:cancelAction];
[alertController addAction:loginAction];
// 3.显示警报控制器
[self presentViewController:alertController animated:YES completion:nil];
}
现在运行app,在第一个文本框中输入pro648
,在第二个文本框内输入x
,点击Login按钮,控制台输出如下:
username is pro648, password is x
在实际应用中我们一般会对用户名和密码长度进行限制,当长度不足时应该禁用Login按钮。我们可以通过为文本框添加一个UIControllEventEditingChanged
响应事件来实现。记得在添加按钮前先禁用按钮。更新后代码如下:
- (IBAction)showLoginAlertView:(UIButton *)sender
{
...
// 2.1 添加文本框
[alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.placeholder = @"username";
[textField addTarget:self action:@selector(alertUserAccountInfDidChange:) forControlEvents:UIControlEventEditingChanged]; // 添加响应事件
}];
[alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.placeholder = @"password";
textField.secureTextEntry = YES;
[textField addTarget:self action:@selector(alertUserAccountInfDidChange:) forControlEvents:UIControlEventEditingChanged]; // 添加响应事件
}];
...
// 2.3 添加按钮
loginAction.enabled = NO; // 禁用Login按钮
[alertController addAction:cancelAction];
[alertController addAction:loginAction];
...
}
- (void)alertUserAccountInfDidChange:(UITextField *)sender
{
UIAlertController *alertController = (UIAlertController *)self.presentedViewController;
if (alertController)
{
UITextField *userName = alertController.textFields.firstObject;
UIAlertAction *loginAction = alertController.actions.lastObject;
UITextField *password = alertController.textFields.lastObject;
if (userName.text.length>3 && password.text.length > 6) // 用户名必须大于三位 密码必须大于六位
{
loginAction.enabled = YES;
}
}
}
UIAlertController
中的textFields
和actions
均是数组类型,第一个添加的对象index为0
。之后按照添加的顺序index依次加1,虽然前面说到Cancel按钮一般显示在左侧(横排)或底部(竖排),但并不代表它在数组中的位置是第一个或最后一个,其index是由添加的顺序决定。你可以根据username
字符串长度来禁用Login按钮进行测试。
现在只有在用户名大于三位、密码大于六位时,Login按钮才可以点击。
操作表一般用于为用户提供一组可供选择的操作选项,如删除,恢复等。一般根据设备尺寸大小决定呈现形式,在iPhone上,操作表由底部滑出,在iPad上,操作表以弹出框形式出现。
创建操作表的方法与警告框类似,唯一不同在于preferredStyle:
参数的选择。在showActionSheet:
方法中创建操作表。
- (IBAction)showActionSheet:(UIButton *)sender
{
// 1.创建UIAlertController
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Action Sheet"
message:@"Deleted data can't be restored"
preferredStyle:UIAlertControllerStyleActionSheet];
}
下面创建并添加按钮,最后呈现警报控制器。
- (IBAction)showActionSheet:(UIButton *)sender
{
...
// 2.1 创建按钮
UIAlertAction *deleteAction = [UIAlertAction actionWithTitle:@"Delete" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
NSLog(@"Delete Action");
}];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
NSLog(@"Cancel Action");
}];
// 2.2 添加按钮
[alertController addAction:deleteAction];
[alertController addAction:cancelAction];
// 3.显示警报控制器
[self presentViewController:alertController animated:YES completion:nil];
}
运行app,操作表展示如下:
如果Action Sheet
中有取消按钮,取消按钮每次都会在底部显示,其他按钮会按照添加的顺序显示。在Action Sheet
内不能添加文本框。如果你添加了文本框,在运行时会抛出下面的异常提醒:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Text fields can only be added to an alert controller of style UIAlertControllerStyleAlert'
如上面说到的在iPad中,Action Sheet
以弹出框的形式呈现。弹出框总是需要一个锚点,锚点可以是源视图,也可以是按钮。在这个demo中,我们用按钮触发弹出框,所以这里将把按钮作为锚点。showActionSheet:
方法更新后如下:
- (IBAction)showActionSheet:(UIButton *)sender
{
...
UIPopoverPresentationController *popover = alertController.popoverPresentationController;
if (popover)
{
popover.sourceView = sender;
popover.sourceRect = sender.bounds;
popover.permittedArrowDirections = UIPopoverArrowDirectionAny;
}
// 3.显示警报控制器
[self presentViewController:alertController animated:YES completion:nil];
}
如果在iPad中没有添加上面方法,运行时会出现下面崩溃提示:
*** Terminating app due to uncaught exception 'NSGenericException', reason: 'Your application has presented a UIAlertController (<UIAlertController: 0x7f88c85221f0>) of style UIAlertControllerStyleActionSheet. The modalPresentationStyle of a UIAlertController with this style is UIModalPresentationPopover. You must provide location information for this popover through the alert controller's popoverPresentationController. You must provide either a sourceView and sourceRect or a barButtonItem. If this information is not known when you present the alert controller, you may provide it in the UIPopoverPresentationControllerDelegate method -prepareForPopoverPresentation.'
现在,Action Sheet
以触发它的按钮为锚点,以弹出框形式展现。
当
Action Sheet
以弹出框形式展现时,UIAlertController
会自动取消Cancel按钮,用户可以通过点击弹出框以外的区域来取消弹出框。
警报控制器会在用户点击按钮后自动消失,如果需要,也可以在代码中实现手动退出。一般在app进入后台时警告框和选择表并没有退出,我们可以通过在viewDidLoad
方法中添加通知中心,当观察者接收到app进入后台时,退出警报控制器。
- (void)viewDidLoad
{
[super viewDidLoad];
// app 进入后台后隐藏警报控制器
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidEnterBackgroundNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
[self.presentedViewController dismissViewControllerAnimated:YES completion:nil];
}];
}
- (void)dealloc
{
// 移除观察者
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
}
最后一定记得移除观察者,否则会引起崩溃。
下面总结下Alert View
和Action Sheet
的异同。
警告框Alert View
- 一般显示在当前视图控制器的中心,点击警告框以外区域不能隐藏警告控制器。
- 可以添加任意数量文本框。
- 有一个或两个按钮时,横向排布,如果有Cancel按钮,则Cancel按钮显示在左侧;有两个以上按钮时,竖列排布,如果有Cancel按钮,则Cancel按钮显示在最底部。其他按钮按照添加顺序排布。
操作表Action Sheet
- 在iPhone中一般以滑出的形式显示在当前控制器的底部,点击警告框以外区域可以隐藏警告控制器。
- 在iPad中以popover方式、以源视图为锚点显示,点击选择表以外的区域可以隐藏警告控制器。
- 不能添加文本框。
- 按钮竖列排布,在iPhone中,Cancel按钮默认在底部显示;在iPad中,Cancel按钮默认不显示。
Alert View
和Action Sheet
都只能包含一个UIAlertActionStyleCancel
样式的按钮,否则运行时会崩溃。UIAlertController
类只能原样使用,不支持子类化。此类的视图层次结构是私有的,不能修改。
Demo名称:AlertController
源码地址:https://github.com/pro648/BasicDemos-iOS
参考资料: