SnapKit 为什么这么好用?

1,465 阅读3分钟

以前在项目中都会用到 SnapKit 来布局 UI,最近看了看 SnapKit 的源码,才知道 SnapKit 为什么如此优秀。

那么,为什么 SnapKit 会比较好用?

我们来对比一下使用原生的方法进行布局和使用 SnapKit 进行布局的差别。加入我们要将一个 label 放到 view 的中心,并且设置 label 的 width 为 300.0。

我们先来看一下使用 NSLayoutConstraint 进行布局的代码:

view.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
let centerX = NSLayoutConstraint(item: label, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerX, multiplier: 1.0, constant: 0)
let centerY = NSLayoutConstraint(item: label, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .centerY, multiplier: 1.0, constant: 0)
let width = NSLayoutConstraint(item: label, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 300.0)
view.addConstraints([centerX, centerY, width])

使用 iOS 9.0 之后的 NSLayoutAnchor 布局:

view.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
label.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0).isActive = true
label.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0).isActive = true
label.widthAnchor.constraint(equalToConstant: 300).isActive = true

使用 SnapKit 的代码为:

view.addSubview(label)
label.snp.makeConstraints { (maker) in
    maker.center.equalToSuperview()
    maker.width.equalTo(300)
}

经过对比,SnapKit 在代码做到了简洁、清晰。

SnapKit 最终还是生成了原生的约束代码,但是为什么可以使代码做到简洁呢?我觉得有以下几点:

允许同时设置多个属性(Attributes)

原生代码一次只能设置一个属性值,而 SnapKit 允许同时设置多个属性值。当多个属性值的设置类似或相同时,就减少了不必要的重复。

为了实现这一点,SnapKit 使用了自定义 ConstraintAttributes 结构,扩充了原生的属性范围。

前置条件的自动设置

在设置一个 view 的约束时,它的 translatesAutoresizingMaskIntoConstraints 都会设置为 false。Apple 把这个属性值默认为 ture, 应该是方便将设置 frame 的代码迁移到自动约束上,但是这却给新的项目带来了麻烦。

SnapKit 中会在设置约束之前将 translatesAutoresizingMaskIntoConstraints 设置为 false,避免了重复的代码。

抽象参考对象

在原生中,必须设置一个参考对象(AnyObject?)和参考属性。在 SnapKit 中,参考对象可以是数值类型(Int,Float,Edge 等),也可以是 view 等,而且不需要必须设置属性。

为了实现这一点,SnapKit 将参考对象抽象成了 ConstraintRelatableTarget 协议。

public protocol ConstraintRelatableTarget {
    
}

SnapKit 再根据具体的类型进行转化具体的参考对象和参考属性。

  • 有参考对象的,采用参考对象,约束中的常量(constant)设置为 0;没有参考对象的,将参考对象设置为 nil,将值设置为约束中的常量(constant)
  • 有参考属性的,采用被设置的属性;没有的,则采用被设置 view 的属性。

将设置进行归并

SnapKit 将零散的约束代码进行了归并:

  1. 将对一个 view 的约束归并到了一个方法内;
  2. 将相同参考对象或参考值归为了一类;

这种思路上的转变使得代码更为简单。

将约束添加到 view 的属性当中

在布局的过程中,除了设置,我们还会修改、移除属性。为了方便获取已经添加的布局约束,SnapKit 通过运行时方法添加了一个 NSMutableSet,用于保存添加的约束。在更新约束的时候就可以判断当前是否已经设置为相应的属性。