UIAlertController

UIAlertController 同時替代了 UIAlertView 和 UIActionSheet,從系統(tǒng)層級上統(tǒng)一了 alert 的概念 —— 即以 modal 方式或 popover 方式展示。

UIAlertController 是 UIViewController 的子類,而非其先前的方式。因此新的 alert 可以由 view controller 展示相關(guān)的配置中獲益很多。

UIAlertController 不管是要用 alert 還是 action sheet 方式展示,都要以 title 和 message 參數(shù)來初始化。Alert 會在當(dāng)前顯示的 view controller 中心以模態(tài)形式出現(xiàn),action sheet 則會在底部滑出。Alert 可以同時有按鈕和輸入框,action sheet 僅支持按鈕。

新的方式并沒有把所有的 alert 按鈕配置都放在初始化函數(shù)中,而是引入了一個新類 UIAlertAction 的對象,在初始化之后可以進行配置。這種形式的 API 重構(gòu)讓對按鈕數(shù)量、類型、順序方便有了更大的控制。同時也棄用了 UIAlertView 和 UIActionSheet 使用的delegate 這種方式,而是采用更簡便的完成時回調(diào)。
—— 摘自Mattt Thompson

UIAlertControllerStyle ——Alert 樣式

typedef enum UIAlertControllerStyle : NSInteger {
    UIAlertControllerStyleActionSheet = 0, // 從底部向上推出的操作列表
    UIAlertControllerStyleAlert            // 模態(tài)顯示的警告框
} UIAlertControllerStyle;

UIAlertActionStyle —— 按鈕樣式

typedef enum UIAlertActionStyle : NSInteger {
    UIAlertActionStyleDefault = 0,
    UIAlertActionStyleCancel,
    UIAlertActionStyleDestructive
} UIAlertActionStyle;
UIAlertActionStyle 按鈕樣式枚舉類型 描述
UIAlertActionStyleDefault 默認樣式的動作按鈕
UIAlertActionStyleCancel 取消按鈕</br>用于取消操作并且保持視圖內(nèi)容不變的動作按鈕
UIAlertActionStyleDestructive 警示按鈕</br>可能更改或刪除數(shù)據(jù)樣式的動作按鈕,默認按鈕字體為紅色,提示用戶這樣做可能會刪除或者改變某些數(shù)據(jù)

UIAlertController 原生代碼示例

1.1 一個按鈕的 Alert 樣式

一個按鈕的Alert樣式

創(chuàng)建方法

// 1.實例化alert
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"標題" message:@"消息" preferredStyle:UIAlertControllerStyleAlert];

// 2.實例化按鈕
UIAlertAction *action = [UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
    // 點擊按鈕,調(diào)用此block
    NSLog(@"Button Click");
}];
[alert addAction:action];

// 3.顯示alertController
[self presentViewController:alert animated:YES completion:nil];

1.2 標準的 Alert 樣式

創(chuàng)建方法

//  1.實例化UIAlertController對象
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"標準的Alert 樣式" message:@"UIAlertControllerStyleAlert" preferredStyle:UIAlertControllerStyleAlert];

//  2.1實例化UIAlertAction按鈕:取消按鈕
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
    // 點擊取消按鈕,調(diào)用此block
    NSLog(@"取消按鈕被按下!");
}];
[alert addAction:cancelAction];

//  2.2實例化UIAlertAction按鈕:確定按鈕
UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
    // 點擊按鈕,調(diào)用此block
    NSLog(@"確定按鈕被按下");
}];
[alert addAction:defaultAction];

//  3.顯示alertController
[self presentViewController:alert animated:YES completion:nil];

注:當(dāng) Alert View 樣式中有 Cancel 按鈕時,Cancel 按鈕總是顯示在左側(cè),與添加按鈕的順序無關(guān)。

1.3 帶有多個按鈕的 Alert 樣式

創(chuàng)建方法

//  1.實例化UIAlertController對象
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"多個按鈕的Alert 樣式" message:@"當(dāng)按鈕數(shù)超過兩個后,會呈現(xiàn)上下分布" preferredStyle:UIAlertControllerStyleAlert];

//  2.1實例化UIAlertAction按鈕:確定按鈕
UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
    // 點擊按鈕,調(diào)用此block
    NSLog(@"確定按鈕被按下");
}];
[alert addAction:defaultAction];

//  2.2實例化UIAlertAction按鈕:更多按鈕
UIAlertAction *moreAction = [UIAlertAction actionWithTitle:@"更多" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
    // 點擊按鈕,調(diào)用此block
    NSLog(@"更多按鈕被按下");
}];
[alert addAction:moreAction];

//  2.3實例化UIAlertAction按鈕:取消按鈕
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
    // 點擊取消按鈕,調(diào)用此block
    NSLog(@"取消按鈕被按下!");
}];
[alert addAction:cancelAction];

//  3.顯示alertController
[self presentViewController:alert animated:YES completion:nil];

注:

  • 有1個或者2個操作按鈕的時候,按鈕會水平排布。更多按鈕時,就會像 action sheet 那樣垂直展示;
  • UIAlertActionStyleCancel 樣式的按鈕時,該按鈕總是在最底部,其他按鈕順序由添加順序決定。如果包含 UIAlertActionStyleDestructive 樣式的按鈕,一般先添加,以便在第一個位置顯示。每一個警報控制器只能包含一個 Cancel 按鈕,如果你添加了兩個或多個,在運行時會拋出 NSInternalInconsistencyException 的異常。

2.帶輸入框樣式

創(chuàng)建方法

- (void)createAlertControllerWithTextField {
    // 1.實例化UIAlertController對象
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"標題" message:@"信息" preferredStyle:UIAlertControllerStyleAlert];
    
    // 2.1添加輸入文本框
    [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
        textField.placeholder = @"支付密碼";
        textField.secureTextEntry = YES;
        
        // 監(jiān)聽文本輸入字符長度,長度不足時禁用確定按鈕
        [textField addTarget:self action:@selector(alertPasswordInputDidChange:) forControlEvents:UIControlEventEditingChanged];
    }];
    
    // 2.2實例化UIAlertAction按鈕:確定按鈕
    UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        UITextField *passwordTextField = alert.textFields.firstObject;
        NSLog(@"讀取輸入密碼:%@",passwordTextField.text);
    }];
    confirmAction.enabled = NO; // 初始化時禁用確定按鈕
    [alert addAction:confirmAction];
    
    //  3.顯示alertController
    [self presentViewController:alert animated:YES completion:nil];
}

- (void)alertPasswordInputDidChange:(UITextField *)sender {
    UIAlertController *alert = (UIAlertController *)self.presentedViewController;
    if (alert) {
        NSString *password = alert.textFields.firstObject.text;
        UIAlertAction *confirmAction = alert.actions.firstObject;
        BOOL isPasswordValidate = password.length > 6;
        confirmAction.enabled = isPasswordValidate;
    }
}

3. 標準的 Alert Sheet 樣式

操作表一般用于為用戶提供一組可供選擇的操作選項,如刪除、恢復(fù)等。一般根據(jù)設(shè)備尺寸大小決定呈現(xiàn)形式,在 iPhone 上,操作表由底部滑出;在 iPad 上,操作表以彈出框(popover) 形式出現(xiàn)。

創(chuàng)建方法

// 1.實例化UIAlertController對象
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"標準的Action Sheet樣式" message:@"UIAlertControllerStyleActionSheet" preferredStyle:UIAlertControllerStyleActionSheet];

// 2.1實例化UIAlertAction按鈕:更多按鈕
UIAlertAction *moreAction = [UIAlertAction actionWithTitle:@"更多" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
    NSLog(@"更多按鈕被按下!");
}];
[alert addAction:moreAction];

// 2.2實例化UIAlertAction按鈕:確定按鈕
UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
    NSLog(@"確定按鈕被按下");
}];
[alert addAction:confirmAction];

// 2.3實例化UIAlertAction按鈕:取消按鈕
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
    NSLog(@"取消按鈕被按下!");
}];
[alert addAction:cancelAction];

//  3.顯示alertController
[self presentViewController:alert animated:YES completion:nil];

注:

如果 Action Sheet 中有取消按鈕,取消按鈕每次都會在底部顯示,其他按鈕會按照添加的順序顯示。在 Action Sheet 內(nèi)不能添加文本框。如果你添加了文本框,在運行時會拋出異常。

如上面說到的,在 iPad 中 Action Sheet 以彈出框的形式呈現(xiàn)。彈出框總是需要一個錨點,錨點可以是源視圖,也可以是按鈕。如果我們用按鈕觸發(fā)彈出框,就可以把按鈕作為錨點。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];
}

當(dāng) Action Sheet 以彈出框形式展現(xiàn)時,UIKit 會取消顯示 Cancel 按鈕。此時,點擊 popover 以外任何區(qū)域和點擊 Cancel 按鈕效果一致,同時會調(diào)用取消按鈕的完成處理程序。

4.通過通知中心退出警報控制器

警報控制器會在用戶點擊按鈕后自動消失,但在 app 進入后臺時,警告框和選擇表并不會自動退出。此時,我們需要通過代碼實現(xiàn)退出警報控制器。

- (void)dealloc {
    // 移除觀察者
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // APP 進入后臺后隱藏 Alert 彈窗
    [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidEnterBackgroundNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
        [self.presentedViewController dismissViewControllerAnimated:YES completion:nil];
    }];
}

總結(jié)

下面總結(jié)下 Alert View 和 Action Sheet 的異同。

警告框 Alert View:

  • 一般顯示在當(dāng)前視圖控制器的中心,點擊警告框以外區(qū)域不能隱藏警告控制器。
  • 可以添加任意數(shù)量文本框。
  • 有一個或兩個按鈕時,橫向排布,如果有 Cancel 按鈕,則 Cancel 按鈕顯示在左側(cè);有兩個以上按鈕時,豎列排布,如果有 Cancel 按鈕,則 Cancel 按鈕顯示在最底部。其他按鈕按照添加順序排布。

操作表 Action Sheet:

  • 在 iPhone 中自下而上滑出顯示在當(dāng)前控制器的底部,點擊 action sheet 以外區(qū)域可以隱藏 UIAlertController
  • 在 iPad 中以 popover 方式、以源視圖為錨點顯示,點擊選擇表以外的區(qū)域可以隱藏警告控制器。
  • Alert 可以同時有按鈕和輸入框,而 action sheet 僅支持按鈕,不能添加文本框。
  • 按鈕豎列排布,在 iPhone 中,Cancel 按鈕默認在底部顯示;在 iPad 中,Cancel 按鈕默認不顯示。

UIAlertController 類只能原樣使用,不支持子類化。該類的視圖層次結(jié)構(gòu)是私有的,不能修改。最后,需要注意的是,警告框和操作表向用戶顯示信息時會中斷應(yīng)用的當(dāng)前流程,請只在需要的時候使用,切勿濫用

第三方框架示例:

ryanmaxwell/UIAlertController-Blocks

UIAlertController+BlocksUIAlertViewController 進行了封裝,支持用 Blocks 方式封裝的便捷擴展類,調(diào)用更簡單。

// 通用創(chuàng)建方法
+ (nonnull instancetype)showInViewController:(nonnull UIViewController *)viewController
                                   withTitle:(nullable NSString *)title
                                     message:(nullable NSString *)message
                              preferredStyle:(UIAlertControllerStyle)preferredStyle
                           cancelButtonTitle:(nullable NSString *)cancelButtonTitle
                      destructiveButtonTitle:(nullable NSString *)destructiveButtonTitle
                           otherButtonTitles:(nullable NSArray *)otherButtonTitles
#if TARGET_OS_iOS
          popoverPresentationControllerBlock:(nullable UIAlertControllerPopoverPresentationControllerBlock)popoverPresentationControllerBlock
#endif
                                    tapBlock:(nullable UIAlertControllerCompletionBlock)tapBlock;

// 指明創(chuàng)建 UIAlertControllerStyleAlert 樣式的彈窗:
+ (nonnull instancetype)showAlertInViewController:(nonnull UIViewController *)viewController
                                        withTitle:(nullable NSString *)title
                                          message:(nullable NSString *)message
                                cancelButtonTitle:(nullable NSString *)cancelButtonTitle
                           destructiveButtonTitle:(nullable NSString *)destructiveButtonTitle
                                otherButtonTitles:(nullable NSArray *)otherButtonTitles
                                         tapBlock:(nullable UIAlertControllerCompletionBlock)tapBlock;

// 指明創(chuàng)建 UIAlertControllerStyleActionSheet 樣式的彈窗:
+ (nonnull instancetype)showActionSheetInViewController:(nonnull UIViewController *)viewController
                                              withTitle:(nullable NSString *)title
                                                message:(nullable NSString *)message
                                      cancelButtonTitle:(nullable NSString *)cancelButtonTitle
                                 destructiveButtonTitle:(nullable NSString *)destructiveButtonTitle
                                      otherButtonTitles:(nullable NSArray *)otherButtonTitles
#if TARGET_OS_iOS
                     popoverPresentationControllerBlock:(nullable UIAlertControllerPopoverPresentationControllerBlock)popoverPresentationControllerBlock
#endif
                                               tapBlock:(nullable UIAlertControllerCompletionBlock)tapBlock;

1. 一個按鈕的 Alert

[UIAlertController showAlertInViewController:self
                                           withTitle:@"無法訪問位置信息"
                                             message:@"請去設(shè)置-隱私-定位服務(wù)中開啟該功能"
                                   cancelButtonTitle:@"知道了"
                              destructiveButtonTitle:nil
                                   otherButtonTitles:nil
                                            tapBlock:nil];

2. 多個按鈕的 Alert

// 通過 Block 的方式封裝按鈕點擊的回調(diào)
UIAlertControllerCompletionBlock tapBlock = ^(UIAlertController *controller, UIAlertAction *action, NSInteger buttonIndex){
            if (buttonIndex == controller.destructiveButtonIndex) {
                NSLog(@"Delete");
            } else if (buttonIndex == controller.cancelButtonIndex) {
                NSLog(@"Cancel");
            } else if (buttonIndex >= controller.firstOtherButtonIndex) {
                NSLog(@"Other %ld", (long)buttonIndex - controller.firstOtherButtonIndex + 1);
            }
        };

// Alert 樣式
[UIAlertController showAlertInViewController:self
                                       withTitle:@"Test Alert"
                                         message:@"Test Message"
                               cancelButtonTitle:@"Cancel"
                          destructiveButtonTitle:@"Delete"
                               otherButtonTitles:@[@"First Other", @"Second Other"]
                                        tapBlock:tapBlock];

3. Action Sheet 樣式

    // 通過 Block 的方式封裝按鈕點擊的回調(diào)
    UIAlertControllerCompletionBlock tapBlock = ^(UIAlertController *controller, UIAlertAction *action, NSInteger buttonIndex){
                if (buttonIndex == controller.destructiveButtonIndex) {
                    NSLog(@"Delete");
                } else if (buttonIndex == controller.cancelButtonIndex) {
                    NSLog(@"Cancel");
                } else if (buttonIndex >= controller.firstOtherButtonIndex) {
                    NSLog(@"Other %ld", (long)buttonIndex - controller.firstOtherButtonIndex + 1);
                }
            };
    
    [UIAlertController showActionSheetInViewController:self
                                             withTitle:@"Test Action Sheet"
                                               message:@"Test Message"
                                     cancelButtonTitle:@"Cancel"
                                destructiveButtonTitle:@"Destructive"
                                     otherButtonTitles:@[@"First Other", @"Second Other"] popoverPresentationControllerBlock:^(UIPopoverPresentationController * _Nonnull popover) {
        popover.sourceView = self.view;
        // FIXME: 以下支持 iPad 的屬性設(shè)置待修復(fù),
        popover.sourceRect = self.view.frame;
    } tapBlock:tapBlock];

?????? Tips:如果不需要某個按鈕,就給按鈕的Title 傳 nil

ButtonIndex 按鈕索引的判斷:

static NSInteger const UIAlertControllerBlocksCancelButtonIndex = 0; // 取消、返回按鈕
static NSInteger const UIAlertControllerBlocksDestructiveButtonIndex = 1; // 更改、刪除按鈕
static NSInteger const UIAlertControllerBlocksFirstOtherButtonIndex = 2; // 第一個默認按鈕
button Index

kukumaluCN/JXTAlertManager

這個框架支持鏈式語法:
使用示例:

1. Alert

[self jxt_showAlertWithTitle:@"title"
                     message:@"message"
           appearanceProcess:^(JXTAlertController * _Nonnull alertMaker) {
    alertMaker.
    addActionCancelTitle(@"cancel").
    addActionDestructiveTitle(@"按鈕1");
} actionsBlock:^(NSInteger buttonIndex, UIAlertAction * _Nonnull action, JXTAlertController * _Nonnull alertSelf) {
    if (buttonIndex == 0) {
        NSLog(@"cancel");
    }
    else if (buttonIndex == 1) {
        NSLog(@"按鈕1");
    }
}];

2.AlertSheet

[self jxt_showAlertWithTitle:@"title"
                     message:@"message"
           appearanceProcess:^(JXTAlertController * _Nonnull alertMaker) {
    alertMaker.
    addActionDestructiveTitle(@"獲取輸入框1").
    addActionDestructiveTitle(@"獲取輸入框2");
    
    [alertMaker addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
        textField.placeholder = @"輸入框1-請輸入";
    }];
    [alertMaker addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
        textField.placeholder = @"輸入框2-請輸入";
    }];
} actionsBlock:^(NSInteger buttonIndex, UIAlertAction * _Nonnull action, JXTAlertController * _Nonnull alertSelf) {
    if (buttonIndex == 0) {
        UITextField *textField = alertSelf.textFields.firstObject;
        [self logMsg:textField.text];//不用擔(dān)心循環(huán)引用
    }
    else if (buttonIndex == 1) {
        UITextField *textField = alertSelf.textFields.lastObject;
        [self logMsg:textField.text];
    }
}];

播放系統(tǒng)聲音、提醒聲音和振動設(shè)備

// 導(dǎo)入框架
#import <AudioToolbox/AudioToolbox.h>

播放系統(tǒng)聲音

AudioServicesPlaySystemSound(1005);

播放提醒聲音

AudioServicesPlayAlertSound(1006);

執(zhí)行震動

AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);

其它開源框架

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • iOS 8的新特性之一就是讓接口更有適應(yīng)性、更靈活,因此許多視圖控制器的實現(xiàn)方式發(fā)生了巨大的變化。全新的UIPre...
    烏拉拉zzZ閱讀 939評論 0 2
  • iOS 8的新特性之一就是讓接口更有適應(yīng)性、更靈活,因此許多視圖控制器的實現(xiàn)方式發(fā)生了巨大的變化。全新的UIPre...
    Tank丶Farmer閱讀 2,116評論 2 4
  • 蘋果在iOS8.0后推出了UIAlertController以代替UIAlertView,導(dǎo)致的后果就是UIAle...
    LZM輪回閱讀 2,379評論 6 0
  • runtime 介紹 Objective-C 語言將決定盡可能的從編譯和鏈接時推遲到運行時。只要有可能,Objec...
    SuAdrenine閱讀 211評論 0 1
  • 有些事,不是你想的那樣, 有些人,不是你看到的那樣, 有些美麗,不是你看到的美麗, 有些齷齪,也不是你看到的齷齪。...
    五六月天閱讀 303評論 0 3