UIAlertView是iOS開發(fā)過程中最常用的控件之一,是提醒用戶做出選擇最主要的工具.在iOS8及后來的系統(tǒng)中,蘋果更推薦使用UIAlertController來代替UIAlertView.所以本文也并不提倡開發(fā)者再使用UIAlertView,
本文的目的是探討如何將原來的給變量賦值和通過Delete來回調(diào)的方式變成鏈?zhǔn)骄幊田L(fēng)格和通過Block來回調(diào).通過學(xué)習(xí)對UIAlertView的改造讓各位iOS開發(fā)者能夠?qū)W會這種更加便捷的開發(fā)方式
2018年Swift4已經(jīng)發(fā)布,現(xiàn)在需要更新這些文章了,我也不再使用
UIAlertView。但是其鏈?zhǔn)骄幊痰暮诵乃枷霙]有變過,使用UIAlertController一也可以改出鏈?zhǔn)骄幊痰男Ч鰜怼N野堰@些代碼都放在iOSDemo項(xiàng)目里
https://github.com/DuckDeck/iOSDemo Library的 GrandCue里面。

什么是鏈?zhǔn)骄幊?/h3>
對于有一定開發(fā)經(jīng)驗(yàn)的開發(fā)者來說,鏈?zhǔn)骄幊滩⒉荒吧?有很多知名的開源庫使用了這種編程風(fēng)格,比如大名鼎鼎的JQuery,而iOS庫中,Masory和Snapkit也是典型的使用鏈?zhǔn)骄幊痰睦?
大體來說,鏈?zhǔn)骄幊叹褪菍⒍鄠€操作(多行代碼)通過某種操作符(通常是點(diǎn)號.)鏈接成一句的代碼.便代碼更加緊湊,可讀性也更好,降低了代碼的重復(fù)度.比如以下代碼:
//使用普通的賦值模式
txtUserName.placeHolder = "請輸入用戶名"
txtUserName.font = UIFont.systemFont(15)
txtUserName.textColor = UIColor.GrayColor()
//使用鏈?zhǔn)骄幊痰恼Z句
txtUserName.setPlaceHolder("請輸入用戶名").setFont(15).setTextColor(UIColor.GrayColor())
通過這個例子讀者應(yīng)該可以更清楚的了解什么是鏈?zhǔn)骄幊田L(fēng)格.簡單來說,鏈?zhǔn)骄幊田L(fēng)格要有以下特點(diǎn)
- 通常是都是調(diào)用一個函數(shù)來給屬性賦值.也就是說,該函數(shù)封裝了賦值的語句,還可以在里面加入自己的判斷和一些邏輯.
- 該函數(shù)必須有一個返回值,通常是它本身.也可以是處理后的數(shù)據(jù)或者對象.
- 可以用靜態(tài)函數(shù)作為起始函數(shù),但是后面的全是實(shí)例函數(shù).
- 可以設(shè)定一個最終函數(shù),該函數(shù)不返回任何對象,用它來完全最后所有操作.
鏈?zhǔn)骄幊痰睦?/h3>
使用鏈?zhǔn)骄幊套钪饕暮锰幨强梢允勾a更簡潔,寫起來一種"爽快"感.設(shè)計(jì)優(yōu)秀的鏈?zhǔn)骄幊炭梢源蟠蠼档椭貜?fù)的代碼,增強(qiáng)邏輯感.不足之處就是對開
發(fā)者的業(yè)務(wù)邏輯能力要求較高,同時因?yàn)殒準(zhǔn)骄幊潭际钦{(diào)用函數(shù),所以有可能會造成過深的函數(shù)調(diào)用棧.稍微影響性能.
使用Block來回調(diào)UIAlertView的Delegate
iOS開發(fā)中有很多回調(diào)都是使用Delegate完成的,相信各位讀者已經(jīng)寫過很多次了.相對來說,使用Delegate比較繁瑣,有時還需要在Delegate里
判斷是哪個對象的Delegate,優(yōu)點(diǎn)是運(yùn)行效率較高.而Block則相反,寫起來更直觀,開發(fā)效率更高.蘋果也是逐步使用Block來代替Delegate,iOS
8最新的UIViewController里的Action已經(jīng)全部使用Block來實(shí)現(xiàn).而且Swift里的閉包,匿名函數(shù)更上比比皆是.所以大膽地使用Block來代替
Delegate和Target-Action吧,蘋果會幫我們處理好各種性能問題的.但是需要注意的是retian-circle問題,初識Block很容易出現(xiàn)這種問題.
使用鏈?zhǔn)骄幊毯虰lock來改造UIAlertView
目前有兩種方式可以實(shí)現(xiàn)將UIAlertView的Delegate改成Block的回調(diào),一是使用運(yùn)行時給UIAlertView加上Block屬性.二是再寫一個新的類繼承
UIAlertView,本文使用的是第二種方式,寫起來更簡單一點(diǎn).
先定義一個新的類來繼承UIAlertView,并且實(shí)現(xiàn)UIAlertViewDelegate協(xié)議
class BlockAlert:UIAlertView,UIAlertViewDelegate{
var completion:((buttonIndex:Int,alert:UIAlertView)->Void)? //定義各個Block用來回調(diào),其參數(shù)名和Delegate回調(diào)的函數(shù)參數(shù)名一至
var willDismissBlock:((buttonIndex:Int,alert:UIAlertView)->Void)?
var didDismissBlock:((buttonIndex:Int,alert:UIAlertView)->Void)?
var didPresentBlock:((alert:UIAlertView)->Void)?
var willPresentBlock:((alert:UIAlertView)->Void)?
var alertWithCancelBlock:((alert:UIAlertView)->Void)?
override init(frame: CGRect) {
super.init(frame: frame)
self.delegate = self //將delegate設(shè)為自己
}
func alertView(alertView: UIAlertView, clickedButtonAtIndex buttonIndex: Int) {
if let block = completion{ //最主要的Block,當(dāng)用戶點(diǎn)了按鈕時回調(diào),先要判斷這個Block存在不?
block(buttonIndex: buttonIndex, alert: alertView)
}
}
func alertView(alertView: UIAlertView, didDismissWithButtonIndex buttonIndex: Int) {
if let block = didDismissBlock{//其他根據(jù)相應(yīng)的Delegate回調(diào)函數(shù)依次調(diào)用各個Block
block(buttonIndex: buttonIndex, alert: alertView)
}
}
func alertView(alertView: UIAlertView, willDismissWithButtonIndex buttonIndex: Int) {
if let block = willDismissBlock{
block(buttonIndex: buttonIndex, alert: alertView)
}
}
// 其他幾個委托方法也省略了,原理都是一樣的
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
參考上面的代碼,總體思路是在構(gòu)造器里面將委托設(shè)為自己,再將Delegate里面所有的函數(shù)用Block來實(shí)現(xiàn),Block里面的參數(shù)和委托回調(diào)的函數(shù)參數(shù)一至.當(dāng)有委托函數(shù)回調(diào)時,先判斷這個
Block是不是nil,如果不是,則執(zhí)行該Block
接下來的操作就很容易了
再直接擴(kuò)展UIAlertView,加上自己想要的鏈?zhǔn)骄幊毯瘮?shù)
extension UIAlertView {
static func setMessage(msg:String)->UIAlertView{//在這里設(shè)定這個為靜態(tài)函數(shù),在里面實(shí)例化一個BlockAlert對象,再返回該對象
let alert = BlockAlert()
alert.message = msg
return alert
}
func addAlertStyle(style:UIAlertViewStyle)->UIAlertView{ //直接在各個函數(shù)中封裝一些操作.再返回自身
self.alertViewStyle = style
return self
}
func addTitle(title:String)->UIAlertView{
self.title = title
return self
}
func addFirstButton(btnTitle:String)->UIAlertView{
self.addButtonWithTitle(btnTitle)
return self
}
func addSecondButton(btnTitle:String)->UIAlertView{
self.addButtonWithTitle(btnTitle)
return self
}
func addButtons(btnTitles:[String])->UIAlertView{
for title in btnTitles{
self.addButtonWithTitle(title)
}
return self
}
func addButtonClickEvent(clickButton:((buttonIndex:Int,alert:UIAlertView)->Void)?)->UIAlertView{
if let alert = self as? BlockAlert{//對于block,先要判斷它是不是BlockAlert,如果是的話才添加該block,后面都是這種操作
alert.completion = clickButton
}
return self
}
//這里也是省略了部分函數(shù),因?yàn)樵矶际且粯拥? func addAlertCancelEvent(event:((alert:UIAlertView)->Void)?)->UIAlertView{
if let alert = self as? BlockAlert{
alert.alertWithCancelBlock = event
}
return self
}
func alertWithButtonClick(clickButton:((buttonIndex:Int,alert:UIAlertView)->Void)?){
if let alert = self as? BlockAlert{ //這個為終結(jié)函數(shù),沒有返回值,在里面直接調(diào)用了show()方法.
alert.completion = clickButton
alert.show()
}
}
}
在這里面我選擇了用UIAlverView來擴(kuò)展,而不是BlockAlert,這樣可能會導(dǎo)致開發(fā)過程中踩進(jìn)陷阱.因?yàn)樵谑讉€靜態(tài)函數(shù)返回的對象是一個BlockAction對象,而不是UIAlertView對象
在后面添加Block時先要判斷本身是不是BlockAction對象,如果是的話再將設(shè)定Block,所以在開發(fā)過程中要小心這個陷阱.當(dāng)然讀者也可以直接擴(kuò)展BlockAlert,這樣會更安全一些.
最后直接使用靜態(tài)函數(shù)加鏈?zhǔn)骄幊虂韽棾鯝lert
UIAlertView.setMessage("這里要設(shè)置信息").addTitle("我是標(biāo)題").addFirstButton("取消").addSecondButton("確認(rèn)").alertWithButtonClick { (buttonIndex, alert) -> Void in
print("你按下了第\(buttonIndex)個按鍵")
//你按下了第1個按鍵
//你按下了第0個按鍵
}
如果要使用其他的委托方法也很簡單
UIAlertView.setMessage("這里要設(shè)置信息").addTitle("我是標(biāo)題").addFirstButton("取消").addSecondButton("確認(rèn)").addWillPresentEvent { (alert) in
print("the alertView will present")
}.addDidPresentEvent { (alert) in
print("the alertView did present")
}.alertWithButtonClick { (buttonIndex, alert) in
print("你按下了第\(buttonIndex)個按鍵")
}
當(dāng)然其他的一些自定義樣式,或者是帶用戶名輸入和密碼輸入框的也很簡單
UIAlertView.setMessage("這里要設(shè)置信息").addTitle("我是標(biāo)題").addFirstButton("取消").addSecondButton("確認(rèn)").addAlertStyle(.LoginAndPasswordInput).addDidPresentEvent { (alert) in
print("the alertView did present")
}.alertWithButtonClick { (buttonIndex, alert) in
let txt1 = alert.textFieldAtIndex(0)?.text
let txt2 = alert.textFieldAtIndex(1)?.text
print("userName:\(txt1) password:\(txt2)")
//打印出the alertView did present
//userName:Optional("111") password:Optional("222")
}

可見,使用鏈?zhǔn)骄幊碳覤lock的方式基本只需要短短幾行代碼就可以滿足大部分使用UIAlertView的使用場景,寫起來很方便快捷.對于UIActionSheet也一樣,可以將其完全改造成使用鏈?zhǔn)骄幊碳覤lock的形式來使用.這個便留給讀者
來完成了.相信看完上面的代碼并實(shí)踐過的讀者很快就可以寫出來.以上所有代碼都可以在我的Github的iOSDemo項(xiàng)目里有所有的示例:
https://github.com/DuckDeck/iOSDemo Library的 GrandCue里面。關(guān)于UIAlertView的代碼我都刪除了,而是用UIAlertController來作為示例。
因?yàn)楝F(xiàn)在Apple還是更推薦開發(fā)者使用最新的UIAlertController,它的API設(shè)計(jì)和UIAlertView是完全不同的.改成了基于Block的回調(diào)方式,使用起來更方便了.在這里這篇文章給大家詳細(xì)地介紹了
UIAlertController詳解,有興趣的讀者可以去看看
總結(jié)
本文用一個足夠簡單的例子和大家探討了怎么將原先使用UIAlertView編程方式改造成基于鏈?zhǔn)骄幊毯蚥lock的編程方式.但是在實(shí)際的開發(fā)過程中,情況要比這個復(fù)雜得多,首先要考慮該業(yè)務(wù)和邏輯適不適合使用鏈?zhǔn)骄幊?其次設(shè)計(jì)出良好的
鏈?zhǔn)骄幊藺PI也是需要花費(fèi)很大精力的.函數(shù)返回值的設(shè)計(jì)也是很需要功力的,因?yàn)樗苯佑绊懥讼乱粋€調(diào)用函數(shù).