一个轻量级、简洁、声明式的 Autolayout 语法。(
我们都使用 Autolayout 在 UIKit 中排列和定位 UI 组件。 但是用代码实现它相当考验大脑,需要想象这段代码在屏幕上的呈现方式。
我们需要考虑按钮、标签、各种控件、子视图。 所有 UI 组件及其之间的关系,父级、子级、兄弟、安全区域、布局指南、尺寸类 🤯。
让我们尝试从 SwiftUI 和一个不太为人熟知的朋友 Swift KeyPaths
中汲取一些灵感,使事情变得更简单一些。
我们都写过这样的代码。
self.view.addSubview(otherView)
otherView.translatesAutoresizingMaskIntoConstraints = true
otherView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
otherView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -16).isActive = true
...
在上面的代码中,仅仅为了将 otherView
的 leadingAnchor
相对于 view
添加约束,整行代码就非常冗长和繁琐。
otherView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
如果我们只用以下方式编写相同的代码会怎样?
self.view.anchor(otherView) {
EqualTo(\.leadingAnchor)
EqualTo(\.trailingAnchor).constant(-16)
}
一目了然,更加清晰、简洁和易读。
原生 | AutolayoutExtension | 锚点类型 |
---|---|---|
.constraint(equalTo:) |
EqualTo (等于) |
NSLayoutAnchor<Axis> |
.constraint(equalTo:constant) |
EqualToConstant (等于常量) |
NSLayoutDimension |
.constraint(greaterThanOrEqualTo:) |
GreaterThanOrEqualTo (大于等于) |
NSLayoutAnchor<Axis> |
.constraint(greaterThanOrEqualTo:constant) |
GreaterThanOrEqualToConstant (大于等于常量) |
NSLayoutDimension |
.constraint(lessThanOrEqualTo:) |
LessThanOrEqualTo (小于等于) |
NSLayoutAnchor<Axis> |
.constraint(lessThanOrEqualTo:Constant) |
LessThanOrEqualToConstant (小于等于常量) |
NSLayoutDimension |
有 3 个内置的约束修饰符可用。
.constant(_ constant: CGFloat)
.priority(_ priority : UILayoutPriority)
.id<ID: Hashable>(_ id: ID)
import AutolayoutExtension
import UIKit
class AView: UIView {
var condition: Bool = false {
didSet {
self.updateLayout()
}
}
private var labelTopConstraint: NSLayoutConstraint?
private var imageBottomConstraint: NSLayoutConstraint?
private lazy var titleLabel: UILabel = {
let label = UILabel()
return label
}()
private lazy var imageView: UIImageView = {
let imageView = UIImageView()
return imageView
}()
override init(frame: CGRect) {
super.init(frame: frame)
configureSubviews()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
configureSubviews()
}
private func configureSubviews() {
self.addSubview(titleLabel)
self.addSubview(imageView)
let topAnchorID = UUID()
let bottomAnchorID = UUID()
let labelConstraints = self.constraint(titleLabel) {
EqualTo(\.leadingAnchor)
EqualTo(\.trailingAnchor)
EqualTo(\.topAnchor)
.constant(24)
.id(topAnchorID)
EqualToConstant(\.heightAnchor, constant: 32)
}
let imageConstraints = self.constraint(imageView) {
EqualTo(\.centerXAnchor)
GreaterThanOrEqualTo(\.leadingAnchor)
.constant(12)
EqualTo(\.topAnchor, with: titleLabel.bottomAnchor)
.constant(12)
EqualTo(\.bottomAnchor)
.priority(.defaultHigh)
.id(bottomAnchorID)
}
self.labelTopConstraint = labelConstraints[topAnchorID]
self.imageBottomConstraint = imageConstraints[bottomAnchorID]
}
private func updateLayout() {
self.labelTopConstraint?.constant = condition ? 24 : 32
self.imageBottomConstraint?.constant = condition ? 24 : 0
self.layoutIfNeeded()
}
}