Auto Layout详解
Auto Layout前世今生
Auto Layout ,是蘋果公司提供的一個基于約束布局,動態計算視圖大小和位置的庫,并且已經集成到了 Xcode 開發環境里。
在引入 Auto Layout 這種自動布局方式之前,iOS 開發都是采用手動布局的方式。而手動布局的方式,原始落后、界面開發維護效率低,對從事過前端開發的人來說更是難以適應。所以,蘋果需要提供更好的界面引擎來提升開發者的體驗,Auto Layout 隨之出現。但是在推出來之后,響應卻平平,其原因就在于對其性能的擔憂。即使后來,蘋果公司推出了在 Auto Layout 基礎上模仿前端 Flexbox 布局思路的 UIStackView 工具,提高了開發體驗和效率,也無法解除開發者們對其性能的顧慮。
iOS 12 將大幅提高 Auto Layout 性能,使滑動達到滿幀。這不得不說Cassowary 算法。
Cassowary布局算法
Cassowary 能夠有效解析線性等式系統和線性不等式系統,用來表示用戶界面中那些相等關系和不等關系。基于此,Cassowary 開發了一種規則系統,通過約束來描視圖間的關系。約束就是規則,這個規則能夠表示出一個視圖相對于另一個視圖的位置。由于 Cassowary 算法讓視圖位置可以按照一種簡單的布局思路來寫,這些簡單的相對位置描述可以在運行時動態地計算出視圖具體的位置。視圖位置的寫法簡化了,界面相關代碼也就更易于維于維護。蘋果公司也是看重了這一點,將其引入到了自己的系統中。由于 Cassowary 算法本身的先進性,更多的開發者將Cassowary 運用到了各個開發語言中,比如 JavaScript、.NET、Java、Smalltalk、C++都有對應的庫。
Auto Layout 的生命周期
Auto Layout 不只有布局算法 Cassowary,還包含了布局在運行時的生命周期等一整套布局引擎系統,用來統一管理布局的創建、更新和銷毀。了解 Auto Layout 的生命周期,是理解它的性能相關話題的基礎。
這一整套布局引擎系統叫作 Layout Engine ,是 Auto Layout 的核心,主導著整個界面布局。
每個視圖在得到自己的布局之前,Layout Engine 會會將視圖、約束、優先級、固定大小通過計算轉換成最終的大小和位置。在 Layout Engine 里,每當約束發生變化,就會觸發 Deffered Layout Pass,完成后進入監聽約束變化的狀態。當再次監聽到約束變化,即進入下一輪循環中。整個過程如下圖所示:
圖1:Layout Engine 界面布局過程圖中, Constraints Change 表示的就是約束變化,添加、刪除視圖時會觸發約束變化。Activating 或 Deactivating,設置 Constant 或 Priority 時也會觸發約束變化。Layout Engine 在碰到約束變化后會重新計算布局,獲取到布局后調用 superview.setNeedsLayout(),然后進入 Deferred Layout Pass。
Deferred Layout Pass 的主要作用是做容錯錯處理。如果有些視圖在更新約束時沒有確定或缺失布局聲明的話,會先在這里做容錯處理。
接下來,Layout Engine 會從上到下調用layoutSubviews() ,通過 Cassowary算法計算各個子視圖的位置,算出來后將子視圖的 frame 從Layout Engine 里拷貝出來。
在這之后的處理,就和手寫布局的繪制、渲染過程一樣了。所以,使用 Auto Layout 和手寫布局的區別,就是多了布局上的這個計算過程。
Auto Layout 性能問題
Auto Layout 在 iOS 12 中優化后,它的性能得到了極大的提升,已經基本和手寫布局一樣可以達到性能隨著視圖嵌套的數量呈線性增長了。而在此之前的 Auto Layout,視圖嵌套的數量對對性能的影響是呈指數級增長的。使用 Auto Layout 一定要注意多使用 Compression Resistance Priority 和 Hugging Priority,利用優先級的設置,讓布局更加靈活,代碼更少,更易于維護。
圖2: Auto Layout 在 iOS 12 中優化后的表現AutoLayout常見的問題
(1)幾個更新方法的區別
- setNeedsLayout:告知頁面需要更新,但是不會立刻開始更新。執行后會立刻調用layoutSubviews。
- layoutIfNeeded:如果有需要刷新的標記,立即調用layoutSubviews進行布局;如果沒有標記,不會調用layoutSubviews。如果希望立刻生成新的frame需要調用此方法,利用這點一般布局動畫可以在更新布局后直接使用這個方法讓動畫生效。
- layoutSubviews:對subviews進行布局,不能主動調用,需要的時候在子類重寫,系統會在合適的時候自動調用。
- 注意 : 如果要立即刷新frame,要先調用setNeedsLayout(),把標記設為需要布局,然后馬上調用layoutIfNeeded(),實現布局。
- setNeedsUpdateConstraints:告知需要更新約束,但是不會立刻開始
- updateConstraintsIfNeeded:告知立刻更新約束
- updateConstraints:系統更新約束
(2)系統調用layoutSubviews的時機
- init初始化不會觸發layoutSubviews,但是使用initWithFrame進行初始化且rect不為zero時,會調用layoutSubviews。
- addSubview的時候會觸發系統調用layoutSubviews。
- 當view的frame發生改變的時候觸發layoutSubviews。
- 滾動一個UIScrollView會觸發layoutSubviews。
- 旋轉Screen會觸發父UIView上的layoutSubviews事件。
- 改變一個UIView大小的時候也會調用父UIView上的layoutSubviews事件。
具體細節可以參考:http://www.code4app.com/blog-822415-3151.html
(3)什么時候使用frame布局,什么時候選用Auto Layout布局
簡單的 UI 使用 Auto Layout ,復雜的 UI 使用 frame。原因如下:
- 從代碼量上來看,兩種布局方式相差不大。有時候發現復雜的 UI 使用 Auto Layout 的話,代碼量反而會變多,因為復雜的 UI 往往會有復雜的邏輯,比如根據數據的不同,部分 UI 的顯示會有變動(比如某個子視圖隱藏與顯示, 會影響到其它視圖的布局)。
- 固定的UI簡單的布局,這種情況下使用 Auto Layout 還是挺方便的,具有快速、方便、簡潔的布局效果。
- 動態復雜的 UI 布局,這種情況下使用 Auto Layout 來布局,感覺就不合適。因為不管是 frame 還是 Auto Layout,都需要去計算高度,Auto Layout通過 Cassowary 算法計算各個子視圖的位置,算出來后將子視圖的 frame 從 Layout Engine 里拷貝出來;而frame布局,則可以快速的通過事先約定的布局計算出相應的frame,再進行相應的繪制、渲染。這種情況下,直接使用 frame 會比較精簡。
?
總結
以上是生活随笔為你收集整理的Auto Layout详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Samba 常用服务器搭建操作过程
- 下一篇: import package的问题