SwiftAutoLayout
概览
SwiftAutoLayout 协助您以最为简洁、Swift风格和本地化地编写 AutoLayout 约束。可以使用熟悉的语法,这种语法与它们的原生属性相匹配,相互转换地约束 UIView
和 UILayoutGuide
。此库故意最小化定义视图层次结构和构建约束的重复代码,而通过可选参数最大化和约束灵活性。
SwiftAutoLayout 尽可能地与 AutoLayout API 匹配,仅在改进可读性和简化编码量时才包装类型。这意味着您的 AutoLayout 知识可直接转换为 SwiftAutoLayout,并在上面引入最小功能。SwiftAutoLayout 不提供用于定义约束的自定义闭包或语法,更倾向于功能性编程角度,以保持行数为最少数。
用法
将视图约束到父视图
首先思考您想影响哪两个视图。在这个示例中,一个标签将被约束到一个 UIViewController
的视图,并将其添加到视图层次结构中。
// UIViewController subclass
override func viewDidLoad() {
super.viewDidLoad()
// Create a label
let label = UILabel()
label.text = "SwiftAutoLayout is neato!"
// Constrain its leading and centerY anchors to be equal to our view's respective anchors
// Because label doesn't yet have a parent, it will become a child of our view
label.constrain(to: view).leading().centerY()
}
就是这样!标签现在已经在层次结构中并且已经被正确约束。
构建视图层次结构
constrain(to:)
方法在执行任何约束之前执行一些有用的操作。调用此方法的视图将禁用其 translatesAutoresizingMaskIntoConstraints
,如果没有父视图,它将成为第二个视图的子视图。这使得在同时构建约束的同时定义视图层次结构变得容易。
// Wrap a label in a container view that has a grey background and some internal padding.
let container = UIView()
container.backgroundColor = .gray
container.directionalLayoutMargins = NSDirectionalEdgeInsets(top: 8, leading: 8, bottom: 8, trailing: 8)
container.constrain(to: view).centerXY()
// Constrain a label to the layout margins guide of the container. This means we
// get padding for free! No need to define constants in these constraints.
// Label will become a child of the layout guide's owning view.
let label = UILabel()
label.constrain(to: container.layoutMarginsGuide).leadingTrailingTopBottom()
// Ensure the label doesn't get wider than our view within a constant.
// Since label is a child of container by this point, SwiftAutoLayout doesn't set its parent.
label.constrain(to: view).width(.lessThanOrEqual, constant: -60, priority: .defaultHigh)
// The view hierarchy is now:
// view
// └ container
// └ label
自定义约束
SwiftAutoLayout利用可选参数来在约束条件是.equal
关系、具有常数为0、乘数为1、优先级为.required
且在创建时应该激活约束时提供简洁的代码。要指定自定义值,请使用适当的方法并提供一个参数。
// Simple leading padding of 16 points
label.constrain(to: view).leading(constant: 16)
// Simple trailing padding of 16 points.
// NOTE: Constraints between trailing and bottom anchors have their items reversed
// so your constants can always be positive when insetting!
label.constrain(to: view).trailing(constant: 16)
// Get as customized as you like!
label.constrain(to: view).top(.greaterThanOrEqual, constant: 8, multiplier: 0.5, priority: .defaultLow)
// In common scenarios where multiple constraints are defined together,
// helper methods create multiple constraints using the supplied arguments
label.constrain(to: view).leadingTrailing(constant: 16).topBottom(constant: 8)
// As a bonus, this makes it super easy to pin a view to a container,
// become its child, and disable its resizing mask in a single line of code
label.constrain(to: view).leadingTrailingTopBottom()
// And as a bonus to that bonus, if you want the label to be constrained to
// the view's margins inset from the safe area, use its margins layout guide!
label.constrain(to: view.layoutMarginsGuide).leadingTrailingTopBottom()
定义不同类型的约束
SwiftAutoLayout提供了3个主要方法来创建适用于不同任务的不同类型的约束构建器。
-
constrain(to:)
返回一个RelationalConstraintBuilder
,这对于将视图嵌入另一个视图并创建匹配锚点的约束非常有用。在不常见的场景中,您想要在两个不同的锚点之间定义约束,请使用此构建器的xAxis(_:to:)
、yAxis(_:to:)
和dimension(_:to:)
方法。 -
constrain(after:)
返回一个DistributiveConstraintBuilder
,有几个方法可用于在该视图之后垂直或水平放置该视图。此构建器期望其视图和布局指南已经有了父视图。 -
constrainSelf()
返回一个SelfConstraintBuilder
,这非常适合约束视图的宽度、高度或长宽比。
获取约束
这三种构建器都提供了一个按创建顺序排列的约束数组。
// You can grab a reference to the builder itself...
let builder = label.constrain(to: view).leading().centerY()
print(builder.constraints.last!) // NSLayoutConstraint between centerY anchors
// ...or just access the array of constraints directly!
let constraint = label.constrain(to: view).centerY(constant: 0).constraints.last!
// Then use the constraint later as needed.
constraint.constant = 100
// Keep in mind some helper methods create multiple constraints in the order they're named.
// This should be clear based on method name, and their documentation will specify constraint count.
let constraints = label.constrain(to: view).leadingTrailingTopBottom().constraints
print(constraints.count) // 4
// You can put constraints on their own lines thanks to functional chanining. Here we
// dynamically activate a constraint later, as such its priority must be lower than `.required`
let constraint = label.constrainSelf()
.height(constant: 0, priority: .required - 1, activate: false)
.constraints.last!
constraint.isActive = true // smoosh!
系统间距
您可以在 iOS 11 和更高版本中,指定为它们的“常数”使用系统间距的约束。这是通过名为 .systemSpacing
的 CGFloat
扩展完成的——这是一个特别占位符值,SwiftAutoLayout 在创建您约束时会考虑到它。此值仅在 SwiftAutoLayout 中使用,并且不与 constrainSelf()
构造函数一起工作。
label.constrain(to: view).leadingTrailing(constant: .systemSpacing)
自定义约束
在您想在两个不同的锚点之间创建自定义约束的情况下,请在 constrain(to:)
之后使用相应的方法。对 NSLayoutAnchor<T>
中的 T
的专门方法的需要使创建这些自定义约束类型安全并且更不易崩溃。
// NSLayoutXAxisAnchor: Constrain label's centerXAnchor to view's leadingAnchor
label.constrain(to: view).xAxis(.centerX, to: .leading)
// NSLayoutYAxisAnchor: Constrain label's centerYAnchor to view's topAnchor
label.constrain(to: view).yAxis(.centerY, to: .top)
// NSLayoutDimension: Constrain label's widthAnchor to view's heightAnchor
label.constrain(to: view).dimension(.width, to: .height)
理念
关于左右锚点的说明
SwiftAutoLayout 不使用左右锚点。这简化了 x 轴锚点的使用,通过不允许错误使用(混淆左和 leading)并清理自动完成。
避免使用左右属性。请使用 Leading 和 Trailing。这样可以使布局适应视图的阅读方向。
默认情况下,阅读方向是基于用户设置的当前语言决定的。但是,在必要时您可以覆盖此设置。在 iOS 中,将约束视图(所有受约束视图最近共同的祖先视图)上的
semanticContentAttribute
属性设置为指定在从左到右和从右到左的语言之间切换时内容布局是否应该翻转。
技巧、窍门和陷阱
自定义父视图
如果您有一个特殊场景,希望在使用constrain(to:)
时视图的父视图未被设置,只需要事先设置其父视图即可。SwiftAutoLayout的目标是简化层次结构生成,并确保创建约束时视图有一个父视图,一旦层次结构存在,它将不会更改。
从底部开始工作
在定义视图层次结构时,最好首先定义和约束成为根视图子视图的第一个视图,并在之后约束子视图。通常,您想在使用constrain(after:)
之前使用constrain(to:)
,因为后者期望两个视图/布局指南都有父视图。constrainSelf()
可以在任何时候调用,视图不需要父视图即可进行自我约束。
一致地定义约束
尝试将所有约束代码与视图设置代码分离开来是很容易的,但建议在根视图有父视图的地方设置所有约束。对于视图控制器,在viewDidLoad()
或之后设置约束,并避免在UIView
或UIViewController
初始化器中定义约束。
调试约束问题
提醒一下,将视图的accessibilityIdentifier
设置为一个简洁的字符串将帮助您在打印约束错误时识别问题视图。
使用布局向导!
UILayoutGuide
非常强大。如果你正确设置了视图并使用它们的directionalLayoutMargins
,你可以使用最少的常量来编写优雅的约束。由于SwiftAutoLayout不使用左右锚点,因此在设置布局向导时建议使用NSDirectionalEdgeInsets
。
当你需要简化视图布局时,也可以创建新的布局向导而不是视图。
// Create a layout guide that will determine a height in which some buttons
// will be spread out along the x axis, and centered on the y axis
let buttonsLayoutGuide = UILayoutGuide()
buttonsLayoutGuide.constrain(to: view.layoutMarginsGuide).leadingTrailing().bottom()
buttonsLayoutGuide.constrainSelf().height(constant: 60)
let buttons = [UIButton(), UIButton(), UIButton()]
buttons.forEach { $0.constrainSelf().widthHeight(constant: 40) }
zip(buttons, [0.5, 1.0, 1.5]) { (button, multiplier)
button.constrain(to: buttonsLayoutGuide).centerX(multiplier: multiplier).centerY()
}
改进空间
目前,SwiftAutoLayout不支持AppKit,但欢迎提交拉取请求!
SwiftAutoLayout仅支持Both UIView
和 UILayoutGuide
都有的锚点,因此不支持firstBaselineAnchor
和 lastBaselineAnchor
(仅存在于 UIView
中)。再次欢迎提交流修复构的拉取请求!
安装
CocoaPods
将以下行添加到您的Podfile中
pod 'SwiftAutoLayout'
Carthage
将以下行添加到您的Cartfile中
github "SwiftKickMobile/SwiftAutoLayout"
手册
- 将SwiftAutoLayout仓库放置在项目目录中的某个地方。
- 在Xcode中,将
SwiftAutoLayout.xcodeproj
添加到您的项目中。 - 在您的应用程序目标上,在“通用”选项卡中添加SwiftAutoLayout框架
- 作为嵌入式二进制文件。
- 在“构建阶段”选项卡上作为目标依赖项。
关于SwiftKick Mobile
我们构建高质量的应用程序!如果您在项目中需要帮助,请联系我们。
许可
SwiftAutoLayout遵循MIT许可协议。有关详细信息,请参阅LICENSE。