UIAlertView是iOS開發過程中最常用的控件之一,是提醒使用者做出選擇最主要的工具.在iOS8及後來的系統中,蘋果更推薦使用UIAlertController來代替UIAlertView.是以本文也并不提倡開發者再使用UIAlertView,
本文的目的是探讨如何将原來的給變量指派和通過Delete來回調的方式變成鍊式程式設計風格和通過Block來回調.通過學習對UIAlertView的改造讓各位iOS開發者能夠學會這種更加便捷的開發方式
什麼是鍊式程式設計
對于有一定開發經驗的開發者來說,鍊式程式設計并不陌生.有很多知名的開源庫使用了這種程式設計風格,比如大名鼎鼎的JQuery,而iOS庫中,Masory和Snapkit也是典型的使用鍊式程式設計的例子.
大體來說,鍊式程式設計就是将多個操作(多行代碼)通過某種操作符(通常是點号.)連結成一句的代碼.便代碼更加緊湊,可讀性也更好,降低了代碼的重複度.比如以下代碼:
1 //使用普通的指派模式
2 txtUserName.placeHolder = "請輸入使用者名"
3 txtUserName.font = UIFont.systemFont(15)
4 txtUserName.textColor = UIColor.GrayColor()
5 //使用鍊式程式設計的語句
6 txtUserName.setPlaceHolder("請輸入使用者名").setFont(15).setTextColor(UIColor.GrayColor())
通過這個例子讀者應該可以更清楚的了解什麼是鍊式程式設計風格.簡單來說,鍊式程式設計風格要有以下特點
- 通常是都是調用一個函數來給屬性指派.也就是說,該函數封裝了指派的語句,還可以在裡面加入自己的判斷和一些邏輯.
- 該函數必須有一個傳回值,通常是它本身.也可以是處理後的資料或者對象.
- 可以用靜态函數作為起始函數,但是後面的全是執行個體函數.
- 可以設定一個最終函數,該函數不傳回任何對象,用它來完全最後所有操作.
鍊式程式設計的利弊
使用鍊式程式設計最主要的好處是可以使代碼更簡潔,寫起來一種"爽快"感.設計優秀的鍊式程式設計可以大大降低重複的代碼,增強邏輯感.不足之處就是對開
發者的業務邏輯能力要求較高,同時因為鍊式程式設計都是調用函數,是以有可能會造成過深的函數調用棧.稍微影響性能.
使用Block來回調UIAlertView的Delegate
iOS開發中有很多回調都是使用Delegate完成的,相信各位讀者已經寫過很多次了.相對來說,使用Delegate比較繁瑣,有時還需要在Delegate裡
判斷是哪個對象的Delegate,優點是運作效率較高.而Block則相反,寫起來更直覺,開發效率更高.蘋果也是逐漸使用Block來代替Delegate,iOS
8最新的UIViewController裡的Action已經全部使用Block來實作.而且Swift裡的閉包,匿名函數更上比比皆是.是以大膽地使用Block來代替
Delegate和Target-Action吧,蘋果會幫我們處理好各種性能問題的.但是需要注意的是retian-circle問題,初識Block很容易出現這種問題.
使用鍊式程式設計和Block來改造UIAlertView
目前有兩種方式可以實作将UIAlertView的Delegate改成Block的回調,一是使用運作時給UIAlertView加上Block屬性.二是再寫一個新的類繼承
UIAlertView,本文使用的是第二種方式,寫起來更簡單一點.
先定義一個新的類來繼承UIAlertView,并且實作UIAlertViewDelegate協定
1 class BlockAlert:UIAlertView,UIAlertViewDelegate{
2 var completion:((buttonIndex:Int,alert:UIAlertView)->Void)? //定義各個Block用來回調,其參數名和Delegate回調的函數參數名一至
3 var willDismissBlock:((buttonIndex:Int,alert:UIAlertView)->Void)?
4 var didDismissBlock:((buttonIndex:Int,alert:UIAlertView)->Void)?
5 var didPresentBlock:((alert:UIAlertView)->Void)?
6 var willPresentBlock:((alert:UIAlertView)->Void)?
7 var alertWithCancelBlock:((alert:UIAlertView)->Void)?
8 override init(frame: CGRect) {
9 super.init(frame: frame)
10 self.delegate = self //将delegate設為自己
11 }
12 func alertView(alertView: UIAlertView, clickedButtonAtIndex buttonIndex: Int) {
13 if let block = completion{ //最主要的Block,當使用者點了按鈕時回調,先要判斷這個Block存在不?
14 block(buttonIndex: buttonIndex, alert: alertView)
15 }
16 }
17 func alertView(alertView: UIAlertView, didDismissWithButtonIndex buttonIndex: Int) {
18 if let block = didDismissBlock{//其他根據相應的Delegate回調函數依次調用各個Block
19 block(buttonIndex: buttonIndex, alert: alertView)
20 }
21 }
22 func alertView(alertView: UIAlertView, willDismissWithButtonIndex buttonIndex: Int) {
23 if let block = willDismissBlock{
24 block(buttonIndex: buttonIndex, alert: alertView)
25 }
26 }
27 // 其他幾個委托方法也省略了,原理都是一樣的
28 required init?(coder aDecoder: NSCoder) {
29 fatalError("init(coder:) has not been implemented")
30 }
31 }
參考上面的代碼,總體思路是在構造器裡面将委托設為自己,再将Delegate裡面所有的函數用Block來實作,Block裡面的參數和委托回調的函數參數一至.當有委托函數回調時,先判斷這個
Block是不是nil,如果不是,則執行該Block
接下來的操作就很容易了
再直接擴充UIAlertView,加上自己想要的鍊式程式設計函數
1 extension UIAlertView {
2 static func setMessage(msg:String)->UIAlertView{//在這裡設定這個為靜态函數,在裡面執行個體化一個BlockAlert對象,再傳回該對象
3 let alert = BlockAlert()
4 alert.message = msg
5 return alert
6 }
7
8 func addAlertStyle(style:UIAlertViewStyle)->UIAlertView{ //直接在各個函數中封裝一些操作.再傳回自身
9 self.alertViewStyle = style
10 return self
11 }
12
13 func addTitle(title:String)->UIAlertView{
14 self.title = title
15 return self
16 }
17
18 func addFirstButton(btnTitle:String)->UIAlertView{
19 self.addButtonWithTitle(btnTitle)
20 return self
21 }
22
23 func addSecondButton(btnTitle:String)->UIAlertView{
24 self.addButtonWithTitle(btnTitle)
25 return self
26 }
27
28 func addButtons(btnTitles:[String])->UIAlertView{
29 for title in btnTitles{
30 self.addButtonWithTitle(title)
31 }
32 return self
33 }
34
35 func addButtonClickEvent(clickButton:((buttonIndex:Int,alert:UIAlertView)->Void)?)->UIAlertView{
36 if let alert = self as? BlockAlert{//對于block,先要判斷它是不是BlockAlert,如果是的話才添加該block,後面都是這種操作
37 alert.completion = clickButton
38 }
39 return self
40 }
41
42 //這裡也是省略了部分函數,因為原理都是一樣的
43 func addAlertCancelEvent(event:((alert:UIAlertView)->Void)?)->UIAlertView{
44 if let alert = self as? BlockAlert{
45 alert.alertWithCancelBlock = event
46 }
47 return self
48 }
49
50
51 func alertWithButtonClick(clickButton:((buttonIndex:Int,alert:UIAlertView)->Void)?){
52 if let alert = self as? BlockAlert{ //這個為終結函數,沒有傳回值,在裡面直接調用了show()方法.
53 alert.completion = clickButton
54 alert.show()
55 }
56 }
57 }
在這裡面我選擇了用UIAlverView來擴充,而不是BlockAlert,這樣可能會導緻開發過程中踩進陷阱.因為在首個靜态函數傳回的對象是一個BlockAction對象,而不是UIAlertView對象
在後面添加Block時先要判斷本身是不是BlockAction對象,如果是的話再将設定Block,是以在開發過程中要小心這個陷阱.當然讀者也可以直接擴充BlockAlert,這樣會更安全一些.
最後直接使用靜态函數加鍊式程式設計來彈出Alert
UIAlertView.setMessage("這裡要設定資訊").addTitle("我是标題").addFirstButton("取消").addSecondButton("确認").alertWithButtonClick { (buttonIndex, alert) -> Void in
print("你按下了第\(buttonIndex)個按鍵")
//你按下了第1個按鍵
//你按下了第0個按鍵
}
如果要使用其他的委托方法也很簡單
1 UIAlertView.setMessage("這裡要設定資訊").addTitle("我是标題").addFirstButton("取消").addSecondButton("确認").addWillPresentEvent { (alert) in
2 print("the alertView will present")
3 }.addDidPresentEvent { (alert) in
4 print("the alertView did present")
5 }.alertWithButtonClick { (buttonIndex, alert) in
6 print("你按下了第\(buttonIndex)個按鍵")
7 }
1 UIAlertView.setMessage("這裡要設定資訊").addTitle("我是标題").addFirstButton("取消").addSecondButton("确認").addAlertStyle(.LoginAndPasswordInput).addDidPresentEvent { (alert) in
2 print("the alertView did present")
3 }.alertWithButtonClick { (buttonIndex, alert) in
4 let txt1 = alert.textFieldAtIndex(0)?.text
5 let txt2 = alert.textFieldAtIndex(1)?.text
6 print("userName:\(txt1) password:\(txt2)")
7 //列印出the alertView did present
8 //userName:Optional("111") password:Optional("222")
9 }
帶使用者名密碼的Alert