AnchorKit 使用锚点创建布局提供了一种简单直观的方法。
快速开始
以下是示例代码
// Multiple constraints on one line
myView.constrain(.leading, .top, .trailing, to: anotherView)
// One-line edge constraints with insets
myView.constrainEdges(to: anotherView).inset(10)
myView.constrainEdges(to: anotherView).insetVertical(20).insetHorizontal(30)
// Set height/width equal to a constant
myView.constrain(.height, .width, toConstant: 200)
myView.constrainWidth(to: 42)
// Set the height/width equal to a CGSize
myView.constrain(to: CGSize(width: 100, height: 200))
// Set offset (constant)
myView.constrain(.leading, to: .trailing, of: anotherView).offset(20)
// Set insets
myView.constrain(.leading, .trailing, to: anotherView).inset(24)
// Set the relation and multiplier
myView.constrain(.height, relation: .lessThanOrEqual, to: anotherView, multiplier: 1.6)
// Set the priority
myView.constrain(.centerY, to: anotherView, priority: .high).offset(-15)
// Easily center items
myView.constrainCenter(to: anotherView)
// Return value for single constraint is NSLayoutConstraint
let bottomConstraint = myView.constrain(.bottom, to: .top, of: anotherView.layoutMarginsGuide)
// Return value for multiple constraints is [NSLayoutConstraint]
let topAndSideConstraints = myView.constrain(.leading, .trailing, .top, to: anotherView)
特性
- 简单、直观、Swifty 语法
- 不再需要每行都写
isActive = true
(约束默认激活返回值)🎉 ) - 自动处理
translatesAutoresizingMaskIntoConstraints = false
- 与 布局指南和视图 一起工作
- 在所有支持 AutoLayout 约束的 3 个平台(iOS、macOS、tvOS)上工作
- 不需要处理专有的类;返回值是
NSLayoutConstraint
或[NSLayoutConstraint]
- 使用任何数字类型(
Int
、Double
、Float
等),无需转换为CGFloat
需求
- iOS 9.0+, macOS 10.12+, tvOS 9.0+
- Swift 3.1+
- Xcode 8+
安装
CocoaPods
pod 'AnchorKit'
Carthage
github "Weebly/AnchorKit"
Swift Package Manager
package(url: "https://github.com/Weebly/AnchorKit.git", .from("2.3.0"))
使用
Anchor
枚举
AnchorKit 的 API 主要基于表示视图和布局指南上不同锚定类型的枚举。
public enum Anchor {
case leading
case trailing
case left
case right
case top
case bottom
case width
case height
case centerX
case centerY
// These two are only available on views, not layout guides.
case firstBaseline
case lastBaseline
}
自动布局定义了 3 种类型的锚,并且您只能将锚定关系限制在同一个组内的其它锚上。
- 横轴锚点:
leading
、trailing
、left
、right
、centerX
- Y轴锚点:
top
,bottom
,centerY
,firstBaseline
,lastBaseline
- 尺寸锚点:
width
,height
在内部,这些锚点都映射到您正在约束的视图或布局指南上的实际NSLayoutAnchor
。
创建约束
Anchorable
协议
AnchorKit 同时支持布局指南和视图。为了统一这两个,我们使用一个名为 Anchorable
的协议。视图遵守另一个协议,ViewAnchorable
,这使得它们可以使用基线锚点。以下文档中的“项”一词指的是视图和布局指南。
锚点至项目约束
将一个项目的锚点约束到另一个项目的对应锚点上。
myView.constrain(.leading, .trailing, .top, to: anotherView)
使用单个锚点调用此方法将隐式返回 NSLayoutConstraint
。否则,返回类型是 [NSLayoutConstraint]
。以下显示了这些方法的完整签名。
// Single constraint
@discardableResult
public func constrain<AnchorableType: Anchorable>(_ anchor: Anchor, relation: NSLayoutRelation = .equal, to item: AnchorableType, multiplier: CGFloatRepresentable = 1, priority: LayoutPriority = .required) -> NSLayoutConstraint
// Multiple constraints
@discardableResult
public func constrain<AnchorableType: Anchorable>(_ anchors: Anchor..., relation: NSLayoutRelation = .equal, to item: AnchorableType, multiplier: CGFloatRepresentable = 1, priority: LayoutPriority = .required) -> [NSLayoutConstraint]
锚点至锚点约束
将一个项目的锚点约束到另一个项目的锚点上。
myView.constrain(.top, to: .bottom, of: anotherView.readableContentGuide)
此方法只支持创建单个约束。
@discardableResult
public func constrain<AnchorableType: Anchorable>(_ anchor: Anchor, relation: NSLayoutRelation = .equal, to otherAnchor: Anchor, of item: AnchorableType, multiplier: CGFloatRepresentable = 1, priority: LayoutPriority = .required) -> NSLayoutConstraint
锚点至常量约束
将项的锚点约束到常量。这对于 width
和 height
锚点特别有用(且更易读)。
myView.constrain(.height, toConstant: 200)
myBoxView.constrain(.width, .height, toConstant: 50) // Creates a box
// Even easier:
myView.constrainHeight(to: 42)
myView.constrainWidth(to: 60)
使用单个锚点调用此方法将隐式返回 NSLayoutConstraint
。否则,返回类型是 [NSLayoutConstraint]
。以下显示了这些方法的完整签名。
// Single constraint
@discardableResult
public func constrain(_ anchor: Anchor, relation: NSLayoutRelation = .equal, toConstant constant: CGFloatRepresentable, priority: LayoutPriority = .required) -> NSLayoutConstraint
// Multiple constraints
@discardableResult
public func constrain(_ anchors: Anchor..., relation: NSLayoutRelation = .equal, toConstant constant: CGFloatRepresentable, priority: LayoutPriority = .required) -> [NSLayoutConstraint]
此方法也可以应用于具有除 width
和 height
之外锚点的项目。生成的行为与将锚点约束到视图的 superview
(或布局指南的 owningView
)上的对应锚点等效。因此,myView.constrain(.leading, toConstant: 10)
等同于 myView.constrain(.leading, to: myView.superview!).offset(10)
。
约束到边缘
将当前项目的边缘约束到另一个项目。
myView.constrainEdges(to: anotherView)
这只是一个用来约束 leading
、trailing
、top
和 bottom
锚点的便利方法。如果您想添加一个不同于 .equal
的 relation
到约束,有一个相当于的独立方法。
@discardableResult
public func constrainEdges<AnchorableType: Anchorable>(to item: AnchorableType, priority: LayoutPriority = .required) -> [NSLayoutConstraint]
// With a relation other than .equal
@discardableResult
public func constrainEdges<AnchorableType: Anchorable>(_ relation: NSLayoutRelation, to item: AnchorableType, priority: LayoutPriority = .required) -> [NSLayoutConstraint]
约束到中心
将当前项目的中心约束到另一个项目。
myView.constrainCenter(to: anotherView)
这是一个用来约束 centerX
和 centerY
锚点的便利方法。如果您想添加一个不同于 .equal
的 relation
到约束,有一个相当于的独立方法。
@discardableResult
public func constrainCenter<AnchorableType: Anchorable>(to item: AnchorableType, priority: LayoutPriority = .required) -> [NSLayoutConstraint]
// With a relation other than .equal
@discardableResult
public func constrainCenter<AnchorableType: Anchorable>(_ relation: NSLayoutRelation, to item: AnchorableType, priority: LayoutPriority = .required) -> [NSLayoutConstraint]
约束到大小
将项目的宽度约束到特定 CGSize
。
myView.constrain(to: CGSize(width: 10, height: 20))
这是一个用来设置 width
和 height
锚点到相应的 CGSize
宽度和高度的便利方法。如果您想添加一个不同于 .equal
的 relation
到约束,有一个相当于的独立方法。
@discardableResult
public func constrain(to size: CGSize, priority: LayoutPriority = .required) -> [NSLayoutConstraint]
@discardableResult
public func constrain(_ relation: NSLayoutRelation, to size: CGSize, priority: LayoutPriority = .required) -> [NSLayoutConstraint]
对于视图,由于宽度和高度约束属于视图本身,您可以直接从视图更改这些约束。
myView.updateSize(newSize)
myView2.updateWidth(100)
myView3.updateHeight(200)
topLayoutGuide
和 bottomLayoutGuide
约束到 UIViewController 的 在 UIViewController
上的 topLayoutGuide
和 bottomLayoutGuide
属性的类型是 UILayoutSupport
,这是一个协议。就像约束到任何其他项目一样,将这些约束到它们上面。
请注意,这些上仅有的可用锚点是 height
、top
和 bottom
。
myView.constrain(.top, to: .bottom, of: topLayoutGuide).offset(10)
otherView.constrain(.height, to: bottomLayoutGuide)
这些方法的签名与锚到锚和锚到项目的签名相同,只是第二个条目具有类型 UILayoutSupport
。
CGFloatRepresentable
协议
本协议允许您为约束偏移量、内边距和乘数使用任何数字类型,而无需将所有值转换为 CGFloat
。
public protocol CGFloatRepresentable {
var cgFloatValue: CGFloat { get }
}
以下类型采用此协议
Int
、Int8
、Int16
、Int32
、Int64
UInt
、UInt8
、UInt16
、UInt32
、UInt64
Double
Float
、Float80
CGFloat
NSNumber
偏移量和内边距
要设置约束上的常量,AnchorKit 使用 偏移量 和 内边距。偏移量的行为与 NSLayoutConstraint
上的 constant
相当。但是,内边距却会对 trailing
、right
、bottom
和 lastBaseline
锚的常量求反,并对所有其他锚按正常方式行为。
// The top of bottomView will be 20 points below the top of topView
bottomView.constrain(.top, of: topView).offset(20)
// The innerView will be "inside" the outerView, with 10 points of padding on the sides
innerView.constrain(.leading, .trailing, to: outerView).inset(10)
如您在第二个示例中注意到的,offset(_:)
和 inset(_:)
既可以用于单个约束,也可以用于一系列约束。
在更新约束时,为了遵循 Swift 命名约定,AnchorKit 还提供了 updateOffset(_:)
和 updateInset(_:)
方法。
您还可以使用 UIEdgeInsets
(或者在 macOS 上的 EdgeInsets
)来设置内边距。内边距将应用于相应的约束。
innerView.constrainEdges(to: outerView).inset(UIEdgeInsets(top: 10, left: 12, bottom: 14, right: 16))
对于多个约束,还可以只设置水平/垂直内边距,如下所示
// The innerView will have an inset of 10 on the top and bottom sides and an inset of 20 on the leading and trailing sides
innerView.constrainEdges(to: outerView).insetVertical(10).insetHorizontal(20)`
当更新水平/垂直内边距时,使用 updateHorizontalInsets(_:)
和 updateVerticalInsets(_:)
。
布局优先级
要设置约束的优先级,AnchorKit 提供了一个名为 LayoutPriority
的枚举。
public enum LayoutPriority: RawRepresentable {
case low // Priority: 250
case medium // Priority: 500
case high // Priority: 750
case required // Priority: 1000
case custom(Float)
}
所有约束创建方法都有一个默认优先级 required
,但在需要时可以设置自己的优先级。
myView.constrain(.centerX, to: anotherView, priority: .medium)
对于复杂的布局,有时您可能只想为某个约束设置 稍微高一点 的优先级。好消息是您可以使用加法和减法运算符与布局优先级一起使用。
myView.constrain(.left, to: anotherView, priority: .low + 1) // Priority = 251
您可以使用 layoutPriority
扩展方法获取/设置 NSLayoutConstraint
的布局优先级。
let topConstraint = myView.constrain(.top, to: anotherView, priority: .medium)
topConstraint.layoutPriority = .high
最后,使用布局优先级设置您的内容压缩抵抗和内容抓紧。
myView.hug(with: .low, for: .vertical)
myView.resistCompression(with: .high, for: .horizontal)
其他好东西
激活 & 停用
AnchorKit 限制条件默认处于激活状态。然而,您也可以停用限制条件并在同一行捕获它们
let centerYConstraint = myView.constraint(.centerY, to: anotherView).deactivate()
// ...
centerYConstraint.activate()
这些方法同样适用于一系列限制条件。
处理iOS 11系统间隔约束
在iOS 11和tvOS 11中,UIKit通过使用系统定义间距的方法,如constraintEqualToSystemSpacingAfter(_:multiplier:)
,提供了一种新的方式来定义限制条件。当对新的安全区域布局指南进行限制时,这些方法非常有用,这是在UIView上找到的,这是现在推荐的API,而不是在UIViewController上使用topLayoutGuide
和bottomLayoutGuide
(现在已被废弃)。
AnchorKit提供了系统间距限制的高级别方法
myView.constrainUsingSystemSpacing(.top, .below, .bottom, of: anotherView.safeAreaLayoutGuide)
myView.constrainUsingSystemSpacing(.leading, .after, .trailing, of: anotherView.safeAreaLayoutGuide)
此方法的全局签名如下
@discardableResult
public func constrainUsingSystemSpacing<AnchorableType: Anchorable>(_ anchor: Anchor, relation: Relation = .equal, _ position: SystemSpacingPosition, _ otherAnchor: Anchor, of item: AnchorableType, multiplier: CGFloatRepresentable = 1, priority: LayoutPriority = .required) -> NSLayoutConstraint
支持
有关问题、支持和建议,请创建一个issue。
许可证
AnchorKit 采用MIT许可证。请参阅LICENSE文件以获取更多信息。