Snap是一个轻量级的布局框架,它用更简洁的语法包装AutoLayout。Snap拥有自己的布局DSL(领域特定语言),提供了一种连续描述NSLayoutConstraints的方法,从而使得布局代码更加简洁和易于阅读。Snap支持iOS和OS X。
Snap使用了Swift独有的特性,如函数重载,因此无法从Objective-C中使用。正因为如此,我们选择了将Masonry的
mas_
前缀互换为snp_
,这样您可以在同一个项目中同时使用Masonry和Snap。
嵌入式框架需要至少iOS 8或OS X Mavericks的部署目标。
如果您不希望使用上述任一依赖管理工具,可以使用以下命令将Snap手动集成到项目中。
$ git submodule add https://github.com/Masonry/Snap.git
Snap
文件夹,并将 Snap.xcodeproj
拖到应用程序项目的文件导航器中。Snap.framework
.+
按钮,并选择“New Copy Files Phase”。将此新阶段重命名为“Copy Frameworks”,将“Destination”设置为“Frameworks”,并添加 Snap.framework
.Auto Layout是组织和管理视图的强大而灵活的方式,但在代码中创建约束既冗长又不清晰。想象一个简单的例子,您希望有一个视图填满其父视图,但在每边缩进10像素:
let superview = self;
let view1 = UIView()
view1.setTranslatesAutoresizingMaskIntoConstraints(false)
view1.backgroundColor = UIColor.greenColor()
superview.addSubview(view1)
let padding = UIEdgeInsetsMake(10, 10, 10, 10)
superview.addConstraints([
NSLayoutConstraint(
item: view1,
attribute: NSLayoutAttribute.Top,
relatedBy: NSLayoutRelation.Equal,
toItem: superview,
attribute: NSLayoutAttribute.Top,
multiplier: 1.0,
constant: padding.top
),
NSLayoutConstraint(
item: view1,
attribute: NSLayoutAttribute.Left,
relatedBy: NSLayoutRelation.Equal,
toItem: superview,
attribute: NSLayoutAttribute.Left,
multiplier: 1.0,
constant: padding.left
),
NSLayoutConstraint(
item: view1,
attribute: NSLayoutAttribute.Bottom,
relatedBy: NSLayoutRelation.Equal,
toItem: superview,
attribute: NSLayoutAttribute.Bottom,
multiplier: 1.0,
constant: -padding.bottom
),
NSLayoutConstraint(
item: view1,
attribute: NSLayoutAttribute.Right,
relatedBy: NSLayoutRelation.Equal,
toItem: superview,
attribute: NSLayoutAttribute.Right,
multiplier: 1.0,
constant: -padding.right
)
])
即使在如此简单的示例中,所需的代码也已经相当冗长,当你有超过 2 或 3 个视图时,代码很快就会变得难以阅读。另一个选择是使用视觉格式语言(VFL),它比前者稍微简短一些。然而,ASCII 类型的语法也有其自身的问题,并且由于 NSLayoutConstraint.constraintsWithVisualFormat
返回一个数组,这使得它稍微难以动画。
下面是使用 ConstraintMaker 创建的相同约束
let padding = UIEdgeInsetsMake(10, 10, 10, 10)
view1.snp_makeConstraints { make in
make.top.equalTo(superview.snp_top).with.offset(padding.top) // with is an optional semantic filler
make.left.equalTo(superview.snp_left).with.offset(padding.left)
make.bottom.equalTo(superview.snp_bottom).with.offset(-padding.bottom)
make.right.equalTo(superview.snp_right).with.offset(-padding.right)
}
或者更加简洁
view1.snp_makeConstraints { make in
make.edges.equalTo(superview).with.insets(padding)
return // this return is a fix for implicit returns in Swift and is only required for single line constraints
}
请注意,在第一个示例中,我们必须将约束添加到父视图 superview.addConstraints
。然而,Snap 会自动将约束添加到相应的视图。
Snap 还会为你调用 view1.setTranslatesAutoresizingMaskIntoConstraints(false)
。
.equalTo
等同于 NSLayoutRelation.Equal
.lessThanOrEqualTo
等同于 NSLayoutRelation.LessThanOrEqual
.greaterThanOrEqualTo
等同于 NSLayoutRelation.GreaterThanOrEqual
这三个相等约束接受一个参数,可以是以下任何一种
make.centerX.lessThanOrEqualTo(view2.snp_left)
视图属性 | NSLayoutAttribute |
---|---|
view.snp_left | NSLayoutAttribute.Left |
view.snp_right | NSLayoutAttribute.Right |
view.snp_top | NSLayoutAttribute.Top |
view.snp_bottom | NSLayoutAttribute.Bottom |
view.snp_leading | NSLayoutAttribute.Leading |
view.snp_trailing | NSLayoutAttribute.Trailing |
view.snp_width | NSLayoutAttribute.Width |
view.snp_height | NSLayoutAttribute.Height |
view.snp_centerX | NSLayoutAttribute.CenterX |
view.snp_centerY | NSLayoutAttribute.CenterY |
view.snp_baseline | NSLayoutAttribute.Baseline |
如果你想使视图的 left 大于或等于标签的 left
// these two constraints are exactly the same
make.left.greaterThanOrEqualTo(label)
make.left.greaterThanOrEqualTo(label.snp_left)
自动布局允许将宽度和高度设置为常量值。如果你想要设置视图的宽度和高度具有最小值和最大值,你可以在相等块中传递一个原始值
// width >= 200 && width <= 400
make.width.greaterThanOrEqualTo(200)
make.width.lessThanOrEqualTo(400)
然而,自动布局不允许将左、右、中心等对齐属性设置为常量值。因此,如果你为这些属性传递了一个原始值,Snap 会将这些属性转换为相对父视图的约束,即
// creates view.left <= view.superview.left + 10
make.left.lessThanOrEqualTo(10)
你还可以使用其他原始值和结构体来构建你的约束,如下所示
make.top.equalTo(42)
make.height.equalTo(20)
make.size.equalTo(CGSizeMake(50, 100))
make.edges.equalTo(UIEdgeInsetsMake(10, 0, 10, 0))
make.left.equalTo(view).offset(UIEdgeInsetsMake(10, 0, 10, 0))
.prority
允许你指定精确的优先级
.priorityHigh
等同于 UILayoutPriority.DefaultHigh
.priorityMedium
处于高新与低之间
.priorityLow
等同于 UILayoutPriority.DefaultLow
优先级可以添加到约束链的末尾,如下所示
make.left.greaterThanOrEqualTo(label.snp_left).with.priorityLow();
make.top.equalTo(label.snp_top).with.priority(600);
Snap 还提供了一些便利的方法,可以在一次操作中创建多个约束。
// make top, left, bottom, right equal view2
make.edges.equalTo(view2);
// make top = superview.top + 5, left = superview.left + 10,
// bottom = superview.bottom - 15, right = superview.right - 20
make.edges.equalTo(superview).insets(UIEdgeInsetsMake(5, 10, 15, 20))
// make width and height greater than or equal to titleLabel
make.size.greaterThanOrEqualTo(titleLabel)
// make width = superview.width + 100, height = superview.height - 50
make.size.equalTo(superview).offset(CGSizeMake(100, -50))
// make centerX and centerY = button1
make.center.equalTo(button1)
// make centerX = superview.centerX - 5, centerY = superview.centerY + 10
make.center.equalTo(superview).offset(CGPointMake(-5, 10))
你可以链式调用视图属性以增强可读性
// All edges but the top should equal those of the superview
make.left.right.and.bottom.equalTo(superview)
make.top.equalTo(otherView)
有时你需要修改现有的约束以便进行动画或移除/替换约束。在 Snap 中,更新约束有几种不同的方法。
您可以通过将约束构建表达式的结果分配给局部变量或类属性来保留特定约束的引用。您还可以通过将它们存储在数组中来引用多个约束。
var topConstraint: Constraint? = nil
...
// when making constraints
view1.snp_makeConstraints { make in
self.topConstraint = make.top.equalTo(superview).with.offset(padding.top)
make.left.equalTo(superview).with.offset(padding.left)
}
...
// then later you can call
self.topConstraint.uninstall()
snp_remakeConstraints
与 snp_addConstraints
类似,但首先会删除 Snap 安装的所有现有约束。
func changeButtonPosition() {
self.button.snp_remakeConstraints { make in
make.size.equalTo(self.buttonSize)
if topLeft {
make.top.left.equalTo(10)
} else {
make.bottom.equalTo(self.view).offset(-10)
make.right.equalTo(self.view).offset(-10)
}
}
}
将包含的代码片段复制到 ~/Library/Developer/Xcode/UserData/CodeSnippets
,以您的 snap 关闭编写的闪电速度!
snp_make
-> <view>.snp_addConstraints { make in <code> }
snp_remake
-> <view>.snp_remakeConstraints { make in <code> }