UIAlertController
你知道 UIAlert
、UIAction
(以及它们各自的 delegate protocols) 在 iOS 8 中已经被废弃了吗?
这是真的。在你的代码中按住 ⌘ 点击 UIAlert
或者 UIAction
,你就会看到最上面的注释:
UIAlert
is deprecated. UseView UIAlert
with aController preferred
ofStyle UIAlert
instead.Controller Style Alert
你可能好奇为什么 Xcode 不直接警告你这个变化呢?别急,往下读:
@availability(i OS, introduced=2.0)
虽然类已经被废弃了,但在 @availability
属性中并没有表达出这一点。UIAlert
目前还是能用的。
最开始的时候,UIAlert
充满了无底线的让步,牺牲格式和设计正确性来顺应开发者的喜好。它的 delegate
类型是在初始化函数中注释的 (delegate:(id /* <UIAlert
),并且在 protocol 方法中实现了让人匪夷所思的概念——button
的 “clicked” 而不是 “tapped”;然后还提供了不限数量的参数来引入 other
,因此 button 的索引管理变得非常痛苦;一个 -show
方法也根本不考虑 view 的层级关系……类似的问题数不胜数。
UIAction
也一样糟糕,但是开发者大多数时间里都没时间想起那些糟糕的使用方法,与其相关的抱怨特就更少了。
因此,介绍 UIAlert
给大家,就好比派出军队解放一座被占领的城市一样。它不仅仅改进了那些让人头疼的 API,也开辟了一条为最新设备上 UIKit 特性适配的新路径。
本周文章的主题就是 UIAlert
,向大家展示如何替换旧的 alert,以及这些操作方法的高级扩展。
UIAlert
同时替代了 UIAlert
和 UIAction
,从系统层级上统一了 alert 的概念 —— 即以 modal 方式或 popover 方式展示。
UIAlert
是 UIView
的子类,而非其先前的方式。因此新的 alert 可以由 view controller 展示相关的配置中获益很多。
UIAlert
不管是要用 alert 还是 action sheet 方式展示,都要以 title
和 message
参数来初始化。Alert 会在当前显示的 view controller 中心以模态形式出现,action sheet 则会在底部滑出。Alert 可以同时有按钮和输入框,action sheet 仅支持按钮。
新的方式并没有把所有的 alert 按钮配置都放在初始化函数中,而是引入了一个新类 UIAlert
的对象,在初始化之后可以进行配置。这种形式的 API 重构让对按钮数量、类型、顺序方便有了更大的控制。同时也弃用了 UIAlert
和 UIAction
使用的delegate 这种方式,而是采用更简便的完成时回调。
新旧 Alert 方式对比
标准的 Alert 样式
旧方法:UIAlertView
let alert View = UIAlert View(title: "Default Style", message: "A standard alert.", delegate: self, cancel Button Title: "Cancel", other Button Titles: "OK")
alert View.alert View Style = .Default
alert View.show()
// MARK: UIAlert View Delegate
func alert View(alert View: UIAlert View, clicked Button At Index button Index: Int) {
switch button Index {
// ...
}
}
新方法:UIAlertController
let alert Controller = UIAlert Controller(title: "Default Style", message: "A standard alert.", preferred Style: .Alert)
let cancel Action = UIAlert Action(title: "Cancel", style: .Cancel) { (action) in
// ...
}
alert Controller.add Action(cancel Action)
let OKAction = UIAlert Action(title: "OK", style: .Default) { (action) in
// ...
}
alert Controller.add Action(OKAction)
self.present View Controller(alert Controller, animated: true) {
// ...
}
标准的 Action Sheet 样式
UIActionSheet
let action Sheet = UIAction Sheet(title: "Takes the appearance of the bottom bar if specified; otherwise, same as UIAction Sheet Style Default.", delegate: self, cancel Button Title: "Cancel", destructive Button Title: "Destroy", other Button Titles: "OK")
action Sheet.action Sheet Style = .Default
action Sheet.show In View(self.view)
// MARK: UIAction Sheet Delegate
func action Sheet(action Sheet: UIAction Sheet, clicked Button At Index button Index: Int) {
switch button Index {
...
}
}
UIAlertController
let alert Controller = UIAlert Controller(title: nil, message: "Takes the appearance of the bottom bar if specified; otherwise, same as UIAction Sheet Style Default.", preferred Style: .Action Sheet)
let cancel Action = UIAlert Action(title: "Cancel", style: .Cancel) { (action) in
// ...
}
alert Controller.add Action(cancel Action)
let OKAction = UIAlert Action(title: "OK", style: .Default) { (action) in
// ...
}
alert Controller.add Action(OKAction)
let destroy Action = UIAlert Action(title: "Destroy", style: .Destructive) { (action) in
println(action)
}
alert Controller.add Action(destroy Action)
self.present View Controller(alert Controller, animated: true) {
// ...
}
新功能
UIAlert
并不只是对已有的 API 做了清理,而是进行了标准化归纳。以前,预设的样式闲置有很多(swizzling 虽然可以提供更多的功能但还是有很大风险)。UIAlert
让以前看起来很神奇的事情变为了可能。
带有警示按钮的 Alert
这种行为已经被 UIAlert
所涵盖,共有三种类型:
.Default
: 对按钮应用标准样式。.Cancel
: 对按钮应用取消样式,代表取消操作不做任何改变。.Destructive
: 对按钮应用警示性的样式,提示用户这样做可能会改变或删除某些数据。
所以想要对模态的 alert 加一个警示性的按钮,只需要加上 .Destructive
风格的 UIAlert
属性:
let alert Controller = UIAlert Controller(title: "Title", message: "Message", preferred Style: .Alert)
let cancel Action = UIAlert Action(title: "Cancel", style: .Cancel) { (action) in
println(action)
}
alert Controller.add Action(cancel Action)
let destroy Action = UIAlert Action(title: "Destroy", style: .Destructive) { (action) in
println(action)
}
alert Controller.add Action(destroy Action)
self.present View Controller(alert Controller, animated: true) {
// ...
}
大于 2 个按钮的 Alert
有 1 个或者 2 个操作的时候,按钮会水平排布。更多按钮的情况,就会像 action sheet 那样展示:
let one Action = UIAlert Action(title: "One", style: .Default) { (_) in }
let two Action = UIAlert Action(title: "Two", style: .Default) { (_) in }
let three Action = UIAlert Action(title: "Three", style: .Default) { (_) in }
let cancel Action = UIAlert Action(title: "Cancel", style: .Cancel) { (_) in }
alert Controller.add Action(one Action)
alert Controller.add Action(two Action)
alert Controller.add Action(three Action)
alert Controller.add Action(cancel Action)
创建登录表单
iOS 5 就为 UIAlert
加入了 alert
属性,将原本私有的 API 暴露出来给开发者使用 —— 像某些系统内建应用一样允许在 alert 中显示登录和密码框。
在 iOS 8 中,UIAlert
则加入了 add
方法:
let login Action = UIAlert Action(title: "Login", style: .Default) { (_) in
let login Text Field = alert Controller.text Fields![0] as UIText Field
let password Text Field = alert Controller.text Fields![1] as UIText Field
login(login Text Field.text, password Text Field.text)
}
login Action.enabled = false
let forgot Password Action = UIAlert Action(title: "Forgot Password", style: .Destructive) { (_) in }
let cancel Action = UIAlert Action(title: "Cancel", style: .Cancel) { (_) in }
alert Controller.add Text Field With Configuration Handler { (text Field) in
text Field.placeholder = "Login"
NSNotification Center.default Center().add Observer For Name(UIText Field Text Did Change Notification, object: text Field, queue: NSOperation Queue.main Queue()) { (notification) in
login Action.enabled = text Field.text != ""
}
}
alert Controller.add Text Field With Configuration Handler { (text Field) in
text Field.placeholder = "Password"
text Field.secure Text Entry = true
}
alert Controller.add Action(login Action)
alert Controller.add Action(forgot Password Action)
alert Controller.add Action(cancel Action)
创建注册表单
UIAlert
想得比以前更周到一些,它提供了展示无限个输入框的权限,并且每一个输入框都可以根据需求进行自定义。这让仅在一个模态的 alert 中实现完善的注册功能成为可能:
alert Controller.add Text Field With Configuration Handler { (text Field) in
text Field.placeholder = "Email"
text Field.keyboard Type = .Email Address
}
alert Controller.add Text Field With Configuration Handler { (text Field) in
text Field.placeholder = "Password"
text Field.secure Text Entry = true
}
alert Controller.add Text Field With Configuration Handler { (text Field) in
text Field.placeholder = "Password Confirmation"
text Field.secure Text Entry = true
}
虽然有了这些,但必须要说明的是,不用_矫枉过正_。不能因为你可以这样做,就代表你_应该_这样做。忘了这玩意吧,提供一个 view controller 来做注册功能吧,因为你本就应该这样做!
注意
如果试图向 alert controller 添加带有 .Action
属性的输入框,将会抛出异常:
Terminating app due to uncaught exception
NSInternal
, reason: ‘Text fields can only be added to an alert controller of styleInconsistency Exception UIAlert
’Controller Style Alert
同样的,向 alert 或 action sheet 添加一个以上的 .Cancel
按钮将会抛出异常:
Terminating app due to uncaught exception
NSInternal
, reason: ‘Inconsistency Exception UIAlert
can only have one action with a style ofController UIAlert
’Action Style Cancel