EasyAnchor
目录
- 故事
- 特性
- 与锚点的基本使用
- 推理
- 找到现有的约束
- 动画
- 使用 Group 更新约束
- 可扩展的 ConstraintProducer
- 使用 Builder 快速构建
- 调试 Auto Layout
- 支持多屏尺寸
故事
我喜欢在代码中构建视图,所以 Auto Layout 是我的首选。语法多年来已经有了改进,但我总是希望用最少的努力来完成它。更多的重复代码会让你倾向于复制粘贴,从而产生更多的错误。
Auto Layout API 历史
多年来如何引入新的 API,以便您知道设置您的部署目标
NSLayoutConstraint
自 iOS 6,macOS 10.7isActive
自 iOS 8,macOS 10.10NSLayoutAnchor
,UI|NSLayoutGuide
自 iOS 9,macOS 10.11
你需要另一个Auto Layout框架吗?
你所看到的所有Auto Layout框架,其实只是构建NSLayoutConstraint
的一种方便方法,实际上这些都是你通常需要的东西。
- 调用
addSubview
使得视图进入层级结构。 - 设置
translatesAutoresizingMaskIntoConstraints = false
。 - 将
isActive = true
设置为启用约束。
大多数时候,你需要的是NSLayoutAnchor
。但如果需要更多,EasyAnchor可以成为你的选择。
示例
俄罗斯方块
当然,你可以使用Auto Layout来制作俄罗斯方块
。Auto Layout与仿射变换
相配合也很好。查看代码。
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
和固定间距
制作钢琴的方法。查看代码。
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
,因为EasyAnchor会推断到代理视图。
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
推断
您知道您基本上想要做什么。EasyAnchor也知道。
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个group,然后轮流切换哪个group被激活
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()
可扩展与ConstraintProducer
Group
是一组由ConstraintProducer
生成的
public protocol ConstraintProducer {
func constraints() -> [NSLayoutConstraint]
}
目前,有Anchor
和Builder
符合ConstraintProducer
的要求,你可以通过满足ConstraintProducer
来轻松扩展EasyAnchor。例如
// 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快速构建
好的,Anchor
是用来在两个视图之间创建约束。如果你想要一次为多个视图创建约束,你可以使用多个Anchor
。有一些经常要做的工作,让Builder
来帮助你。下面的方法在内部使用了Builder
EasyAnchor提供了一系列的Builder来帮助您避免重复性任务,并快速构建用户界面
应用
将相同的锚应用到其他视图
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
声明多个不同屏幕尺寸/尺寸类别的约束集合 - 使用比例,阅读 不同屏幕尺寸下的自动布局
安装
EasyAnchor 可通过 CocoaPods 文件安装。要安装,只需将以下行添加到 Podfile 中
pod 'EasyAnchor'
EasyAnchor 同样可通过 Carthage 安装。只需在 Cartfile 中写入
github "onmyway133/EasyAnchor"
EasyAnchor 也可以手动安装。只需下载并将 Sources
文件夹拖到您的项目文件中。
作者
Pham Khao, [email protected]
贡献
我们非常欢迎您为 EasyAnchor 贡献,有关更多信息,请查看 贡献指南 文件。
许可证
EasyAnchor 在 MIT 许可下可用。有关更多信息,请参阅 许可证文件。