连续两个多月高强度的工作,每天几乎加班到8点才下班,项目在今天上午终于是提交到 App Store 了。整个人瞬时如释重负,但却有种空虚感,不知道做什么了。再看下SnapKit吧。发现之前一直认为就那么用来设置约束即可,没留意到的地方还挺多。下面分别概述下。
一、inset 和 offset
相信大部分小伙伴们都是使用 offset 来控制边距的吧。
cciView.snp.makeConstraints { (make) in
make.top.equalTo(researchView.snp.bottom).offset(kHeight.d10)
make.left.equalTo(10)
make.right.equalTo(-10)
make.bottom.equalTo(bottomView.snp.bottom)
}
其实 offset 使用的是绝对值,尤其是设置右、下约束的时候,子控件相对于父控件都需要加-号。这一点使用久了,和苹果的 xib 比起来,或多或少觉得有点不太合理。其实该框架是有 inset 来抽象控制的。下面贴下框架的源码:
public class ConstraintMakerEditable: ConstraintMakerPriortizable {
@discardableResult
public func multipliedBy(_ amount: ConstraintMultiplierTarget) -> ConstraintMakerEditable {
self.description.multiplier = amount
return self
}
@discardableResult
public func dividedBy(_ amount: ConstraintMultiplierTarget) -> ConstraintMakerEditable {
return self.multipliedBy(1.0 / amount.constraintMultiplierTargetValue)
}
@discardableResult
public func offset(_ amount: ConstraintOffsetTarget) -> ConstraintMakerEditable {
self.description.constant = amount.constraintOffsetTargetValue
return self
}
@discardableResult
public func inset(_ amount: ConstraintInsetTarget) -> ConstraintMakerEditable {
self.description.constant = amount.constraintInsetTargetValue
return self
}
}
使用 inset,之前的代码就可以简化成这样(只是举例):
make.top.equalTo(researchView.snp.bottom).offset(kHeight.d10)
make.left.equalTo(10)
make.right.equalToSuperview().inset(10)
make.bottom.equalTo(bottomView.snp.bottom)
}
总结下来就是:在描述 view 与 superview 关系时,应该使用 inset,而描述 view 与同一层级的其它 view 时,应该使用 offset。
二、ConstraintConstantTarget
在布局 view 的时候,一般来说设计师都会给 content 一个统一的边距,类似于 html 中的 padding,在构建约束时我们经常会把这个 padding 分散到各处。但事实上,将 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)
}
这样,后期修改起来会很容易,代码也简洁了很多。另外 CGPoint 和 CGSize 也遵循了这个协议,大家可以去研究下更多有趣的用法,例如 size.equalTo(20)。
三、修改约束尽量用 updateConstraints
如果要更新布局约束,小弟之前的做法是仿照苹果,用局部变量来引用,然后更新。如下:
// 局部变量保存约束属性
private var bottomViewConsT: Constraint?
// 展示 view 时更新底部约束
bottomViewConsT?.update(offset: -tmpBottomConsH)
UIView.animate(withDuration: kTime.duration, animations: {[weak self] in
self?.layoutIfNeeded()
}) { (_) in
}
// 设置约束
bottomView.snp.makeConstraints { (make) in
make.left.right.equalTo(self).offset(0)
make.height.equalTo(frame.height * 0.35)
bottomViewConsT = make.top.equalTo(self.snp.bottom).offset(0).constraint
}
// 隐藏 view 时更新底部约束
bottomViewConsT?.update(offset: 0)
UIView.animate(withDuration: 0.1, animations: { [weak self] in
self?.layoutIfNeeded()
}) { [weak self] (_) in
self?.removeFromSuperview()
}
其实可以改为:
// 展示 view 时更新底部约束
bottomView.snp.updateConstraints { (make) in
make.top.equalTo(self.snp.bottom).inset(0)
}
// 设置约束
bottomView.snp.makeConstraints { (make) in
make.left.right.equalTo(self).offset(0)
make.height.equalTo(frame.height * 0.35)
bottomViewConsT = make.top.equalTo(self.snp.bottom).offset(0).constraint
}
// ……
这么做的好处就是语法更简洁一致,让约束表现得更像是 view 的属性。但缺点也很明显,只能更新 constant。
暂时就这些吧(^_^)~~~