本文為WWDC2015的Session 219 "Mysteries of Auto Layout Part2" 視頻筆記,其內(nèi)容主要涉及了約束的生命周期和,Autolayout調(diào)試兩大內(nèi)容,學(xué)習(xí)記錄了視頻內(nèi)容重要的知識內(nèi)容,歡迎一起探討研究.
資源
內(nèi)容
The Layout Cycle

為了說明
Layout Cycle,蘋果工程師給出了這樣以上這張圖,果然一圖勝千言.基于整個App的運(yùn)行循環(huán)過程中,布局界面上的視圖前,首先會進(jìn)行約束進(jìn)行更新和設(shè)置,然后將最終的布局信息延時地傳遞給視圖,讓視圖獲取到相關(guān)Frame的信息,進(jìn)行位置的尺寸的確定,然后回到主運(yùn)行循環(huán),等待約束再發(fā)生變化.
Constraints Change
- 約束改變后
Frame不會馬上變化 - 重寫
layoutSubviews要十分當(dāng)心
讓約束發(fā)生變化的方式主要由以下三種:
1.使用官方極力推薦的NSLayoutConstraint的``active和deactive`兩個類方法來激活,或無效指定約束.(拋棄掉add/remove約束的方式吧~原因可見Mysteries of Auto Layout Part1 16:27)
方法代碼如下:
[NSLayoutConstraint activateConstraints:]
[NSLayoutConstraint deactivateConstraints:]
// 參數(shù)為NSLayoutConstraint對象的數(shù)組
2.修改約束等式的常量值,乘數(shù)因子或者優(yōu)先值.接觸過Autolayout我們應(yīng)該都知道給視圖設(shè)置約束時要滿足一個約束等式如下.(就當(dāng)你知道了,(╯﹏╰)b)

當(dāng)
Constant或者Multipler變化時,創(chuàng)建的約束等式也相應(yīng)改變,使得Layout引擎重寫計算布局.而對于優(yōu)先值
Priority的改變,往往表現(xiàn)在兩個視圖間同一位置的約束,將根據(jù)優(yōu)先級高的計算布局屬性,除此之外還有具有instrinsic Content Size屬性的視圖間的contentCompressionResistance或者contentHug的競爭.
3.添加或移除視圖,視圖都沒,跟自己相關(guān)的約束當(dāng)然也沒有嘍.(╮(╯▽╰)╭)
當(dāng)約束改變后,Layout引擎就會重新計算新的布局,將布局內(nèi)容的舊值替換成新值,然后調(diào)用父視圖的setNeedsLayout.
而對于setNeedsLayout的描述,官方文檔給出了詳細(xì)說明.此方法只是對當(dāng)前約束變化了的視圖了標(biāo)記,只有等到下一個視圖更新周期時視圖才會真正響應(yīng)約束的變化而改變Frame.如果想要讓視圖快速地響應(yīng)約束的變化,則需要調(diào)用layoutIfNeeded.
Deferred Layout Pass
約束發(fā)生變化后,就進(jìn)入了延遲的布局信息傳遞過程.為什么說延時的呢?回想一下,我們操作約束變化時經(jīng)常通過設(shè)置動畫時間來放大對應(yīng)Frame的延時改變.在這個階段里,視圖更新所在視圖層次內(nèi)的所有約束,然后重新賦值Frame屬性.
更新視圖的約束,使用setNeedsUpdateConstraints 就表示著該視圖的約束需要更新,而具體的更新會在將來某一時刻進(jìn)行.其調(diào)用的時機(jī),蘋果工程師給了兩個情景:
- 當(dāng)某約束在所在位置變化太慢時,使用
update期間將更快的更新約束. - 當(dāng)視圖約束頻繁且多余的變化時,調(diào)用后只會執(zhí)行最后一次的更新變化.
響應(yīng)視圖布局約束變化后,就到了對視圖位置布局的最終設(shè)置,而這個過程則是視圖從上到下遍歷視圖層次,調(diào)用layoutSuviews(iOS)/layout(OSX),設(shè)置具體的內(nèi)部子視圖的Frame.而重寫該方法時,主講人多次提醒開發(fā)者需要十分注意重寫方法時內(nèi)部的操作.
重寫layoutSuviews方法時,其內(nèi)部的約束是不完整的,具體表現(xiàn)為有的子視圖已經(jīng)完成了布局,而有的子視圖正在或者還未進(jìn)行布局.為了保證方法的正確執(zhí)行,我們必須調(diào)用父類的layoutSuviews,不能在其內(nèi)部調(diào)用setNeedsUpdateConstraints(時機(jī)太晚了,已經(jīng)進(jìn)入了布局階段),并且不能隨意地更改約束,防止與其他視圖層次相關(guān)的約束的改變.
Interacting Legacy Layout (處理系統(tǒng)遺留的布局)
-
當(dāng)我們用代碼使用Autolayout給視圖添加約束時,要時刻留意將translatesAutoresizingMaskIntoConstraints`設(shè)為NO,否則添加完約束后就會出現(xiàn)一下約束警告日志..這是由于系統(tǒng)默認(rèn)設(shè)置了Yes,給視圖添加的固定約束而與我們自己后添加的約束產(chǎn)生了沖突,導(dǎo)致不能同時滿足約束條件進(jìn)行布局.(IB里自動將此屬性值設(shè)置為No).interactConstraint.png - 如果想要自己設(shè)置內(nèi)部視圖的
Frame時,重寫layoutSuviews,在此方法里設(shè)置,切記不要忘記調(diào)用父類方法.
Constraint Creation
關(guān)于系統(tǒng)自帶API進(jìn)行約束的創(chuàng)建方式一直以來都被我們詬病,設(shè)個約束出要那么大坨代碼(差評~)...但如果了解使用VFL創(chuàng)建約束的話還是很優(yōu)雅的,只是實現(xiàn)的約束有所限制.而現(xiàn)在iOS9提供了新的約束創(chuàng)建方式,讓開發(fā)者可以更加明了,快速地創(chuàng)建約束.

通過查看
UIView的頭文件,可以看到view的anchor屬性都定義在一個名為UIViewLayoutConstraintCreation的UIView分類中.
// NSLayoutXAxisAnchor 類型表示在X軸上的約束
leadingAnchor
trailingAnchor
leftAnchor
rightAnchor
centerXAnchor
// NSLayoutXAxisAnchor 類型表示在Y軸上的約束
topAnchor
bottomAnchor
centerYAnchor
firstBaselineAnchor
lastBaselineAnchor
// NSLayoutDimension 類型表示尺寸相關(guān)的約束
widthAnchor
heightAnchor
初次之外,新的NSLayoutAnchor類的文件里提供了一系列進(jìn)行相關(guān)視圖進(jìn)行約束設(shè)置的方法,這里就不一一介紹,可以Command + R進(jìn)行看看.
Constraining Negative Space
這里所提到的關(guān)于對多個視圖進(jìn)行等間距或者同時居中的情況下,以前的做法就是創(chuàng)建假的UIView僅僅為提供約束來達(dá)成目的,而現(xiàn)在有了UILayoutGuide對象其表示著一個可以使用Autolayout的布局矩形區(qū)域,可以通過視圖的分類方法addLayoutGuide添加guide,來充當(dāng)之前僅僅提供約束的假視圖,作用于Autolayout.具體的話就舉一個實現(xiàn)讓三個按鈕等間距排列,結(jié)合代碼和圖應(yīng)該能剛好理解點.

//UIButton *saveButton;
//UIButton *cancelButton;
//UIButton *clearButton;
UILayoutGuide *space1 = [[UILayoutGuide alloc]init];
[saveButton addLayoutGuide:space1];
UILayoutGuide *space2 = [[UILayoutGuide alloc]init];
[cancelButton addLayoutGuide:space2];
NSLayoutConstraint *spaceConstraint = [space1.widthAnchor constraintEqualToAnchor:space2.widthAnchor];
NSLayoutConstraint *constraintOne = [saveButton.rightAnchor constraintEqualToAnchor: space1.leftAnchor];
NSLayoutConstraint *constraintTow = [cancelButton.leftAnchor constraintEqualToAnchor:space1.rightAnchor];
NSLayoutConstraint *constraintThree = [cancelButton.rightAnchor constraintEqualToAnchor:space2.leftAnchor];
NSLayoutConstraint *constraintFour = [clearButton.leftAnchor constraintEqualToAnchor:space2.rightAnchor];
[NSLayoutConstraint activateConstraints:@[spaceConstraint, constraintOne, constraintTow, constraintThree, constraintFour]];
Unsatisfiable Constraints
想要了解Unsatisfiable的具體的相關(guān)約束以及關(guān)聯(lián)對象,就必須要理解輸出的約束警告日志.

現(xiàn)在允許給約束或者視圖添加約束標(biāo)識符
identifier字符串屬性,將會在日志輸出中顯示,極大讓日志內(nèi)容更加清晰,讓開發(fā)者快速找到存在問題的約束和視圖.
Resolving Ambiguity
出現(xiàn)Ambigous Layout 警告時表示著約束間存在沖突,而造成約束沖突的原因主要有:
- 約束太少
- 約束的優(yōu)先級沖突

總結(jié)
本視頻從約束布局的生命周期到如何Debug布局問題,對Autolayout引擎的工作流程做了充分了說明和Demo演示,也告訴了我們一些在使用Autolayout中值得注意的地方,對工作中使用和處理Autolayout問題有很大的幫助,作為唯一有字幕(當(dāng)然是英文的??)的介紹Autolayout的WWDC Session是值得一看.如果還有說明問題,歡迎留言,一起探討.
