锚点
目录
- 故事
- 特性
- 基本的With Anchor
- 推理
- 查找现有约束
- 动画
- 使用Group更新约束
- 使用ConstraintProducer可扩展
- 使用Builder快速构建
- 调试Auto Layout
- 支持多个屏幕尺寸
故事
我喜欢在代码中构建视图,所以Auto Layout是我的首选。语法在这么多年里得到了改进,但我总是想以最少的努力来完成它。更多的重复代码会让你倾向于复制粘贴,并产生更多的错误。
Auto Layout API历史
多年来引入了新的API,以便您知道如何设置部署目标
NSLayoutConstraint
自iOS 6、macOS 10.7起isActive
自iOS 8、macOS 10.10起NSLayoutAnchor
、UI|NSLayoutGuide
自iOS 9、macOS 10.11起
你需要另一个自动布局框架吗?
你看到的所有自动布局框架都只是方便构建NSLayoutConstraint
的方法,实际上这些都是你通常需要的。
- 调用
addSubview
使得视图进入层次结构。 - 设置
translatesAutoresizingMaskIntoConstraints = false
。 - 设置
isActive = true
以启用约束。
大多数情况下,你需要的是NSLayoutAnchor
。但是,如果你需要更多,锚点可以是你的选择。
示例
俄罗斯方块
你可以使用自动布局来构建俄罗斯方块
。自动布局与仿射变换
配合得很好。请参阅代码
activate(
lineBlock.anchor.left.bottom
)
// later
activate(
firstSquareBlock.anchor.left.equal.to(lineBlock.anchor.right),
firstSquareBlock.anchor.bottom
)
// later
activate(
secondSquareBlock.anchor.right.bottom
)
钢琴
这是使用apply
和fixed spacing
制作钢琴的方法。请参阅代码
activate(
c.anchor.left,
b.anchor.right,
c.anchor.top.bottom,
c.anchor.top.bottom.width.apply(to: [d, e, f, g, a, b]),
c.anchor.fixedSpacingHorizontally(togetherWith: [d, e, f, g, a, b], spacing: 0)
)
activate(
cd.anchor.top,
cd.anchor.size.equal.to(c.anchor.size).multiplier(2/3),
cd.anchor.top.size.apply(to: [de, fg, ga, ab]),
cd.anchor.centerX.equal.to(c.anchor.right),
de.anchor.centerX.equal.to(d.anchor.right),
fg.anchor.centerX.equal.to(f.anchor.right),
ga.anchor.centerX.equal.to(g.anchor.right),
ab.anchor.centerX.equal.to(a.anchor.right)
)
更多
更多示例可查阅示例
特性
- 流畅的构建器语法
- 基于协议的定制容易
- 支持iOS,macOS
- 支持
LayoutGuide
- 更新和重置约束
- 查找现有约束
- 调试约束
- 可视化约束
基本的锚点
访问锚点
优先使用组合而非扩展,这是访问 anchor
的方式。支持 View
、LayoutGuide
、LayoutSupport
let view = UIView()
view.anchor.left.right
let guide = UILayoutGuide()
guide.anchor.width.height
topLayoutGuide.anchor.bottom
bottomLayoutGuide.anchor.top
激活约束
使用接受可变参数的 activate
。这很重要,无论你如何创建约束,直到你 activate
它,它们都没有任何效果 ❗❗❗❗❗
activate(
a.anchor.top.left,
b.anchor.top.right,
c.anchor.bottom.left,
d.anchor.bottom.right
)
属性
支持你所能想到的所有属性
anchor.top.left.bottom.right
.leading.trailing
.topMargin.bottomMargin.leftMargin.rightMargin
.centerX.centerY
.centerXWithinMargins.centerXWithinMargins
.lastBaseline.firstBaseline
.width.height
关系
a.anchor.top.equal.to(b.bottom)
a.anchor.width.greaterThanOrEqual.to(b.anchor.height)
a.anchor.width.lessThanOrEqual.to(b.anchor)
配置
这是如何应用 constant
、multiplier
、priority
、identifier
a.anchor.top.equal.to(b.anchor.bottom)
.constant(10).multiplier(1.5).priority(999)
.id("verticalSpacingBetweenA-B")
参考
获取约束的引用以稍后修改。在ref
闭包中,我们可以访问所有创建的约束
var constraint: NSLayoutConstraint?
activate(
view.anchor.center.constant(10).ref({ constraint = $0.first })
)
方便的属性
使用组合多个内部属性的方便属性
a.anchor.center // centerX, centerY
a.anchor.size // width, height
a.anchor.edges // top, right, bottom, left
方便的方法
内边距
a.anchor.edges.insets(EdgeInsets(top: 1, left: 2, bottom: 3, right: 4)) // top+1, left+2, bottom+3, right+4
a.anchor.edges.insets(5) // top+5, left+5, bottom-5, right-5
填充
a.anchor.paddingHorizontally(20) // leading+20, trailing-20
b.anchor.paddingVertically(20) // top+20, bottom-20
尺寸
设置到另一个锚点
a.anchor.height.equal.to(b.anchor.width)
c.anchor.size.equal.to(d.anchor)
设置为一个常量
a.anchor.height.equal.to(20) // height==20
b.anchor.size.equal.to(20) // width==20, height==20
直接使用constant
是不行的,因为锚点会推断到superview
c.anchor.width.constant(20) // width==superview.width+20
比例
a.anchor.height.equal.to(a.anchor.width) // height==width
你可以直接使用ratio
a.anchor.width.constant(10)
a.anchor.height.ratio(2) // height==width*2
a.anchor.height.constant(10)
a.anchor.width.ratio(2) // width==height*2
推断
你知道你主要想做什么。锚点也是如此
superview
上
大多数情况下,你想要将约束限制在a.anchor.top.left // a.top == a.superview.top, a.left == a.superview.left
大多数情况下,你想要将约束置于相同的属性上
a.anchor.top.bottom.width.equal.to(b.anchor) // a.top == b.top, a.bottom == b.bottom, a.width == b.width
查找现有约束
您无需声明变量来存储constraints
,您可以直接获取它们
a.anchor.find(.height)?.constant = 100
// later
b.anchor.find(.height)?.constant = 100
// later
c.anchor.find(.height)?.constant = 100
动画
动画很简单。您只需更改您的constraint
的isActive
或constant
属性,然后在动画块中调用layoutIfNeeded
。您可以使用UIView.animate
或UIViewPropertyAnimator
// Change constraint
a.anchor.find(.height)?.constant = 100
loginButtonHeightConstraint.isActive = false
let animator = UIViewPropertyAnimator(duration: 1, dampingRatio: 0.7)
animator.addAnimations { [weak self] in
self?.view.layoutIfNeeded()
}
animator.startAnimation(afterDelay: 1)
使用Group更新约束
activate
只是创建group
的一种方便方式,然后设置Group
的isActive
属性。如果您只想对一组约束进行分组,稍后设置isActive
,则使用group
函数
在此示例中,我们有4个分组,然后轮流切换哪个分组被激活
func toggle(_ group: Group) {
[g1, g2, g3, g4].forEach { g in
guard let g = g else {
return
}
g.isActive = (g == group)
}
}
g1 = group(a.anchor.top.left)
g2 = group(a.anchor.top.right)
g3 = group(a.anchor.bottom.right)
g4 = group(a.anchor.bottom.left)
g1.isActive = true
animator = Animator(view: self, animations: [
{
self.toggle(self.g2)
},
{
self.toggle(self.g3)
},
{
self.toggle(self.g4)
},
{
self.toggle(self.g1)
}
])
animator.start()
支持富约束生成器
Group
是由 ConstraintProducer
生成的 NSLayoutConstraint
集合
public protocol ConstraintProducer {
func constraints() -> [NSLayoutConstraint]
}
目前,有 Anchor
和 Builder
符合 ConstraintProducer
,您可以通过符合 ConstraintProducer
来轻松扩展 锚点。例如
// This accepts a list of views, and build constraints
class MyAwesomeLayout: ConstraintProducer {
init(views: [UIView]) {
// Your code goes here
}
func constraints() -> [NSLayoutConstraint] {
// Your code goes here
return []
}
}
let awesomeLayout = MyAwesomeLayout(views: [view1, view2])
activate(awesomeLayout)
用 Builder 快速构建
锚点用于在 2 个视图之间创建 constraints
。如果您想要一次为多个视图创建 constraints
,可以使用多个锚点。有一些常见的任务,让 Builder
来帮您。以下方法在内部使用了 Builder
锚点提供了一套构建器来帮助您避免重复性任务,并快速构建 UI
应用
将相同的锚点应用到其他视图
a.anchor.left.height.apply(to: [b, c]),
分页
水平构建分页 scrollView
addSubview(scrollView)
[a, b, c, d].forEach {
scrollView.addSubview($0)
}
activate(
scrollView.anchor.edges.insets(8),
a.anchor.pagingHorizontally(togetherWith: [b, c, d], in: scrollView)
)
固定间距
添加固定间距。视图将调整大小
activate(
container.anchor.edges.insets(8),
a.anchor.left.top.bottom,
c.anchor.right,
a.anchor.top.bottom.width.apply(to: [b, c]),
a.anchor.fixedSpacingHorizontally(togetherWith: [b, c], spacing: 50)
)
动态间距
使用 LayoutGuide 添加动态间距。间距将调整大小
activate(
container.anchor.edges.insets(8),
a.anchor.size.equal.to(30),
b.anchor.size.equal.to(30),
c.anchor.size.equal.to(30),
a.anchor.left.centerY,
a.anchor.centerY.apply(to: [b, c]),
c.anchor.right,
a.anchor.dynamicSpacingHorizontally(togetherWith: [b, c])
)
调试自动布局
- 阅读 调试自动布局
支持多个屏幕尺寸
- 使用
Group
声明针对不同屏幕尺寸/尺寸类的多组约束 - 使用比例,阅读 不同屏幕尺寸下的自动布局
安装
Anchors 通过 CocoaPods 提供。要安装它,只需将以下行添加到您的 Podfile 中
pod 'Anchors'
Anchors 也可通过 Carthage 提供。安装只需在您的 Cartfile 中写入
github "onmyway133/Anchors"
Anchors 还可手动安装。只需要下载并将 Sources
文件夹拖放到您项目中。
作者
Khoa Pham,[email protected]
贡献
我们很乐意您为 Anchors 贡献,详细信息请查看 CONTRIBUTING 文件。
许可证
Anchors 在 MIT 许可证下可用。更多信息请参阅 LICENSE 文件。