理解Update Cycle摘自《[譯] 揭秘 iOS 布局》
UPdate Cycle是當應用完成了你所有的事件處理代碼之后回到主RunLoop時的時間點挺尾。正是在這個時間點上開始更新布局烦周、顯示和設置約束。如果你在處理事件的代碼中請求修改了一個view,那么系統(tǒng)會把這個view標記為重畫卿叽。接下來的Update cycle中床佳,系統(tǒng)就會執(zhí)行view的更改滋早。用戶交互和布局更新的延遲幾乎不會被用戶察覺到,IOS一般以60fps的速度展示動畫砌们,也就是一個周期大概是1/60s杆麸。這個更新過程很快,用戶也很難察覺到浪感。所以用戶察覺不到UI中更新的延遲昔头,但是由于處理事件和對應view重畫中間存在著間隔,RunLoop中某時刻的view更新可能不是你想要的那樣影兽。如果你的代碼中的某些設計依賴于當下的view內(nèi)容或者布局揭斧,那么就有在過時view信息上操作的風險。
意思就是當我們代碼更新view的布局的時候峻堰,界面上的UI并不會馬上更新讹开,而是會在下一次Update Cycle的時候更新盅视。
layoutSubviews()
這個方法的作用其實就是通知當前的view你的布局發(fā)生變化了,你要重新布局你的subviews萧吠,該方法不要手動調(diào)用左冬,系統(tǒng)會在需要調(diào)用的時候自動調(diào)用。如下幾種情況會觸發(fā) layoutSubviews()
- 修改view的大小
- 新增subview
- 用戶在UIScrollView上滾動
- 旋轉(zhuǎn)屏幕
- 更新視圖的約束
setNeedsLayout()
顧名思義需要設置布局纸型,就是告訴系統(tǒng)這個視圖需要更新布局,這個方法會立即返回梅忌,但是view會在下一次Update cycle中更新狰腌,調(diào)用視圖們的layoutSubviews()
。
layoutIfNeeded()
layoutIfNeeded()
不一定會觸發(fā)view的layoutSubviews
牧氮。系統(tǒng)會檢測layoutSubviews的觸發(fā)條件琼腔,如果符合條件,那么會立即觸發(fā)layoutSubviews()
踱葛,不會像setNeedsLayout()
等待下一次Update cycle丹莲。如果不符合layoutSubviews
的觸發(fā)條件則不會觸發(fā)。
這里可以解釋自動布局在開始執(zhí)行動畫的時候為什么必須執(zhí)行setNeedsLayout()
尸诽,如果不添加setNeedsLayout()
不會執(zhí)行動畫甥材。
如下代碼的結果是2s之后無動畫。
self.viewOne = [[TestViewOne alloc] initWithFrame:CGRectZero];
self.viewOne.backgroundColor = UIColor.redColor;
[self.view addSubview:self.viewOne];
[self.viewOne mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.mas_equalTo(self.view);
make.width.mas_equalTo(100);
make.height.mas_equalTo(100);
}];
[UIView animateWithDuration:2 animations:^{
[self.viewOne mas_remakeConstraints:^(MASConstraintMaker *make) {
make.center.mas_equalTo(self.view);
make.width.mas_equalTo(50);
make.height.mas_equalTo(50);
}];
NSLog(@"動畫打印viewOne的frame%@",NSStringFromCGRect(self.viewOne.frame));
}];
//打印結果
2019-03-13 17:44:27.115060+0800 layoutStudy[16692:523774] 動畫打印viewOne的frame{{182, 423}, {100, 100}}
如果添加進去layoutIfNeeded()
性含;
self.viewOne = [[TestViewOne alloc] initWithFrame:CGRectZero];
self.viewOne.backgroundColor = UIColor.redColor;
[self.view addSubview:self.viewOne];
[self.viewOne mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.mas_equalTo(self.view);
make.width.mas_equalTo(100);
make.height.mas_equalTo(100);
}];
[UIView animateWithDuration:2 animations:^{
[self.viewOne mas_remakeConstraints:^(MASConstraintMaker *make) {
make.center.mas_equalTo(self.view);
make.width.mas_equalTo(50);
make.height.mas_equalTo(50);
}];
[self.viewOne layoutIfNeeded];
NSLog(@"動畫打印viewOne的frame%@",NSStringFromCGRect(self.viewOne.frame));
}];
//打印結果
2019-03-13 17:44:27.115060+0800 layoutStudy[16692:523774] 動畫打印viewOne的frame{{182, 423}, {50, 50}}
其實自動布局也是通過一套引擎轉(zhuǎn)換成frame洲赵,但是這個過程并不是馬上進行的會在下一次Update Cycle執(zhí)行,調(diào)用layoutIfNeeded
會強制計算frame繪制完畢會調(diào)用layoutSubviews
商蕴,并且被animation的block截獲叠萍。可以明顯的看出代碼1中self.viewOne的frame沒有變绪商,但是代碼2的frame被提前繪制出來了苛谷,所以代碼2有動畫,代碼1沒有動畫格郁。
總結
那么我們的布局代碼應該怎么寫呢腹殿,我覺得不改變的靜態(tài)布局直接在init的時候?qū)懢涂梢裕雅c自身視圖尺寸有依賴的約束寫到里面理张,由于layoutSubviews可能會多次調(diào)用赫蛇。不要在其中寫比較耗時的操作和初始化對象。