1. AutoLayout的核心
蘋果公司推出的AutoLayout粪牲,是一個基于約束,動態(tài)計算視圖大小和位置的庫帕胆,以布局引擎系統(tǒng)Layout Engine
為核心轴捎,采用了 Cassowary布局算法,在簡化布局思路的同時百炬,還保證了布局的高效性褐隆。
布局算法Cassowary能夠有效解析線性等式系統(tǒng)和線性不等式系統(tǒng),用來表示用戶界面中那些相等關(guān)系和不等關(guān)系剖踊,通過設(shè)定約束來表示一個視圖相對于另一個視圖的位置庶弃。
Cassowary簡化了布局思路,在運行時動態(tài)的計算視圖位置德澈。布局思路簡化了歇攻,也使界面相關(guān)代碼更容易維護。
布局引擎系統(tǒng)Layout Engine則統(tǒng)一管理了布局的創(chuàng)建梆造、更新缴守、銷毀,將視圖的約束镇辉、優(yōu)先級屡穗、固定大小通過計算轉(zhuǎn)換成最終的大小和位置。
在Layout Engine中忽肛,每當(dāng)約束發(fā)生變化村砂,會重新計算布局,獲取到布局后調(diào)用superview.setNeedLayout()麻裁,然后觸發(fā)Deffered Layout Pass做容錯處理箍镜,然后Layout Engine會從上到下調(diào)用layoutSubviews()來確定各子視圖的位置(通過Cassowary算法計算)源祈,算出來后將子視圖的frame從Layout Engine里拷貝出來,然后進行繪制色迂、渲染香缺,得到我們眼中看到的UI效果。
-
總結(jié):AutoLayout利用約束來控制視圖的大小和位置歇僧,系統(tǒng)會在運行時通過設(shè)置的約束計算得到frame图张,然后繪制出來顯示。
2. AutoLayout的性能
在iOS12之前诈悍,AutoLayout在處理多層級嵌套時祸轮,開銷呈指數(shù)級躍增,但是從iOS12開始侥钳,蘋果補齊了這一漏洞适袜,所以在iOS12及以后,我們就開始放心的使用AutoLayout舷夺,而不用擔(dān)心性能問題苦酱。
更多關(guān)于AutoLayout的性能問題及解決方法,請查看WWDC2018-202
3. AutoLayout常見的問題
(1)幾個更新方法的區(qū)別
setNeedsLayout:告知頁面需要更新给猾,但是不會立刻開始更新疫萤。
layoutIfNeeded:如果有需要刷新的標記,立即調(diào)用layoutSubviews進行布局敢伸;如果沒有標記扯饶,不會調(diào)用layoutSubviews。如果希望立刻生成新的frame需要調(diào)用此方法池颈,利用這點一般布局動畫可以在更新布局后直接使用這個方法讓動畫生效尾序。
layoutSubviews:對subviews進行布局,不能主動調(diào)用饶辙,需要的時候在子類重寫蹲诀,系統(tǒng)會在合適的時候自動調(diào)用。
注意 : 如果要立即刷新frame弃揽,要先調(diào)用setNeedsLayout()脯爪,把標記設(shè)為需要布局,然后馬上調(diào)用layoutIfNeeded()矿微,實現(xiàn)布局痕慢。
setNeedsUpdateConstraints:告知需要更新約束,但是不會立刻開始
updateConstraintsIfNeeded:告知立刻更新約束
updateConstraints:系統(tǒng)更新約束
(2)系統(tǒng)調(diào)用layoutSubviews的時機
init初始化不會觸發(fā)layoutSubviews涌矢,但是使用initWithFrame進行初始化且rect不為zero時掖举,會調(diào)用layoutSubviews。
addSubview的時候會觸發(fā)系統(tǒng)調(diào)用layoutSubviews娜庇。
當(dāng)view的frame發(fā)生改變的時候觸發(fā)layoutSubviews塔次。
滾動一個UIScrollView會觸發(fā)layoutSubviews方篮。
旋轉(zhuǎn)Screen會觸發(fā)父UIView上的layoutSubviews事件。
改變一個UIView大小的時候也會調(diào)用父UIView上的layoutSubviews事件励负。
(3)Intrinsic content size 固有內(nèi)容大小
Intrinsic content size 就是固有內(nèi)容大小藕溅,對應(yīng)的系統(tǒng)方法是
intrinsicContentSize
,重寫UIView的固有內(nèi)容大小后继榆,我們就可以更靈活的使用UIView了巾表。在xib中想使用Intrinsic Size時,記得先在子類中重寫
intrinsicContentSize
略吨,然后在Xib選中Placeholder
集币,才不會報錯哦。
class TestView: UIView {
override var intrinsicContentSize: CGSize {
return CGSize(width: 300, height: 800)
}
}
(4)手寫autoLayout翠忠,寫在哪里最好
如果是自定義的View鞠苟,寫在
init()
方法中如果是在ViewController中,寫在
viewDidLoad()
中
(5)SizeClass適配
關(guān)于SizeClass负间,請看我的另一篇簡書適配iPad和iPhone及其橫豎屏
(6)UIStackView
對一些特定布局時偶妖,使用UIStackView很節(jié)省時間,iOS9.0之后可用
- Axis : 子控件的布局方向政溃,水平或垂直
- Alignment :子控件對齊方式
- Distribution : 子控件分布方式
- Spacing : 控件和控件之間的最小間距
(7)UITableView的高度計算
- 手工計算,并緩存高度
從xib中加載一個Cell态秧,然后給這個Cell中的控件賦值董虱,然后利用systemLayoutSizeFitting
計算高度,然后緩存高度到Model中
let tableViewCell = Bundle.main.loadNibNamed("tableViewCell", owner: self, options: nil)?.last
//先給給tablViewCell中的控件賦值申鱼,然后在計算高度
if let tableViewCell = tableViewCell as? UITableViewCell{
let cellSize = tableViewCell.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
}
//然后在緩存高度到Model中
- 使用Self-Sizing自動計算高度愤诱,當(dāng)布局滿足
self-satisfied
時,系統(tǒng)會自動計算高度捐友,缺點是慢淫半,因為沒有緩存高度,每次都要計算一次
tableView.estimatedRowHeight = 300
tableView.rowHeight = UITableView.automaticDimension