首先看字面意思就是自動佈局,恩,對,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複製代碼