首先看字面意思就是自动布局,恩,对,AutoLayout讲的就是 iOS 中对于视图布局的自动布局。那么视图如何选择使用autolayout呢? 简单地说,它是经过约束来实现的。 约束就告诉自动布局引擎,咱们但愿它在此视图上执行布局以及布置视图的方式。 那么如今咱们了解一下什么是约束。数组
NSLayoutConstraint是继承自NSObject的一个类,也就是用来作约束的一个类。约束是对于视图而言的。 视图能够有许多约束,那么咱们怎么给视图添加或者移除约束呢?bash
如今来讲两种方法给视图添加或者移除约束:less
**第一种方法:**经过UIView的实例方法来添加和移除自身的约束:(具体看下边 UIView 的扩展)布局
extension UIView {
@available(iOS 6.0, *)
open var constraints: [NSLayoutConstraint] { get }
@available(iOS 6.0, *)
open func addConstraint(_ constraint: NSLayoutConstraint)
@available(iOS 6.0, *)
open func addConstraints(_ constraints:
[NSLayoutConstraint])
@available(iOS 6.0, *)
open func removeConstraint(_ constraint:
NSLayoutConstraint)
@available(iOS 6.0, *)
open func removeConstraints(_ constraints:
[NSLayoutConstraint])
}
复制代码
第二种方法: 经过NSLayoutConstraint的一个类方法来给视图添加约束,具体以下: 在看NSLayoutConstraint的实例化方法的时候,会发现他还有类方法,咱们看下面两个方法:ui
// 添加约束
@available(iOS 8.0, *)
open class func activate(_ constraints:
[NSLayoutConstraint])
// 移除约束
@available(iOS 8.0, *)
open class func deactivate(_ constraints:
[NSLayoutConstraint])
复制代码
咱们能够看到以上两种方法都须要一个NSLayoutConstraint类型的参数,如何实例化一个NSLayoutConstraint对象呢? 那么咱们来看一下 NSLayoutConstraint 的初始化方法:spa
public convenience init( item view1: Any,
attribute attr1: NSLayoutAttribute,
relatedBy relation: NSLayoutRelation,
toItem view2: Any?, attribute
attr2: NSLayoutAttribute,
multiplier: CGFloat,
constant c: CGFloat)
复制代码
初始化方法中有不少参数,接下来咱们来说一下各个参数的意义:code
item: 能够看到他后边还有一个 view1 ,我的认为通常的他就是一个 UIView 或者其子类对象,是要进行约束的那个视图orm
attribute: 是一个NSLayoutAttribute枚举,能够看到他的枚举值有 left、right、bottom、top 等,这个参数就是第一个参数中 view 所要进行的约束的位置cdn
relatedBy: 是一个NSLayoutRelation枚举,他的枚举值有 lessThanOrEqual(小于等于)、equal(等于)、 greaterThanOrEqual(大于等于)。 这个参数是用来指定 view1和接下来那个参数 view2两个视图之 间的约束关系的对象
toItem: 和第一个参数同样,这个主要就是来和第一个 view 作参照的那个 视图
attribute: 和第二个参数同样,是来表示第一个视图对第二个视图的 参考位置 ,上下左右 仍是 center等
multiplier: 乘数的意思,CGFloat类型。是来计算两个视图之间位置关系的 一个重要因素
constant: 常数, CGFloat类型。也是计算两个视图位置关系的重要因素
这几个参数所构造出来的NSLayoutConstraint实例,添加到视图以后的位置关系究竟是怎样的呢,能够用下面 这个公式 来计算得出:
item的attribute relatedBy toItem的attribute * multiplier + constant
简化以后就是:
A 视图 = B视图 * multiplier + constant;
复制代码
公式中的等于号能够根据 relatedBy 参数来变为 >= 或者 <=
接下来咱们来实现添加约束
第一种方法view 的实例方法
let viewItem = UIView()
// 必定要禁止 autoresize
viewItem.translatesAutoresizingMaskIntoConstraints = false
viewItem.backgroundColor = UIColor.brown
// 必须先添加到 View上 而后再去作约束 由于约束若是要用到父视图 不提早添加 怎么知道谁是父视图
view.addSubview(viewItem)
// 添加和父视图view X 中心对齐
let centerXConstrains = NSLayoutConstraint( item: viewItem,
attribute: .centerX,
relatedBy: .equal,
toItem: view,
attribute: .centerX,
multiplier: 1, constant: 0)
// 添加和父视图 Y 中心对其的约束
let centerYConstrains = NSLayoutConstraint( item: viewItem,
attribute: .centerY,
relatedBy: .equal,
toItem: view,
attribute: .centerY,
multiplier: 1,
constant: 0)
// 添加和父视图 宽的关系 为1:2约束
let heightConstrains = NSLayoutConstraint( item: viewItem,
attribute: .width ,
relatedBy: .equal,
toItem: view,
attribute: .width,
multiplier: 1 / 2,
constant: 0)
// 添加自身视图宽 高的关系 为1:1 的约束
let widthConstrains = NSLayoutConstraint( item: viewItem,
attribute: .height ,
relatedBy: .equal,
toItem: viewItem,
attribute: .width,
multiplier: 1,
constant: 0)
// 必定要分清楚约束是相对于谁加的 加给谁的
// view.addConstraint(centerXConstrains)
// view.addConstraint(centerYConstrains)
// view.addConstraint(heightConstrains)
// 此处能够向上边注释的那样一个一个添加,也能够以一个数组来一块儿添加
view.addConstraints([centerXConstrains,
centerYConstrains,
heightConstrains])
viewItem.addConstraint(widthConstrains)
复制代码
以上代码实现了一个中心点在父视图中心,宽等于父视图宽一半的一个正方形。细心的人会看到 addConstraint方法有的是 view 调用,有的是 viewItem 调用。的确约束具体加给谁也是有原则的。
具体总结以下:
1.若是两个视图(也就是参数 item 和 toItem)是父子关系,设置子控件的约束,约束添加到父控件上
2.若是两个视图(也就是参数 item 和 toItem)是兄弟关系,设置两兄弟的约束,约束会添加到第一个共同的父控件上
3.若是两个视图(也就是参数 item 和 toItem)是同一个视图,约束会添加到本身上
使用第二种方法 :NSLayoutConstraint的类方法实现约束 在上边咱们看了NSLayoutConstraint的类方法,很明显他是 iOS8 以后才适用的,这个方法参数是一个NSLayoutConstraint数组。 那么NSLayoutConstraint的实例化和上边同样,只是最终加给视图约束的方法不一样,具体实现以下:
let view1 = UIView()
view1.backgroundColor = UIColor.blue
view1.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(view1)
// view1 和 viewItem 的 Y 中心对称
let topContrians = NSLayoutConstraint(item: view1,
attribute: .top,
relatedBy: .equal,
toItem: viewItem,
attribute: .centerY,
multiplier: 1,
constant: 0)
// view1 和 viewItem 的 bottom 对其
let bottomContrians = NSLayoutConstraint(item: view1,
attribute: .bottom,
relatedBy: .equal,
toItem: viewItem,
attribute: .bottom,
multiplier: 1,
constant: 0)
// view1 和 viewItem 的 width 相等
let widthContrains = NSLayoutConstraint(item: view1,
attribute: .width,
relatedBy: .equal,
toItem: viewItem,
attribute: .width,
multiplier: 1,
constant: 0)
// view1 和 viewItem 的 leading 对齐
let leadingContrains = NSLayoutConstraint(item: view1,
attribute: .leading,
relatedBy: .equal,
toItem: viewItem,
attribute: .leading,
multiplier: 1,
constant: 0)
// iOS8 之后 NSLayoutConstraint 的类方法 也能够把约束添加到视图上,并且省掉了判断添加到那个视图上的问题,避免了上面例子中由于视图添加错误而致使的崩溃
NSLayoutConstraint.activate([topContrians,bottomContrians,widthContrains,leadingContrains])
复制代码
最终添加给视图约束的方法就是这个类方法。
Anchor notation 作约束
从iOS 9开始,又一种新的方式来作约束。 其 本质也是经过上边两种方法给视图添加约束,只不过是获取NSLayoutConstraint的实例使用了Anchor,就是经过 Anchor 来实现。
如下几个属性就是 View 的 Anchor notation扩展,主要也是视图的上下左右等约束点的Anchor
extension UIView {
@available(iOS 9.0, *)
open var leadingAnchor: NSLayoutXAxisAnchor { get }
@available(iOS 9.0, *)
open var trailingAnchor: NSLayoutXAxisAnchor { get }
@available(iOS 9.0, *)
open var leftAnchor: NSLayoutXAxisAnchor { get }
@available(iOS 9.0, *)
open var rightAnchor: NSLayoutXAxisAnchor { get }
@available(iOS 9.0, *)
open var topAnchor: NSLayoutYAxisAnchor { get }
@available(iOS 9.0, *)
open var bottomAnchor: NSLayoutYAxisAnchor { get }
@available(iOS 9.0, *)
open var widthAnchor: NSLayoutDimension { get }
@available(iOS 9.0, *)
open var heightAnchor: NSLayoutDimension { get }
@available(iOS 9.0, *)
open var centerXAnchor: NSLayoutXAxisAnchor { get }
@available(iOS 9.0, *)
open var centerYAnchor: NSLayoutYAxisAnchor { get }
@available(iOS 9.0, *)
open var firstBaselineAnchor: NSLayoutYAxisAnchor { get }
@available(iOS 9.0, *)
open var lastBaselineAnchor: NSLayoutYAxisAnchor { get }
}
复制代码
能够看到 View 的 Anchor 属性都是 NSLayoutYAxisAnchor类型的,那么NSLayoutYAxisAnchor是什么呢?
@available(iOS 9.0, *)
open class NSLayoutAnchor<AnchorType : AnyObject> : NSObject {
open func constraint(equalTo anchor:
NSLayoutAnchor<AnchorType>) -> NSLayoutConstraint
open func constraint(greaterThanOrEqualTo anchor:
NSLayoutAnchor<AnchorType>) -> NSLayoutConstraint
open func constraint(lessThanOrEqualTo anchor:
NSLayoutAnchor<AnchorType>) -> NSLayoutConstraint
open func constraint(equalTo anchor:
NSLayoutAnchor<AnchorType>,
constant c: CGFloat) -> NSLayoutConstraint
open func constraint(greaterThanOrEqualTo anchor:
NSLayoutAnchor<AnchorType>,
constant c: CGFloat) -> NSLayoutConstraint
open func constraint(lessThanOrEqualTo anchor:
NSLayoutAnchor<AnchorType>,
constant c: CGFloat) -> NSLayoutConstraint
}
@available(iOS 9.0, *)
open class NSLayoutXAxisAnchor:
NSLayoutAnchor<*NSLayoutXAxisAnchor*> {
}
复制代码
NSLayoutXAxisAnchor就是NSLayoutAnchor的一个子类,NSLayoutAnchor有6个constraint的方法,具体看一下参数就知道有什么区别了,这里就不一一赘述了。每一个方法均可以返回一个 NSLayoutConstraint 类型的实例。那么咱们就可使用返回的实例,利用上边的两种方法给视图添加约束了,具体以下:(此处用的 第二种类方法)
let view2 = UIView()
view.addSubview(view2)
view2.translatesAutoresizingMaskIntoConstraints = false
view2.backgroundColor = UIColor.cyan NSLayoutConstraint.activate(
[view2.leadingAnchor.constraint(equalTo:
view1.leadingAnchor),
view2.trailingAnchor.constraint(equalTo:
view1.trailingAnchor),
view2.topAnchor.constraint(equalTo:
view1.bottomAnchor),
view2.heightAnchor.constraint(equalTo:
view1.heightAnchor)])
复制代码
visual format 是一种基于文原本缩略的建立约束的速记(简写)方法 ,具备 容许同时描述多个约束的优势,而且特别适合于当水平或垂直地布置一系列视图的状况。 例子:"V:|[v2(10)]"
各个符号的意义:
V:表示正在的垂直维度; 若是是H:水平维度
|:竖线(|)表示父视图
v2: 视图的名称显示在方括号中,v2就是视图的名称
10:视图名称以后的小括号中的数字,表示视图的大小。 V的话就是高,H 就是宽,根据视图的排列方向而定
那么怎么样使用visual format呢? 咱们来看一下NSLayoutConstraint的另外一个类方法:
open class func constraints(withVisualFormat
format: String,
options opts: NSLayoutFormatOptions = [],
metrics: [String : Any]?,
views: [String : Any])-> [NSLayoutConstraint]
复制代码
这个类方法最终返回了一个NSLayoutConstraint数组,那么咱们又可使用上边的两个方法给视图添加约束了
要使用visual format,必须提供一个字典,将visual format 字符串提到的每一个视图的字符串名称对应到实际视图,例如:
建立两个视图v1和 v2:
let v1 = UIView()
v1.backgroundColor = UIColor.blue
v1.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(v1)
let v2 = UIView()
v2.backgroundColor = UIColor.cyan
v2.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(v2)
复制代码
而后就建立所谓的字典:
// 字典d 就是将字符串 v1对应实际的视图 v1 ,"v2"对应 v2.了解字典的就很
容易懂什么意思
let d = ["v1":v1,"v2": v2]
复制代码
具体使用咱们经过一段代码来看:(如下代码中注释是对方法的第一个String 类型的参数作出的解释)
NSLayoutConstraint.activate([
//左右两边都有 | 就是说水平方向父视图的两边 距离都为零
NSLayoutConstraint.constraints(withVisualFormat:
"H:|[v1]|", metrics: nil, views: d),
// 左边有 | 就是说竖直方向父视图的上方 距离为0 高度为100
NSLayoutConstraint.constraints(withVisualFormat:
"V:|[v1(100)]", metrics: nil, views: d),
// 左边有 | 就是说水平方向 距离父视图的左边为0 且宽度为140
NSLayoutConstraint.constraints(withVisualFormat:
"H:|[v2(40)]", metrics: nil, views: d),
// 右边有 | 就是说竖直方向距离父视图下方为40 高度为120
NSLayoutConstraint.constraints(withVisualFormat:
"V:[v2]-(40)-|", metrics: nil, views: d),
// 没有 |(竖线),也就是说这个约束和父视图没有关系了。
是[v1]-0-[v2]这样的一个形式,这说的是V(竖直)方向上v1和v2相聚为20
NSLayoutConstraint.constraints(withVisualFormat:
"V:[v1]-20-[v2]", metrics: nil, views: d)].flatMap{$0})
复制代码
上边这种方法不经常使用,也就不作太多的解释了
AutoLayout 就是要用约束来实现的,既然要用约束就离不开 NSLayoutConstraint这个类,最终获得NSLayoutConstraint的类实例,而后经过上边的两种方法任何一种就能够把约束添加到视图上,完成自动布局了。
建议仍是用类方法,出错的几率更低一些。
布局约束的重要计算公式:
View1 = View2 * multiplier + constant
复制代码
若是你会 StoryBoard随便拖进去一个控件作约束: 能够看到约束的这个界面,一目了然:
哪两个视图(ViewItem 和 SuperView)在作约束, 参考的是视图的哪一个位置(CenterY), 两个视图之间的关系(Equal) 常量关系(Constant) 优先级 (Priority) 倍数关系 (Multiplier)
计算公式:
First Item = Second Item * Multiplier + Constant复制代码