Auto Layout前世今生
Auto Layout 拴清,是蘋果公司提供的一個(gè)基于約束布局,動(dòng)態(tài)計(jì)算視圖大小和位置的庫(kù)俱笛,并且已經(jīng)集成到了 Xcode 開發(fā)環(huán)境里朵纷。
在引入 Auto Layout 這種自動(dòng)布局方式之前,iOS 開發(fā)都是采用手動(dòng)布局的方式晤郑。而手動(dòng)布局的方式,原始落后、界面開發(fā)維護(hù)效率低造寝,對(duì)從事過前端開發(fā)的人來(lái)說更是難以適應(yīng)磕洪。所以,蘋果需要提供更好的界面引擎來(lái)提升開發(fā)者的體驗(yàn)诫龙,Auto Layout 隨之出現(xiàn)析显。但是在推出來(lái)之后,響應(yīng)卻平平签赃,其原因就在于對(duì)其性能的擔(dān)憂谷异。即使后來(lái),蘋果公司推出了在 Auto Layout 基礎(chǔ)上模仿前端 Flexbox 布局思路的 UIStackView 工具锦聊,提高了開發(fā)體驗(yàn)和效率歹嘹,也無(wú)法解除開發(fā)者們對(duì)其性能的顧慮。
iOS 12 將大幅提高 Auto Layout 性能孔庭,使滑動(dòng)達(dá)到滿幀尺上。這不得不說Cassowary 算法。
Cassowary布局算法
Cassowary 能夠有效解析線性等式系統(tǒng)和線性不等式系統(tǒng),用來(lái)表示用戶界面中那些相等關(guān)系和不等關(guān)系圆到≡跖祝基于此,Cassowary 開發(fā)了一種規(guī)則系統(tǒng)芽淡,通過約束來(lái)描視圖間的關(guān)系马绝。約束就是規(guī)則,這個(gè)規(guī)則能夠表示出一個(gè)視圖相對(duì)于另一個(gè)視圖的位置挣菲。由于 Cassowary 算法讓視圖位置可以按照一種簡(jiǎn)單的布局思路來(lái)寫富稻,這些簡(jiǎn)單的相對(duì)位置描述可以在運(yùn)行時(shí)動(dòng)態(tài)地計(jì)算出視圖具體的位置。視圖位置的寫法簡(jiǎn)化了己单,界面相關(guān)代碼也就更易于維于維護(hù)唉窃。蘋果公司也是看重了這一點(diǎn),將其引入到了自己的系統(tǒng)中纹笼。由于 Cassowary 算法本身的先進(jìn)性纹份,更多的開發(fā)者將Cassowary 運(yùn)用到了各個(gè)開發(fā)語(yǔ)言中,比如 JavaScript廷痘、.NET蔓涧、Java、Smalltalk笋额、C++都有對(duì)應(yīng)的庫(kù)元暴。
Auto Layout 的生命周期
Auto Layout 不只有布局算法 Cassowary,還包含了布局在運(yùn)行時(shí)的生命周期等一整套布局引擎系統(tǒng)兄猩,用來(lái)統(tǒng)一管理布局的創(chuàng)建茉盏、更新和銷毀鉴未。了解 Auto Layout 的生命周期,是理解它的性能相關(guān)話題的基礎(chǔ)鸠姨。
這一整套布局引擎系統(tǒng)叫作 Layout Engine 铜秆,是 Auto Layout 的核心,主導(dǎo)著整個(gè)界面布局讶迁。
每個(gè)視圖在得到自己的布局之前连茧,Layout Engine 會(huì)會(huì)將視圖、約束巍糯、優(yōu)先級(jí)啸驯、固定大小通過計(jì)算轉(zhuǎn)換成最終的大小和位置。在 Layout Engine 里祟峦,每當(dāng)約束發(fā)生變化罚斗,就會(huì)觸發(fā) Deffered Layout Pass,完成后進(jìn)入監(jiān)聽約束變化的狀態(tài)搀愧。當(dāng)再次監(jiān)聽到約束變化惰聂,即進(jìn)入下一輪循環(huán)中。整個(gè)過程如下圖所示:
圖中咱筛, Constraints Change 表示的就是約束變化搓幌,添加、刪除視圖時(shí)會(huì)觸發(fā)約束變化迅箩。Activating 或 Deactivating溉愁,設(shè)置 Constant 或 Priority 時(shí)也會(huì)觸發(fā)約束變化。Layout Engine 在碰到約束變化后會(huì)重新計(jì)算布局饲趋,獲取到布局后調(diào)用 superview.setNeedLayout()拐揭,然后進(jìn)入 Deferred Layout Pass。
Deferred Layout Pass 的主要作用是做容錯(cuò)錯(cuò)處理奕塑。如果有些視圖在更新約束時(shí)沒有確定或缺失布局聲明的話堂污,會(huì)先在這里做容錯(cuò)處理。
接下來(lái)龄砰,Layout Engine 會(huì)從上到下調(diào)用layoutSubviews() 盟猖,通過 Cassowary算法計(jì)算各個(gè)子視圖的位置,算出來(lái)后將子視圖的 frame 從Layout Engine 里拷貝出來(lái)换棚。
在這之后的處理式镐,就和手寫布局的繪制、渲染過程一樣了固蚤。所以娘汞,使用 Auto Layout 和手寫布局的區(qū)別,就是多了布局上的這個(gè)計(jì)算過程夕玩。
Auto Layout 性能問題
Auto Layout 在 iOS 12 中優(yōu)化后你弦,它的性能得到了極大的提升惊豺,已經(jīng)基本和手寫布局一樣可以達(dá)到性能隨著視圖嵌套的數(shù)量呈線性增長(zhǎng)了。而在此之前的 Auto Layout鳖目,視圖嵌套的數(shù)量對(duì)對(duì)性能的影響是呈指數(shù)級(jí)增長(zhǎng)的扮叨。使用 Auto Layout 一定要注意多使用 Compression Resistance Priority 和 Hugging Priority缤弦,利用優(yōu)先級(jí)的設(shè)置领迈,讓布局更加靈活,代碼更少碍沐,更易于維護(hù)狸捅。
AutoLayout常見的問題
(1)幾個(gè)更新方法的區(qū)別
- setNeedsLayout:告知頁(yè)面需要更新,但是不會(huì)立刻開始更新累提。執(zhí)行后會(huì)立刻調(diào)用layoutSubviews尘喝。
- layoutIfNeeded:如果有需要刷新的標(biāo)記,立即調(diào)用layoutSubviews進(jìn)行布局斋陪;如果沒有標(biāo)記朽褪,不會(huì)調(diào)用layoutSubviews。如果希望立刻生成新的frame需要調(diào)用此方法无虚,利用這點(diǎn)一般布局動(dòng)畫可以在更新布局后直接使用這個(gè)方法讓動(dòng)畫生效缔赠。
- layoutSubviews:對(duì)subviews進(jìn)行布局,不能主動(dòng)調(diào)用友题,需要的時(shí)候在子類重寫嗤堰,系統(tǒng)會(huì)在合適的時(shí)候自動(dòng)調(diào)用。
- 注意 : 如果要立即刷新frame度宦,要先調(diào)用setNeedsLayout()踢匣,把標(biāo)記設(shè)為需要布局,然后馬上調(diào)用layoutIfNeeded()戈抄,實(shí)現(xiàn)布局离唬。
- setNeedsUpdateConstraints:告知需要更新約束,但是不會(huì)立刻開始
- updateConstraintsIfNeeded:告知立刻更新約束
- updateConstraints:系統(tǒng)更新約束
(2)系統(tǒng)調(diào)用layoutSubviews的時(shí)機(jī)
- init初始化不會(huì)觸發(fā)layoutSubviews划鸽,但是使用initWithFrame進(jìn)行初始化且rect不為zero時(shí)输莺,會(huì)調(diào)用layoutSubviews。
- addSubview的時(shí)候會(huì)觸發(fā)系統(tǒng)調(diào)用layoutSubviews漾稀。
- 當(dāng)view的frame發(fā)生改變的時(shí)候觸發(fā)layoutSubviews模闲。
- 滾動(dòng)一個(gè)UIScrollView會(huì)觸發(fā)layoutSubviews。
- 旋轉(zhuǎn)Screen會(huì)觸發(fā)父UIView上的layoutSubviews事件崭捍。
- 改變一個(gè)UIView大小的時(shí)候也會(huì)調(diào)用父UIView上的layoutSubviews事件尸折。
具體細(xì)節(jié)可以參考:http://www.code4app.com/blog-822415-3151.html
(3)什么時(shí)候使用frame布局,什么時(shí)候選用Auto Layout布局
簡(jiǎn)單的 UI 使用 Auto Layout 殷蛇,復(fù)雜的 UI 使用 frame实夹。原因如下:
- 從代碼量上來(lái)看橄浓,兩種布局方式相差不大。有時(shí)候發(fā)現(xiàn)復(fù)雜的 UI 使用 Auto Layout 的話亮航,代碼量反而會(huì)變多荸实,因?yàn)閺?fù)雜的 UI 往往會(huì)有復(fù)雜的邏輯,比如根據(jù)數(shù)據(jù)的不同缴淋,部分 UI 的顯示會(huì)有變動(dòng)(比如某個(gè)子視圖隱藏與顯示准给, 會(huì)影響到其它視圖的布局)。
- 固定的UI簡(jiǎn)單的布局重抖,這種情況下使用 Auto Layout 還是挺方便的露氮,具有快速、方便钟沛、簡(jiǎn)潔的布局效果畔规。
- 動(dòng)態(tài)復(fù)雜的 UI 布局,這種情況下使用 Auto Layout 來(lái)布局恨统,感覺就不合適叁扫。因?yàn)椴还苁?frame 還是 Auto Layout,都需要去計(jì)算高度畜埋,Auto Layout通過 Cassowary 算法計(jì)算各個(gè)子視圖的位置莫绣,算出來(lái)后將子視圖的 frame 從 Layout Engine 里拷貝出來(lái);而frame布局由捎,則可以快速的通過事先約定的布局計(jì)算出相應(yīng)的frame兔综,再進(jìn)行相應(yīng)的繪制、渲染狞玛。這種情況下软驰,直接使用 frame 會(huì)比較精簡(jiǎn)。