用了 SnapKit 很久, 一开始觉得这就是个很简单的语法糖, 后面用着用着还是觉得有点磕磕绊绊, 所以又回去看过了一遍官方文档 http://snapkit.io/docs/ , 发现了几个 best practice 是我之前一直没留意到的, 就写出来分享一下.
inset 是个高级抽象
刚开始使用 SnapKit 时, 我都是直接使用 offset 来控制边距的:
- view.snp.makeConstraints {$0.top.left.equalToSuperview().offset(10)
- $0.right.bottom.equalToSuperview().offset(-10)
- }
offset 使用的是绝对值, 例如说 superview 的 bottom 是 300 时, 那 view 的 bottom 就会是 300 + (-10).
为了简化在这种情况下的语法, SnapKit 封装了一个高级抽象 inset, 帮我们自动转换:
- switch layoutAttribute {
- case .left : return value.left
- case .top : return value.top
- case .right : return -value.right
- case .bottom : return -value.bottom
- ...
- }
使用 inset, 之前的代码就可以简化成这样:
- view.snp.makeConstraints {
- $0.top.left.bottom.right.equalToSuperview().inset(10)
- // 或者直接使用 edges
- $0.edges.equalToSuperview().inset(10)
- }
总结一句就是, 在描述 view 与 superview 关系时, 应该使用 inset, 而描述 view 与同一层级的其它 view 时, 应该使用 offset.
不可忽视的 ConstraintConstantTarget
在一个 view 里, 一般来说设计师都会给 content 一个统一的边距, 类似于 h5 里 padding 的概念, 在构建约束时我们经常会把这个 padding 分散到各处:
- container.addSubview(a)
- container.addSubview(b)
- a.snp.makeConstraints {
- $0.top.equalToSuperview().inset(5)
- $0.left.right.equalToSuperview().inset(15)
- }
- b.snp.makeConstraints {
- $0.top.equalTo(a.snp.bottom).offset(5)
- $0.left.right.equalToSuperview().inset(15)
- $0.bottom.equalToSuperview().inset(5)
- }
同是 padding 但却分散去处理是一件很糟糕的事情, 更好的方式是使用已有的抽象 UIEdgeInsets.
在调用 equalTo, offset 或者 inset 传入数值时, 我们会发现传入的参数类型实际上只有
ConstraintConstantTarget
, 这是一个协议, SnapKit 把它作为一个类簇在使用, 通过一个方法将它转化为 CGFloat 来作为 constraint 的 constant.
UIEdgeInsets 也遵循了这个协议, 所以我们可以更加优雅地去处理边距:
- let containerInsets = UIEdgeInsets(top: 5, left: 15, bottom: 5, right:15)
- container.addSubview(a)
- container.addSubview(b)
- a.snp.makeConstraints {
- $0.top.left.right.equalToSuperview().inset(containerInsets)
- }
- b.snp.makeConstraints {
- $0.top.equalTo(a.snp.bottom).offset(5)
- $0.left.bottom.right.equalToSuperview().inset(containerInsets)
- }
通过这样的代码, 绝大部分时候我们都可以只用一行代码去描述 view 跟 superview 之间的边距, 而且修改起来也很方便. 另外 CGPoint 和 CGSize 也遵循了这个协议, 大家可以去挖掘更多有趣的用法, 例如 size.equalTo(20).
修改约束时尽量使用 updateConstraints
原生的 NSLayoutConstraint 在使用时, 如果我们需要修改 constant 的值, 一般会使用一个变量去引用, 有需要时再去通过这个引用修改它的 constant.
同样的方式也适用于 SnapKit, 我们可以通过 constraint 方法去获取到这个约束, 然后强引用它:
- var someConstraint: Constriant?
- a.snp.makeConstriants {
- someConstraint = $0.top.equalToSuperview().constraint
- $0.left.equalToSuperview().inset(15)
- $0.bottom.equalToSuperview()
- }
但这种方式会让代码看起来很混乱, 并且 top 跟 bottom 的约束必须拆成两行, 一次性只能引用一个约束. 更好的方式是使用 updateConstraints 方法:
- a.snp.makeConstriants {
- $0.top.bottom.equalToSuperview()
- $0.left.equalToSuperview().inset(15)
- }
- ...
- a.snp.updateConstraints {
- $0.top.equalToSuperview().inset(10)
- }
这个方法会遍历现有的所有约束, 然后找到你在 updateConstraints 里更新的约束, 更新它们的 constant.
这么做的好处就是语法更简洁一致, 让约束表现得更像是 view 的属性. 但缺点也很明显, 只能更新 constant.
来源: https://juejin.im/post/5ac6330d518825558a06da47