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 樣式
創(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+Blocks 對 UIAlertViewController 進行了封裝,支持用 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; // 第一個默認按鈕
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);
其它開源框架
- ActionSheetPicker-3.0 ??3.3k
- GitHub:adad184/MMPopupView ?? 2.1k
- ios-custom-alertview ??1.7k
- dogo/SCLAlertView ?? 3.4k
- GitHub:mtonio91/AMSmoothAlert ??1.3k
- GitHub:12207480/TYAlertController ??1.3k