UIAlertView
、UIActionSheet
(以及它們各自的 delegate protocols) 在 iOS 8 中已經被廢棄了嗎?這是真的。在你的代碼中按住 ⌘ 點擊 UIAlertView
或者 UIActionSheet
,你就會看到最上面的註釋:app
UIAlertView
is deprecated. UseUIAlertController
with apreferredStyle
ofUIAlertControllerStyleAlert
instead.ide
你可能好奇爲何 Xcode 不直接警告你這個變化呢?別急,往下讀:函數
Swiftspa
@availability(iOS, introduced=2.0)
雖然類已經被廢棄了,但在 @availability
屬性中並無表達出這一點。UIAlertView
目前仍是能用的。設計
最開始的時候,UIAlertView
充滿了無底線的讓步,犧牲格式和設計正確性來順應開發者的喜愛。它的 delegate
類型是在初始化函數中註釋的 (delegate:(id /* <UIAlertViewDelegate */)delegate
),而且在 protocol 方法中實現了讓人匪夷所思的概念——buttonAtIndex:
的 "clicked" 而不是 "tapped";而後還提供了不限數量的參數來引入 otherButtonTitles
,所以 button 的索引管理變得很是痛苦;一個 -show
方法也根本不考慮 view 的層級關係......相似的問題數不勝數。code
UIActionSheet
也同樣糟糕,可是開發者大多數時間裏都沒時間想起那些糟糕的使用方法,與其相關的抱怨特就更少了。orm
所以,介紹 UIAlertController
給你們,就比如派出軍隊解放一座被佔領的城市同樣。它不只僅改進了那些讓人頭疼的 API,也開闢了一條爲最新設備上 UIKit 特性適配的新路徑。server
本週文章的主題就是 UIAlertController
,向你們展現如何替換舊的 alert,以及這些操做方法的高級擴展。對象
UIAlertController
同時替代了 UIAlertView
和 UIActionSheet
,從系統層級上統一了 alert 的概念 —— 即以 modal 方式或 popover 方式展現。索引
UIAlertController
是 UIViewController
的子類,而非其先前的方式。所以新的 alert 能夠由 view controller 展現相關的配置中獲益不少。
UIAlertController
不論是要用 alert 仍是 action sheet 方式展現,都要以 title
和 message
參數來初始化。Alert 會在當前顯示的 view controller 中心以模態形式出現,action sheet 則會在底部滑出。Alert 能夠同時有按鈕和輸入框,action sheet 僅支持按鈕。
新的方式並無把全部的 alert 按鈕配置都放在初始化函數中,而是引入了一個新類 UIAlertAction
的對象,在初始化以後能夠進行配置。這種形式的 API 重構讓對按鈕數量、類型、順序方便有了更大的控制。同時也棄用了 UIAlertView
和 UIActionSheet
使用的delegate 這種方式,而是採用更簡便的完成時回調。
Swift
let alertView = UIAlertView(title: "Default Style", message: "A standard alert.", delegate: self, cancelButtonTitle: "Cancel", otherButtonTitles: "OK") alertView.alertViewStyle = .Default alertView.show() // MARK: UIAlertViewDelegate func alertView(alertView: UIAlertView, clickedButtonAtIndex buttonIndex: Int) { switch buttonIndex { // ... } }
Swift
let alertController = UIAlertController(title: "Default Style", message: "A standard alert.", preferredStyle: .Alert) let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel) { (action) in // ... } alertController.addAction(cancelAction) let OKAction = UIAlertAction(title: "OK", style: .Default) { (action) in // ... } alertController.addAction(OKAction) self.presentViewController(alertController, animated: true) { // ... }
Swift
let actionSheet = UIActionSheet(title: "Takes the appearance of the bottom bar if specified; otherwise, same as UIActionSheetStyleDefault.", delegate: self, cancelButtonTitle: "Cancel", destructiveButtonTitle: "Destroy", otherButtonTitles: "OK") actionSheet.actionSheetStyle = .Default actionSheet.showInView(self.view) // MARK: UIActionSheetDelegate func actionSheet(actionSheet: UIActionSheet, clickedButtonAtIndex buttonIndex: Int) { switch buttonIndex { ... } }
Swift
let alertController = UIAlertController(title: nil, message: "Takes the appearance of the bottom bar if specified; otherwise, same as UIActionSheetStyleDefault.", preferredStyle: .ActionSheet) let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel) { (action) in // ... } alertController.addAction(cancelAction) let OKAction = UIAlertAction(title: "OK", style: .Default) { (action) in // ... } alertController.addAction(OKAction) let destroyAction = UIAlertAction(title: "Destroy", style: .Destructive) { (action) in println(action) } alertController.addAction(destroyAction) self.presentViewController(alertController, animated: true) { // ... }
UIAlertController
並不僅是對已有的 API 作了清理,而是進行了標準化概括。之前,預設的樣式閒置有不少(swizzling 雖然能夠提供更多的功能但仍是有很大風險)。UIAlertController
讓之前看起來很神奇的事情變爲了可能。
UIAlertController
is not just a cleanup of pre-existing APIs, it's a generalization of them. Previously, one was constrained to whatever presets were provided (swizzling in additional functionality at their own risk). With UIAlertController
, it's possible to do a lot more out-of-the-box:
這種行爲已經被 UIAlertActionStyle
所涵蓋,共有三種類型:
.Default
: 對按鈕應用標準樣式。.Cancel
: 對按鈕應用取消樣式,表明取消操做不作任何改變。.Destructive
: 對按鈕應用警示性的樣式,提示用戶這樣作可能會改變或刪除某些數據。
因此想要對模態的 alert 加一個警示性的按鈕,只須要加上 .Destructive
風格的 UIAlertAction
屬性:
Swift
let alertController = UIAlertController(title: "Title", message: "Message", preferredStyle: .Alert) let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel) { (action) in println(action) } alertController.addAction(cancelAction) let destroyAction = UIAlertAction(title: "Destroy", style: .Destructive) { (action) in println(action) } alertController.addAction(destroyAction) self.presentViewController(alertController, animated: true) { // ... }
有 1 個或者 2 個操做的時候,按鈕會水平排布。更多按鈕的狀況,就會像 action sheet 那樣展現:
Swift
let oneAction = UIAlertAction(title: "One", style: .Default) { (_) in } let twoAction = UIAlertAction(title: "Two", style: .Default) { (_) in } let threeAction = UIAlertAction(title: "Three", style: .Default) { (_) in } let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel) { (_) in } alertController.addAction(oneAction) alertController.addAction(twoAction) alertController.addAction(threeAction) alertController.addAction(cancelAction)
iOS 5 就爲 UIAlertView
加入了 alertViewStyle
屬性,將本來私有的 API 暴露出來給開發者使用 —— 像某些系統內建應用同樣容許在 alert 中顯示登陸和密碼框。
在 iOS 8 中,UIAlertController
則加入了 addTextFieldWithConfigurationHandler
方法:
Swift
let loginAction = UIAlertAction(title: "Login", style: .Default) { (_) in let loginTextField = alertController.textFields![0] as UITextField let passwordTextField = alertController.textFields![1] as UITextField login(loginTextField.text, passwordTextField.text) } loginAction.enabled = false let forgotPasswordAction = UIAlertAction(title: "Forgot Password", style: .Destructive) { (_) in } let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel) { (_) in } alertController.addTextFieldWithConfigurationHandler { (textField) in textField.placeholder = "Login" NSNotificationCenter.defaultCenter().addObserverForName(UITextFieldTextDidChangeNotification, object: textField, queue: NSOperationQueue.mainQueue()) { (notification) in loginAction.enabled = textField.text != "" } } alertController.addTextFieldWithConfigurationHandler { (textField) in textField.placeholder = "Password" textField.secureTextEntry = true } alertController.addAction(loginAction) alertController.addAction(forgotPasswordAction) alertController.addAction(cancelAction)
UIAlertController
想得比之前更周到一些,它提供了展現無限個輸入框的權限,而且每個輸入框均可以根據需求進行自定義。這讓僅在一個模態的 alert 中實現完善的註冊功能成爲可能:
Swift
alertController.addTextFieldWithConfigurationHandler { (textField) in textField.placeholder = "Email" textField.keyboardType = .EmailAddress } alertController.addTextFieldWithConfigurationHandler { (textField) in textField.placeholder = "Password" textField.secureTextEntry = true } alertController.addTextFieldWithConfigurationHandler { (textField) in textField.placeholder = "Password Confirmation" textField.secureTextEntry = true }
雖然有了這些,但必需要說明的是,不用矯枉過正。不能由於你能夠這樣作,就表明你應該這樣作。忘了這玩意吧,提供一個 view controller 來作註冊功能吧,由於你本就應該這樣作!
若是試圖向 alert controller 添加帶有 .ActionSheet
屬性的輸入框,將會拋出異常:
Terminating app due to uncaught exception
NSInternalInconsistencyException
, reason: 'Text fields can only be added to an alert controller of styleUIAlertControllerStyleAlert
'
一樣的,向 alert 或 action sheet 添加一個以上的 .Cancel
按鈕將會拋出異常:
Terminating app due to uncaught exception
NSInternalInconsistencyException
, reason: 'UIAlertController
can only have one action with a style ofUIAlertActionStyleCancel
'