自动布局(AutoLayout)之 NSLayoutAnchor 方式使用
AutoLayout
自动布局(AutoLayout)是iOS6引入的关系布局,实现动态位置和多视图关系的布局方式,是对frame布局和AutoresizingMask的不足进行补充的一种方式,现在已经成为主流的布局方案,由于原始创建方式比较复杂,可以使用优秀的第三方框架方便创建约束(Swift: SnapKit, Objective-C: Masonary)。
自动布局创建约束方式:
- 基于frame属性自动转化AutoLayout的约束,需要
translatesAutoresizingMaskIntoConstraints
为true. - 通过NSLayouConstraint创建,比较麻烦且代码量多。
- 通过VFL语法创建约束组添加,不适合复杂布局,且理解有一定难度。
- NSLayoutAnchor, iOS9.0后添加的布局特性,是原生代码布局中最方便的。
- 直接在xib或storyboard中添加,最方便简单的实现自动布局方式。
自动布局基本原则:
- 创建约束尽量参考依赖父视图。
- 约束意义明确完整,尽量避免约束冲突
- 代码添加约束,一定要将View的
translatesAutoresizingMaskIntoConstraints
属性设置为false,否则约束不起效果。 - 先添加到父视图,再添加约束,否则会崩溃。
NSLayoutAnchor
NSLayoutAnchor是对AutoLayout创建约束的补充,核心还是NSLayoutConstraint,可以避免过长创建约束代码。NSLayoutAnchor可以理解为约束描边,通过视图之间的边关系和X、Y轴关系,以及定义自身宽高来创建约束。
锚(Anchor)关系
苹果公司为UIView添加如下属性Anchor来作为约束参考
四边关系
Anchor(锚边) | 描述 |
---|---|
leadingAnchor | 前边锚(与trailingAnchor成对使用) |
trailingAnchor | 后边锚 |
leftAnchor | 左边锚(与rightAnchor成对使用) |
rightAchor | 右边锚 |
topAnchor | 上边锚 |
bottomAnchor | 下边锚 |
大小关系
Anchor(锚) | 描述 |
---|---|
widthAnchor | 宽度约束 |
heightAnchor | 高度约束 |
中心点关系
Anchor(锚点) | 描述 |
---|---|
centerXAnchor | X轴对齐关系 |
centerYAnchor | Y轴对齐关系 |
基准线
Anchor(锚) | 描述 |
---|---|
firstBaseLineAnchor | 文本首行基准线 |
lastBaeLineAnchor | 文本最后一行基准线 |
基本使用
实现灰色View和橙色View对齐,且大小相同。
1.初始化视图,并添加到父视图
private var grayView = UIView()
private var orangeView = UIView()
grayView.backgroundColor = .gray
orangeView.backgroundColor = .orange
// 1. 先添加到父视图
view.addSubview(grayView)
view.addSubview(orangeView)
2.设置取消自动转化frame为约束
// 2.设置取消自动转化frame为约束
grayView.translatesAutoresizingMaskIntoConstraints = false
orangeView.translatesAutoresizingMaskIntoConstraints = false
3.通过锚关系添加约束
// 3. 添加约束
grayView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 15).isActive = true
grayView.topAnchor.constraint(equalTo: view.topAnchor, constant: 80).isActive = true
grayView.heightAnchor.constraint(equalToConstant: 40).isActive = true
grayView.widthAnchor.constraint(equalToConstant: 40).isActive = true
grayView.rightAnchor.constraint(equalTo: orangeView.leftAnchor, constant: -15).isActive = true // 注意这里的值为负值[是相对于orangeView坐标系来确定值]
orangeView.centerYAnchor.constraint(equalTo: grayView.centerYAnchor).isActive = true
orangeView.widthAnchor.constraint(equalToConstant: 40).isActive = true
orangeView.heightAnchor.constraint(equalToConstant: 40).isActive = true
与创建NSLayoutConstraint一样,也可以设置约束优先级,大于等于,小于等于等关系。
关系值确定
不知道你对grayView.rightAnchor.constraint(equalTo: orangeView.leftAnchor, constant: -15).isActive = true
这个约束有没有疑问?为啥 值是负的,如果是使用xib来添加约束,他们两个的关系应该是15。
那是因为这个约束值是以orangeView的left锚为原点坐标系来确定的,灰色right边在次坐标系中的数值就是负值。参考以下示例图
同理特别容易出错的还有需求,如果现在需要灰色view与橙色view的距离大于等于15.
90%的人会写如下错误代码:
grayView.rightAnchor.constraint(greaterThanOrEqualTo: orangeView.leftAnchor, constant: 15).isActive = true
// 语义上描述就是: 灰色view的right边大于等于橙色left边15距离
事实真是这样吗?你看
他们反而重合了,那你说把值换为负值总可以了吧!
grayView.rightAnchor.constraint(greaterThanOrEqualTo: orangeView.leftAnchor, constant: -15).isActive = true
然而并不是?你看下面这张图
由于是参考橙色left边的为原点的坐标系,所以是负值,又因为距离越大负得越多,值越小(-15 > -65)所以就需要使用小于等于,是不是和xib有点相反。
正确约束应该为:
grayView.rightAnchor.constraint(lessThanOrEqualTo: orangeView.leftAnchor, constant: -15).isActive = true
Tips: 在创建leftAnchor或者TopAnchor,一般为正值和xib一致,但是rightAnchor和BottomAnchor就要注意参考哪个坐标系原点来取值,来决定正负值和大于等、小于等于关系选择
参考坐标系的确定是以参数中视图锚边为原点坐标系(注意:此坐标系,不同于view的坐标系,这是一个以边为原点的虚拟坐标系)
UILayoutGuide
UILayoutGuide用于辅助添加约束,它的作用就像一个透明的View,具备约束参考,但是不会渲染。
假如我们想让三个view水平对齐,且中间间距相等。我们就可以使用UILayoutGuide辅助实现。
-
创建view即layoutGuide
private var grayView = UIView() private var orangeView = UIView() private var redView = UIView() private var layouGuide1 = UILayoutGuide() private var layouGuide2 = UILayoutGuide()
-
添加view到父视图
// 1. 先添加到父视图 view.addSubview(grayView) view.addSubview(orangeView) view.addSubview(redView) // layoutGuide也需要添加进来 view.addLayoutGuide(layouGuide1) view.addLayoutGuide(layouGuide2)
-
通过锚关系添加约束
// 2.设置取消自动转化frame为约束 grayView.translatesAutoresizingMaskIntoConstraints = false orangeView.translatesAutoresizingMaskIntoConstraints = false redView.translatesAutoresizingMaskIntoConstraints = false // 3. 添加约束 // 添加layoutGuide的约束 layouGuide1.widthAnchor.constraint(equalTo: layouGuide2.widthAnchor, multiplier: 1.0).isActive = true layouGuide1.heightAnchor.constraint(equalToConstant: 1).isActive = true layouGuide2.heightAnchor.constraint(equalToConstant: 1).isActive = true // 添加view和layouGuide的约束 grayView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 30).isActive = true grayView.topAnchor.constraint(equalTo: view.topAnchor, constant: 80).isActive = true grayView.heightAnchor.constraint(equalToConstant: 40).isActive = true grayView.widthAnchor.constraint(equalToConstant: 40).isActive = true grayView.rightAnchor.constraint(equalTo: layouGuide1.leftAnchor).isActive = true layouGuide1.rightAnchor.constraint(equalTo: orangeView.leftAnchor).isActive = true orangeView.widthAnchor.constraint(equalToConstant: 40).isActive = true orangeView.heightAnchor.constraint(equalToConstant: 40).isActive = true orangeView.rightAnchor.constraint(equalTo: layouGuide2.leftAnchor).isActive = true layouGuide2.rightAnchor.constraint(equalTo: redView.leftAnchor).isActive = true redView.widthAnchor.constraint(equalToConstant: 40).isActive = true redView.heightAnchor.constraint(equalToConstant: 40).isActive = true redView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -15).isActive = true // 灰色、橙色、红色view和layoutGuide1、layoutGuide2水平中心对齐 grayView.centerYAnchor.constraint(equalTo: layouGuide1.centerYAnchor).isActive = true orangeView.centerYAnchor.constraint(equalTo: grayView.centerYAnchor).isActive = true orangeView.centerYAnchor.constraint(equalTo: layouGuide2.centerYAnchor).isActive = true orangeView.centerYAnchor.constraint(equalTo: redView.centerYAnchor).isActive = true
代码量还是有点多,还是xib和storyboard香。