文章更新:
3月9日:推荐一个开源框架GJAlertController

许多时候,我们需要在应用中弹出提示框,给用户提示信息,然后让用户选择执行哪种操作。有两种形式,一种是弹出提示框,一种是弹出提示菜单。

  • 提示框:进入Apple Music,在登录时,会弹出提示输入AppleID账户和密码的提示框。
    image

  • 提示菜单:在Apple Music中,选择将一首歌曲加入到播放列表时,弹出如下菜单。
    image

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];

效果如下:
image

如果otherButtonTitles参数有多个NSString时,效果如下:
image

我们也可以使用

- (NSInteger)addButtonWithTitle:(NSString *)title

来添加UIAlertView所显示的按钮。

@property(nonatomic, copy) NSString *title
@property(nonatomic, copy) NSString *message

可以设置这两个属性改变titlemessage

不建议添加太多的按钮,否则会降低用户体验。

代理方法

在初始化方法中有参数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对应索引0other1对应索引1other2对应索引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被设为UIAlertControllerStyleActionSheetUIAlertController

在我使用App的过程中,两种情况下使用UIActionSheet。

  • 当用户删除东西的时候,给出提示;

  • 在上传头像时,选择用相机拍照还是相册照片。

初始化UIActionSheet

- (instancetype)initWithTitle:(NSString *)title
                     delegate:(id<UIActionSheetDelegate>)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];

image

destructiveButtonTitleotherButtonTitles根据情况传值,二选一,否则就会像上图所显示的那样,看起来很丑!

代理方法

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;
}];

image

//实现和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;
}];

image

比较上面的两段代码,可以发现一些共同点:

  • 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。所以,我们依然可以使用UIAlertViewUIActionSheet,并且,它们可以在iOS 9.0以上的设备上运行的。话虽如此,进行适配肯定会为我们规避不可预估的不良后果。

给大家推荐一个在iOS 8上使用UIAlertController的框架 —— GJAlertController。框架的作者郭晓亮为大家讲解了内部的实现方式,大家可以参考《黑魔法——“低版本中使用高版本中出现的类”之技术实现原理详解》


已注销
257 声望8 粉丝

毕业于淮海工学院网络工程系。