Swift-核心之面向協(xié)議開發(fā)

Swift is a Protocol-Oriented Programming Language

Swift 是一門面向協(xié)議 (POP) 開發(fā)的語(yǔ)言

Swift 的核心是面向協(xié)議編程

WWDC 對(duì) OOP 很好的詮釋:

Protocol-Oriented Programming in Swift

POP 面向協(xié)議的編程

面向協(xié)議的編程的核心是抽象(Abstraction)和簡(jiǎn)化(Simplicity)?

協(xié)議的高級(jí)使用是協(xié)議的延展?

協(xié)議(protocol) + 結(jié)構(gòu)體(struct) > 類(class)

面向?qū)ο?/b>與面向協(xié)議比較

面向?qū)ο笫且粋€(gè)很古老的軟件開發(fā)模式,通過類來實(shí)現(xiàn)

面向協(xié)議是蘋果在 swift 中主推的,通過協(xié)議和結(jié)構(gòu)體,可以代替類

Swift 中的很多對(duì)象都改成了結(jié)構(gòu)體和協(xié)議

并不是所有的類都可以被協(xié)議+結(jié)構(gòu)體替代,但大多數(shù)是可以被替換的

面向協(xié)議使代碼更加靈活,類似于組件化開發(fā),符合工廠方法模式

實(shí)例比較:給一個(gè)類添加額外的方法

通過繼承?

創(chuàng)建一個(gè)繼承類的子類,在子類中添加方法,以后使用子類即可獲取這個(gè)方法?

通過協(xié)議?

為這個(gè)方法定義一個(gè)協(xié)議,那個(gè)類需要實(shí)現(xiàn)這個(gè)方法,協(xié)議即可

使用繼承的缺點(diǎn):

通過繼承添加的方法,不一定每個(gè)子類都會(huì)使用,使代碼冗余

擁有太多的子類,使類冗余

對(duì)于父類的方法太過依賴,當(dāng)父類的方法更改后,影響子類的重載方法

什么時(shí)候使用面向?qū)ο蟮念惸兀?/a>

協(xié)議的高級(jí)使用是協(xié)議的延展以及和結(jié)構(gòu)體配合

為協(xié)議添加屬性, 屬性為可讀或可寫

protocol MessageModelProtocol {varname: String {getset}varage:? Int {setget}}

1

2

3

4

定義一個(gè)接受協(xié)議的結(jié)構(gòu)體

struct MessageModel: MessageModelProtocol {varname: String =""varage: Int =0varisMale: Bool =falseinit(with dict: [String: Any]) {self.name = (dict["name"]as? String) ??""self.age = (dict["age"]as? Int) ??0self.isMale = (dict["isMale"]as? Bool) ??false}}

1

2

3

4

5

6

7

8

9

10

11

12

對(duì)協(xié)議進(jìn)行延展

extensionMessageModelProtocol{? ? mutating func test() {self.name ="Hello iPhone 8"}}

1

2

3

4

5

協(xié)議的協(xié)議

protocol DemoMessageModelProtocol: MessageModelProtocol {? ? vardate:Date{setget}}

1

2

3

4

具體實(shí)例運(yùn)用:

給 UIViewController 添加 一個(gè)數(shù)據(jù)為空視圖

給 UIViewController 添加 一個(gè)遮擋提示視圖

給 xib 添加一個(gè)快速獲取示例方法

……(對(duì)控制器依賴比較小的視圖等)

數(shù)據(jù)為空或者網(wǎng)絡(luò)請(qǐng)求失敗提示界面


import UIKitenum EmptyType {? ? case emptyData? ? case networkError}protocol EmptyDataSetProtocol { }extension EmptyDataSetProtocol where Self : UIViewController {? ? func addEmptyView(type: EmptyType? =.emptyData, iconName: String, tipTitle: String, action: Selector? = nil) {? ? ? ? let emptyView = UIView(frame: view.bounds)? ? ? ? emptyView.backgroundColor= UIColor.whiteemptyView.tag=1024view.addSubview(emptyView)? ? ? ? let icomViewW: CGFloat =100let imageView = UIImageView(image: UIImage(named: iconName))? ? ? ? imageView.frame.size= imageView.image?.size?? CGSize(width: icomViewW, height: icomViewW)? ? ? ? imageView.contentMode=.centerimageView.center= CGPoint(x: emptyView.center.x,y: emptyView.center.y-100)? ? ? ? emptyView.addSubview(imageView)? ? ? ? let tipLabel = UILabel()? ? ? ? let margin: CGFloat =20tipLabel.numberOfLines=0tipLabel.font= UIFont.systemFont(ofSize:14)? ? ? ? tipLabel.textColor= UIColor.lightGrayif tipTitle.contains("\n") {? ? ? ? ? ? let style = NSMutableParagraphStyle()? ? ? ? ? ? style.lineSpacing=5// 設(shè)置行間距? ? ? ? ? ? style.alignment=.center// 文字居中? ? ? ? ? ? let tipString = NSAttributedString(string: tipTitle, attributes: [NSParagraphStyleAttributeName: style])? ? ? ? ? ? tipLabel.attributedText= tipString? ? ? ? } else {? ? ? ? ? ? tipLabel.text= tipTitle? ? ? ? }? ? ? ? tipLabel.adjustsFontSizeToFitWidth= true? ? ? ? tipLabel.textAlignment=.centertipLabel.sizeToFit()? ? ? ? tipLabel.frame= CGRect(x: margin,y: imageView.frame.maxY+ margin, width: UIScreen.main.bounds.width- margin*2, height: tipLabel.bounds.height)? ? ? ? emptyView.addSubview(tipLabel)? ? ? ? // 網(wǎng)絡(luò)請(qǐng)求失敗? ? ? ? if type ==.networkError{? ? ? ? ? ? let reloadButton = UIButton(type:.system)? ? ? ? ? ? reloadButton.frame.size= CGSize(width:100, height:36)? ? ? ? ? ? reloadButton.center= CGPoint(x: emptyView.center.x,y: tipLabel.frame.maxY+ margin*2)? ? ? ? ? ? reloadButton.backgroundColor= UIColor(red:255/255.0, green:42/255.0, blue:102/255.0, alpha:1.0)? ? ? ? ? ? reloadButton.layer.cornerRadius=18reloadButton.layer.masksToBounds= true? ? ? ? ? ? reloadButton.setTitle("重新加載", for:.normal)? ? ? ? ? ? reloadButton.setTitleColor(UIColor.white, for:.normal)? ? ? ? ? ? reloadButton.titleLabel?.font= UIFont.systemFont(ofSize:16)? ? ? ? ? ? reloadButton.addTarget(self, action: action!, for:.touchUpInside)? ? ? ? ? ? emptyView.addSubview(reloadButton)? ? ? ? }? ? }? ? func hideEmptyView() {? ? ? ? view.subviews.filter({ $0.tag ==1024}).first?.removeFromSuperview()? ? }}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

具體使用:

class ViewController: UIViewController, EmptyDataSetProtocol {...}

1

/// 顯示數(shù)據(jù)為空視圖funcshowEmptyDataView(){addEmptyView(type: .emptyData, iconName:"emptyData", tipTitle:"數(shù)據(jù)為空")}/// 顯示請(qǐng)求失敗重新加載視圖funcshowNetworkErrorReloadView(){addEmptyView(type: .networkError, iconName:"network_error", tipTitle:"網(wǎng)絡(luò)出問題了,請(qǐng)檢查網(wǎng)絡(luò)", action: #selector(reloadData))}/// 移除空視圖/重新加載視圖funcremoveEmptyView(){hideEmptyView()}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

添加 guide 視圖 到 window 上

import UIKitprotocol GuideViewProtocol { }extension GuideViewProtocol where Self : UIViewController {? ? func showGuideView(with title: String, imageName: String, buttonName: String, sureAction: Selector, cancelAction: Selector) {? ? ? ? let kScreenW: CGFloat = UIScreen.main.bounds.widthlet kMargine: CGFloat =30let backgroundView = UIView(frame: UIScreen.main.bounds)? ? ? ? backgroundView.backgroundColor= UIColor.black.withAlphaComponent(0.3)? ? ? ? backgroundView.tag=1024UIApplication.shared.keyWindow?.addSubview(backgroundView)? ? ? ? let containerView = UIView()? ? ? ? containerView.frame.size= CGSize(width: kScreenW-kMargine*2, height: (kScreenW-kMargine*2) +20)? ? ? ? containerView.backgroundColor= UIColor.whitecontainerView.center= backgroundView.centercontainerView.layer.cornerRadius=15backgroundView.addSubview(containerView)? ? ? ? let tipLabel = UILabel(frame: CGRect(x: kMargine,y:30, width: containerView.bounds.width-kMargine*2, height: kMargine))? ? ? ? tipLabel.font= UIFont.systemFont(ofSize:20)? ? ? ? tipLabel.textColor= UIColor.redtipLabel.textAlignment=.centertipLabel.text= title? ? ? ? containerView.addSubview(tipLabel)? ? ? ? let sureButton = UIButton(type:.system)? ? ? ? sureButton.frame.size= CGSize(width:200, height:30)? ? ? ? sureButton.setTitle(buttonName, for:.normal)? ? ? ? sureButton.setTitleColor(UIColor.white, for:.normal)? ? ? ? sureButton.backgroundColor= UIColor.redsureButton.titleLabel?.font= UIFont.systemFont(ofSize:18)? ? ? ? sureButton.frame.origin.x= (containerView.bounds.width- sureButton.bounds.width)*0.5sureButton.center.y= containerView.bounds.height-20- sureButton.bounds.heightsureButton.layer.cornerRadius=15sureButton.addTarget(self, action: sureAction, for:.touchUpInside)? ? ? ? containerView.addSubview(sureButton)? ? ? ? let centerImageView = UIImageView(image: UIImage(named: imageName))? ? ? ? centerImageView.contentMode=.scaleAspectFitcenterImageView.frame= CGRect(x:30,y: tipLabel.frame.maxY+20, width: containerView.bounds.width-60, height: sureButton.frame.minY- tipLabel.frame.maxY-40)? ? ? ? containerView.addSubview(centerImageView)? ? ? ? let cancelButton = UIButton(type:.custom)? ? ? ? cancelButton.setBackgroundImage(UIImage(named:"cancelButton"), for:.normal)? ? ? ? cancelButton.frame.size= cancelButton.currentBackgroundImage?.size?? CGSize.zerocancelButton.center.x= UIScreen.main.bounds.width*0.5cancelButton.center.y= containerView.frame.maxY+50cancelButton.addTarget(self, action: cancelAction, for:.touchUpInside)? ? ? ? backgroundView.addSubview(cancelButton)? ? }? ? func hideGuideView() {? ? ? ? UIView.animate(withDuration:0.25) {? ? ? ? ? ? UIApplication.shared.keyWindow?.subviews.filter({ $0.tag ==1024}).first?.removeFromSuperview()? ? ? ? }? ? }}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

具體使用

class ViewController: UIViewController, GuideViewProtocol {...}

1

/// 添加指示視圖到當(dāng)前視圖的 window 上funcaddGuideView(){showGuideView(with:"您有VIP禮包待領(lǐng)取", imageName:"vip_image", buttonName:"立即領(lǐng)取", sureAction: #selector(sureAction), cancelAction: #selector(removeGuideView))}/// 提示視圖上按鈕的點(diǎn)擊事件funcsureAction(){show(FirstViewController(), sender: nil)hideGuideView()}/// 移除指示視圖funcremoveGuideView(){hideGuideView()}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

修改導(dǎo)航欄左側(cè)按鈕

protocol LeftBarButtonChangeable { }extension LeftBarButtonChangeable where Self :UIViewController{? ? func changeLeftBarButton(_ imageName: String, action: Selector ) {? ? ? ? let itemButton =UIButton(type:.custom)? ? ? ? itemButton.setImage(UIImage(named: imageName),for:.normal)? ? ? ? itemButton.sizeToFit()? ? ? ? itemButton.addTarget(self, action: action,for:.touchUpInside)self.navigationItem.leftBarButtonItem=UIBarButtonItem(customView: itemButton)? ? }}

1

2

3

4

5

6

7

8

9

10

11

12

13

具體使用

classViewController:UIViewController,LeftBarButtonChangeable{override func viewDidLoad() {super.viewDidLoad()? ? ? ? changeLeftBarButton("back_image",action:#selector(backAction))}? ? func backAction() {? ? ? ? navigationController?.popViewController(animated:true)? ? }}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容