Swift is a Protocol-Oriented Programming Language
Swift 是一门面向协议 (POP) 开发的语言
WWDC 对 OOP 很好的诠释:
Protocol-Oriented Programming in Swift
面向协议的编程的核心是抽象(Abstraction)和简化(Simplicity)
协议的高级使用是协议的延展
协议(protocol) + 结构体(struct) > 类(class)
面向对象是一个很古老的软件开发模式,通过类来实现
面向协议是苹果在 swift 中主推的,通过协议和结构体,可以代替类
Swift 中的很多对象都改成了结构体和协议
并不是所有的类都可以被协议+结构体替代,但大多数是可以被替换的
面向协议使代码更加灵活,类似于组件化开发,符合工厂方法模式
通过继承
创建一个继承类的子类,在子类中添加方法,以后使用子类即可获取这个方法
通过协议
为这个方法定义一个协议,那个类需要实现这个方法,协议即可
通过继承添加的方法,不一定每个子类都会使用,使代码冗余
拥有太多的子类,使类冗余
对于父类的方法太过依赖,当父类的方法更改后,影响子类的重载方法
…
protocol MessageModelProtocol {varname: String {getset}varage: Int {setget}}
1
2
3
4
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
extensionMessageModelProtocol{ mutating func test() {self.name ="Hello iPhone 8"}}
1
2
3
4
5
protocol DemoMessageModelProtocol: MessageModelProtocol { vardate:Date{setget}}
1
2
3
4
给 UIViewController 添加 一个数据为空视图
给 UIViewController 添加 一个遮挡提示视图
给 xib 添加一个快速获取示例方法
……(对控制器依赖比较小的视图等)
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// 设置行间距 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) // 网络请求失败 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
/// 显示数据为空视图funcshowEmptyDataView(){addEmptyView(type: .emptyData, iconName:"emptyData", tipTitle:"数据为空")}/// 显示请求失败重新加载视图funcshowNetworkErrorReloadView(){addEmptyView(type: .networkError, iconName:"network_error", tipTitle:"网络出问题了,请检查网络", action: #selector(reloadData))}/// 移除空视图/重新加载视图funcremoveEmptyView(){hideEmptyView()}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
/// 添加指示视图到当前视图的 window 上funcaddGuideView(){showGuideView(with:"您有VIP礼包待领取", imageName:"vip_image", buttonName:"立即领取", sureAction: #selector(sureAction), cancelAction: #selector(removeGuideView))}/// 提示视图上按钮的点击事件funcsureAction(){show(FirstViewController(), sender: nil)hideGuideView()}/// 移除指示视图funcremoveGuideView(){hideGuideView()}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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) }}