文章更新:
3月9日:推荐一个开源框架GJAlertController
。
许多时候,我们需要在应用中弹出提示框,给用户提示信息,然后让用户选择执行哪种操作。有两种形式,一种是弹出提示框,一种是弹出提示菜单。
提示框:进入Apple Music,在登录时,会弹出提示输入AppleID账户和密码的提示框。
提示菜单:在Apple Music中,选择将一首歌曲加入到播放列表时,弹出如下菜单。
UIAlertView
在iOS 9.0中,UIAlertView被弃用。当然,UIAlertViewDelegate同样被弃用。取而代之的是,使用UIAlertController
,初始化时,将preferredStyle
设为UIAlertControllerStyleAlert
。
初始化UIAlertView
使用
- (instancetype)initWithTitle:(NSString *)title
message:(NSString *)message
delegate:(id)delegate
cancelButtonTitle:(NSString *)cancelButtonTitle
otherButtonTitles:(NSString *)otherButtonTitles,
, ...
初始化UIAlertView的实例。
注意:参数otherButtonTitles可以有多个NSString,有分号隔开,以nil结尾即可。
这里直接上代码,看一下出现的效果。
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"title"
message:@"message"
delegate:self
cancelButtonTitle:@"Cancel"
otherButtonTitles:@"Other1", nil];
效果如下:
如果otherButtonTitles参数有多个NSString时,效果如下:
我们也可以使用
- (NSInteger)addButtonWithTitle:(NSString *)title
来添加UIAlertView所显示的按钮。
@property(nonatomic, copy) NSString *title
@property(nonatomic, copy) NSString *message
可以设置这两个属性改变title
和message
。
不建议添加太多的按钮,否则会降低用户体验。
代理方法
在初始化方法中有参数delegate
,所以,如果要响应按钮点击事件,需要当前ViewController遵循UIAlertViewdelegate
。
如果初始化方法中的otherButtonTitles
不为nil,那么必须要实现
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
buttonIndex从0开始计算。这个方法调用之后,UIAlertView会自动消失。
- (BOOL)alertViewShouldEnableFirstOtherButton:(UIAlertView *)alertView
这个方法可以用与输入框的UIAlertView。当没有用户没有输入完成时,令这个方法返回NO,使用户无法点击第一个添加的按钮。
//alertView即将呈现给用户时调用
- (void)willPresentAlertView:(UIAlertView *)alertView
//alertView已经呈现给用户时调用
- (void)didPresentAlertView:(UIAlertView *)alertView
//用户点击某个按钮后,alertView即将消失时调用
- (void)alertView:(UIAlertView *)alertView willDismissWithButtonIndex:(NSInteger)buttonIndex
//用户点击某个按钮后,alertView已经消失时调用
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
在后两个代理方法中,buttonIndex
的值从0开始计算。在我初始化的alertView中,cancel
对应索引0
,other1
对应索引1
,other2
对应索引2
。
alertView的样式
我们可以通过alertViewStyle
属性改变alertView的样式。
UIAlertViewStyleDefault。弹出一个标准的alertView;
UIAlertViewStyleSecureTextInput。弹出的alertView带有一个密码输入框;
UIAlertViewStylePlainTextInput。弹出的alertView带有一个普通的文本输入框;
UIAlertViewStyleLoginAndPasswordInput。弹出的alertView有两个输入框,可以分别输入账号密码。
几个属性和方法
//获取特定按钮的索引
@property(nonatomic) NSInteger cancelButtonIndex
@property(nonatomic, readonly) NSInteger firstOtherButtonIndex
//返回对应索引的UITextField
- (UITextField *)textFieldAtIndex:(NSInteger)textFieldIndex
//UIAlertViewStyleDefault,没有text field。
//UIAlertViewStyleSecureTextInput,text field对应索引0。
//UIAlertViewStyleDefault,text field对应索引0。
//UIAlertViewStyleDefault,login field对应索引0,password field对应索引1。
//如果参数`textFieldIndex`超出索引范围,则会抛出`NSRangeException`异常。
//返回指定索引的按钮的值
- (NSString *)buttonTitleAtIndex:(NSInteger)buttonIndex
//弹出alertView
- (void)show
UIActionSheet
UIActionSheet和UIActionSheetDelegate在iOS 8.3中被弃用。取代它的是preferredStyle
被设为UIAlertControllerStyleActionSheet
的UIAlertController
。
在我使用App的过程中,两种情况下使用UIActionSheet。
当用户删除东西的时候,给出提示;
在上传头像时,选择用相机拍照还是相册照片。
初始化UIActionSheet
- (instancetype)initWithTitle:(NSString *)title
delegate:(id)delegate
cancelButtonTitle:(NSString *)cancelButtonTitle
destructiveButtonTitle:(NSString *)destructiveButtonTitle
otherButtonTitles:(NSString *)otherButtonTitles
, ...
UIActionSheet的初始话方法和UIAlertView的初始化方法类似。
同样地,给出一行初始化代码,看看效果。
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:nil
delegate:self
cancelButtonTitle:@"cancel"
destructiveButtonTitle:@"destructive"
otherButtonTitles:@"使用相机拍照", @"选择相册照片", nil];
destructiveButtonTitle
和otherButtonTitles
根据情况传值,二选一,否则就会像上图所显示的那样,看起来很丑!
代理方法
UIActionSheet的代理方法定义在协议UIActionSheetDelegate
中。它的代理方法和UIAlertViewDelegate中定义的方法是极其相似的。
//添加的按钮被点击调用
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
//actionSheet即将出现时调用
- (void)willPresentActionSheet:(UIActionSheet *)actionSheet
//actionSheet已经出现时调用
- (void)didPresentActionSheet:(UIActionSheet *)actionSheet
//actionSheet即将消失时调用
- (void)actionSheet:(UIActionSheet *)actionSheet willDismissWithButtonIndex:(NSInteger)buttonIndex
//actionSheet已经消失时调用
- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex
对于actionSheet中按钮的索引,是按照从0开始,从上到下依次+1。依然以我初始化的actionSheet为例,destructive
的索引是0,使用相机拍照
的索引是1,选择相册照片
的索引是2,cancel
的索引是3。
一些属性和方法
//获取特定的按钮的索引
@property(nonatomic) NSInteger cancelButtonIndex
@property(nonatomic) NSInteger destructiveButtonIndex
@property(nonatomic, readonly) NSInteger firstOtherButtonIndex
//返回指定索引的按钮的值
- (NSString *)buttonTitleAtIndex:(NSInteger)buttonIndex
//显示actionSheet
- (void)showInView:(UIView*)view
总结UIAlertView和UIActionSheet
这两个的类的属性、方法、代理都极其相似。
需要注意的是,当它们都有多个按钮时,计算按钮索引的方法是不同的。
UIAlertView的取消按钮的索引永远是0,其他的依次+1。
UIActionSheet的索引是所有的按钮从上到下,从0开始,一次+1。
- (void)alertViewCancel:(UIAlertView *)alertView
- (void)actionSheetCancel:(UIActionSheet *)actionSheet
这两个代理方法在什么情况下会调用,我没有搞清楚!
UIAlertController
到了iOS 9.0之后,UIAlertView和UIActionSheet都被弃用了,取而代之的就是UIAlertController
。实际上,UIAlertController在iOS 8.0以后就可以使用了。
UIAlertController是继承自UIController的,所以,使用presentViewController:animated:completaion:
方法来显示UIAlertController。
首先,我把上面给出的UIAlertView和UIActionSheet的初始化,使用UIAlertController来实现。
//实现和UIAlertView相同的效果
UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"title" message:@"message" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"cancel" style:UIAlertActionStyleCancel
handler:^(UIAlertAction * _Nonnull action) {
nil;
}];
UIAlertAction* firstOtherAction = [UIAlertAction actionWithTitle:@"Other1" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
NSLog(@"other1 clicked!");
}];
UIAlertAction* secondOtherAction = [UIAlertAction actionWithTitle:@"Other2" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
NSLog(@"other2 clicked!");
}];
[alert addAction:cancelAction];
[alert addAction:firstOtherAction];
[alert addAction:secondOtherAction];
[self presentViewController:alert animated:YES completion:^{
nil;
}];
//实现和UIActionSheet相同的效果
UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"title" message:@"message" preferredStyle:UIAlertControllerStyleActionSheet];
UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
nil;
}];
UIAlertAction* destructiveAction = [UIAlertAction actionWithTitle:@"destructive" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
NSLog(@"destructive clicked");
}];
UIAlertAction* firstAction = [UIAlertAction actionWithTitle:@"使用相机拍照" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
NSLog(@"使用相机拍照");
}];
UIAlertAction* secondAction = [UIAlertAction actionWithTitle:@"使用相册照片" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
NSLog(@"使用相册照片");
}];
[alert addAction:cancelAction];
[alert addAction:firstAction];
[alert addAction:destructiveAction];
[alert addAction:secondAction];
[self presentViewController:alert animated:YES completion:^{
nil;
}];
比较上面的两段代码,可以发现一些共同点:
UIAlertController使用
alertControllerWithTitle:message:preferredStyle:
实例化一个对象。都用到了
UIAlertAction
类,和addAction:
方法。都使用了
presentViewController:animated:completion:
方法显示。
其实,上述总结的共同点就是UIAlertController对象被实例化,然后添加每个按钮以及按钮所需要响应的事件的一个过程。
初始化方法
+ (instancetype)alertControllerWithTitle:(NSString *)title
message:(NSString *)message
preferredStyle:(UIAlertControllerStyle)preferredStyle
这是UIAlertController的类方法,其中的preferredStyle
参数的值,决定了它显示的样式。如果是UIAlertControllerStyleAlert
,那么会显示一个alertView
。如果是UIAlertControllerActionSheet
,那么会显示一个actionSheet
。
UIAlertAction
这个类的作用就是给UIAlertController添加按钮,以及按钮所响应的事件。
+ (instancetype)actionWithTitle:(NSString *)title
style:(UIAlertActionStyle)style
handler:(void (^)(UIAlertAction *action))handler
在这个初始化方法中,style
参数指定按钮显示的样式,而handler
这个Block里放按钮点击后需要执行的操作。
style
这个参数可以选择3个值:
UIAlertActionStyleDefault。显示常规的按钮样式。
UIAlertActionStyleCancel。显示
取消
按钮的样式,是加粗的。UIAlertActionStyleDestructive。显示红色的文字。一般情况下,表示这个按钮点击后可能会改变或者删除数据。
在实例化UIAlertAction的对象之后,需要使用addAction:
方法,将他们添加到UIAlertController上。除了取消按钮外,添加的顺序决定了按钮在UIAlertController上显示的顺序。
只能在UIAlertControllerStyleAlert下使用的一些特殊的设置
preferredAction
这个属性值默认为nil。
赋给这个属性的alertAction必须已经添加到UIAlertController中,否则运行后会crash。设置之后,对应的按钮的文字会被加粗显示。_所有的按钮的文字只有一个是加粗显示的。_
文本输入框
- (void)addTextFieldWithConfigurationHandler:(void (^)(UITextField *textField))configurationHandler
这个方法可以为alertController添加一个文本框。这个方法所带的block参数可以用来设置文本框的样式。这个方法可以多次被调用,被一次添加到UIAlertController中。
结语
一般情况下,我们的App需要适配到iOS 7.0。所以,我们依然可以使用UIAlertView
和UIActionSheet
,并且,它们可以在iOS 9.0以上的设备上运行的。话虽如此,进行适配肯定会为我们规避不可预估的不良后果。
给大家推荐一个在iOS 8上使用UIAlertController的框架 —— GJAlertController。框架的作者郭晓亮为大家讲解了内部的实现方式,大家可以参考《黑魔法——“低版本中使用高版本中出现的类”之技术实现原理详解》。