Swift面向协议编程

简介

面向协议编程的思想在swift中处处可见,比如swift常用的数据类型String, Array, Dictonary,等等都是通过尊村不同的协议来实现对应的功能,今天我主要是想讨论下在实际开发中怎么更好的利用面向协议编程

xib 加载 UIView

平时开发中难免会遇到使用xib创建视图的时候,加载xib视图的代码又有点冗长,这个时候使用协议就很方便

1
2
3
4
5
6
7
8
9
10
protocol NibLoadable: class {}

extension NibLoadable where Self: UIView {
static var NibName: String {
return String.init(describing: self)
}
static func loadViewFromNib() -> Self {
return Bundle.main.loadNibNamed("\(self)", owner: nil, options: nil)?.last as! Self
}
}

我们可以声明一个协议,然后在extension里添加一个默认实现,当我们需要加载一个xib视图的时候,只需要让这个xib视图遵守这个协议就可以了

1
2
3
4
5
6
7
8
class ShakeView: UIView, NibLoadable, Shakeable {

@IBOutlet weak var centerLabel: UILabel!

override func awakeFromNib() {
super.awakeFromNib()
}
}

然后我们在创建xib视图的时候就可以直接使用loadViewFromNib方法

1
2
3
4
5
lazy var shakeView: ShakeView = {
let v = ShakeView.loadViewFromNib()
v.frame = CGRect.init(x: 40, y: 100, width: 200, height: 80)
return v
}()

UITableView注册Cell

平时开发UITableView用的是比较多的,tableview的注册cell的代码我们也可以使用协议实现更简便的实现

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
/**load nib protocol*/
protocol NibLoadable: class {}

extension NibLoadable where Self: UIView {
static var NibName: String {
return String.init(describing: self)
}
static func loadViewFromNib() -> Self {
return Bundle.main.loadNibNamed("\(self)", owner: nil, options: nil)?.last as! Self
}
}


/** reuse view protocol*/
protocol ReusableView: class {}

extension ReusableView where Self: UIView {
static var reuseIdentifier: String {
return String.init(describing: self)
}
}

/**UITableView regist cell protocol*/
extension UITableView {
func register<T: UITableViewCell>(_: T.Type) where T: ReusableView, T: NibLoadable {
let Nib = UINib(nibName: T.NibName, bundle: nil)
register(Nib, forCellReuseIdentifier: T.reuseIdentifier)
}

func register<T: UITableViewCell>(_ : T.Type) where T: ReusableView {
register(T.self, forCellReuseIdentifier: T.reuseIdentifier)
}
}

这样我们在注册cell的时候就可以写出比较简洁的代码

1
2
//        tableView.register(MMTableViewCell.self)
tableView.register(MMNibTableViewCell.self)

另外我们也可以用协议让创建和复用cell更简洁

1
2
3
4
5
6
7
8
9
/**UITableView create cell protocol*/
extension UITableView {
func dequeueReusableCell<T: UITableViewCell>(forIndexPath indexPath: IndexPath) -> T where T: ReusableView {
guard let cell = dequeueReusableCell(withIdentifier: T.reuseIdentifier, for: indexPath as IndexPath) as? T else {
fatalError("Could not dequeue cell with identifier: \(T.reuseIdentifier)")
}
return cell
}
}

使用的时候

1
2
3
4
5
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(forIndexPath: indexPath) as MMNibTableViewCell
cell.textLabel?.text = "the nib row \(indexPath.row)"
return cell
}

实现抖动view

网上很多人都会拿这个举例面向协议,个人觉得确实很贴切,当你想要实现一个视图的抖动确实有很多方法,但是使用协议确实很方便,只要视图遵守协议,就具有对应功能,,想去除这个功能的时候就取消对协议的遵守就可以了,对代码的侵入性也比较小,这样对代码的维护和扩展,还有可读性都很好,就像swift里的Array遵守了Equatable、Collection等不同的协议来实现不同的功能

1
2
3
4
5
6
7
8
9
10
11
12
13
protocol Shakeable {}

extension Shakeable where Self : UIView {
func shake() {
let animation = CABasicAnimation(keyPath: "position")
animation.duration = 0.05
animation.repeatCount = 5
animation.autoreverses = true
animation.fromValue = CGPoint.init(x: self.center.x - 4.0, y: self.center.y)
animation.toValue = CGPoint.init(x: self.center.x + 4.0, y: self.center.y)
layer.add(animation, forKey: "position")
}
}

使用

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
import UIKit

class ShakeView: UIView, NibLoadable, Shakeable {

@IBOutlet weak var centerLabel: UILabel!

override func awakeFromNib() {
super.awakeFromNib()
}
}



class ViewController: UIViewController {

//MARK: - lazy var
lazy var shakeView: ShakeView = {
let v = ShakeView.loadViewFromNib()
v.frame = CGRect.init(x: 40, y: 100, width: 200, height: 80)
return v
}()

// MARK: - Action
@objc func btnAction() {
shakeView.shake()
}
}

这里是相关Demo,如有需要可自取

参考

Swift 面向协议编程
刀真枪 面向协议编程