簡(jiǎn)要
通過Masonry設(shè)置約束后积担,無法再通過Frame對(duì)其調(diào)整位置先誉。因?yàn)樵谡嬲季诛@示到屏幕時(shí)褐耳,系統(tǒng)會(huì)根據(jù)約束從新計(jì)算Frame刃滓,設(shè)置過的Frame會(huì)失效砰诵,所以想要對(duì)利用Masonry約束過的view設(shè)置動(dòng)畫等击罪,不能按照常規(guī)方式設(shè)置娃弓。
關(guān)于UIView的Layer台丛,IOS提供了三個(gè)方法:
setNeedsLayout:
標(biāo)記為需要重新布局,異步調(diào)用layoutIfNeeded刷新布局砾肺,不立即刷新挽霉,但layoutSubviews一定會(huì)被調(diào)用。
此方法會(huì)將view當(dāng)前的layout設(shè)置為無效的变汪,并在下一個(gè)upadte cycle里去觸發(fā)layout更新侠坎。
此行為允許你將所有的布局更新合并到一個(gè)更新周期,這很適合用來優(yōu)化性能裙盾。
setNeedDisplay在receiver標(biāo)上一個(gè)需要被重新繪圖的標(biāo)記硅蹦,
在下一個(gè)draw周期自動(dòng)重繪荣德,iphone device的刷新頻率是60hz,也就是1/60秒后重繪童芹。
layoutIfNeeded:
如果有需要刷新的涮瞻,立即調(diào)用layoutSubviews進(jìn)行布局(如果沒有,不會(huì)調(diào)用layoutSubviews)假褪。
使用此方法強(qiáng)制立即進(jìn)行l(wèi)ayout,從當(dāng)前view開始署咽,
此方法會(huì)遍歷整個(gè)view層次(包括superviews)請(qǐng)求layout。
因此生音,調(diào)用此方法會(huì)強(qiáng)制整個(gè)view層次布局宁否。
layoutIfNeeded不一定會(huì)調(diào)用layoutSubviews方法。
setNeedsLayout一定會(huì)調(diào)用layoutSubviews方法(有延遲缀遍,在下一輪runloop結(jié)束前)慕匠。
如果想在當(dāng)前runloop中立即刷新,調(diào)用順序應(yīng)該是
[self setNeedsLayout];
[self layoutIfNeeded];
最好組合使用
layoutSubviews:
在layoutSubviews方法里對(duì)subviews重新布局域醇。比如台谊,我們想更新子視圖的位置的時(shí)候,可以通過調(diào)用layoutSubviews方法譬挚,即可以實(shí)現(xiàn)對(duì)子視圖重新布局锅铅。
layoutSubviews默認(rèn)是不做任何事情的,用到的時(shí)候减宣,需要在子類進(jìn)行重寫盐须。
這個(gè)方法不能直接調(diào)用
1. init初始化不會(huì)觸發(fā)layoutSubviews
2. addSubview會(huì)觸發(fā)layoutSubviews錢,前提是frame有值
3. 設(shè)置view的Frame會(huì)觸發(fā)layoutSubviews漆腌,前提是frame的值設(shè)置前后發(fā)生了變化
4. 滾動(dòng)一個(gè)UIScrollView會(huì)觸發(fā)layoutSubviews
5. 旋轉(zhuǎn)Screen會(huì)觸發(fā)父UIView上的layoutSubviews事件
6. 改變一個(gè)UIView大小的時(shí)候也會(huì)觸發(fā)父UIView上的layoutSubviews事件
子類可以根據(jù)需要重寫此方法贼邓,以執(zhí)行更精確的子視圖布局。
只有當(dāng)子視圖的自動(dòng)調(diào)整闷尿、約束的行為不能滿足你時(shí)立帖,你才應(yīng)該重寫此方法
基于約束的AutoLayer的方法:
setNeedsUpdateConstraints:
控制 view 的約束是否需要更新。當(dāng)一個(gè)自定義view的某個(gè)屬性發(fā)生改變悠砚,并且可能影響到constraint時(shí)晓勇,
需要調(diào)用此方法去標(biāo)記constraints需要在未來的某個(gè)點(diǎn)更新,系統(tǒng)然后會(huì)調(diào)用 updateConstraints灌旧,
以解決這個(gè)由屬性改變帶來的影響绑咱。
updateConstraintsIfNeeded:
立即觸發(fā)約束更新,自動(dòng)更新布局枢泰。
updateConstraints:
自定義view應(yīng)該重寫此方法在其中建立constraints.
注意:要在實(shí)現(xiàn)在最后調(diào)用[super updateConstraints]
viewDidLayoutSubviews:
控制器的view布局子控件完成
awakeFromNib:
從xib或者storyboard加載完畢就會(huì)調(diào)用
initWithCoder:
只要對(duì)象是從文件解析來的描融,就會(huì)調(diào)用
awakeFromNib和initWithCoder同時(shí)存在會(huì)先調(diào)用initWithCoder:
awakeFromNib 相較于 initWithCoder 的優(yōu)勢(shì)是:當(dāng) awakeFromNib 執(zhí)行的時(shí)候,
各種 IBOutlet 也都連接好了衡蚂;而 initWithCoder 調(diào)用的時(shí)候窿克,雖然子視圖已經(jīng)被添加到視圖層級(jí)中骏庸,但是還沒有引用。
如果你是基于 xib 或 storyboard 創(chuàng)建的控件年叮,那么你可能需要對(duì) IBOutlet 連接的子控件進(jìn)行初始化工作具被,
這種情況下,你只能在 awakeFromNib 里進(jìn)行處理只损。
只要跟xib&storyBoard有關(guān) 就會(huì)調(diào)這兩個(gè)方法
drawRect:
drawRect是對(duì)receiver的重繪一姿,能獲得context;
layoutSubviews方法調(diào)用先于drawRect跃惫;
一般用于繪圖操作
如果在UIView初始化時(shí)沒有設(shè)置rect大小叮叹,將直接導(dǎo)致drawRect不被自動(dòng)調(diào)用。
1. 該方法在調(diào)用sizeThatFits后被調(diào)用爆存,所以可以先調(diào)用sizeToFit計(jì)算出size蛉顽。
2. 然后系統(tǒng)自動(dòng)調(diào)用drawRect:方法。
3. 通過設(shè)置contentMode屬性值為UIViewContentModeRedraw先较。那么將在每次設(shè)置或更改frame的時(shí)候自動(dòng)調(diào)用drawRect:携冤。
4. 直接調(diào)用setNeedsDisplay,或者setNeedsDisplayInRect:觸發(fā)drawRect:拇泣,但是有個(gè)前提條件是rect不能為0.
xib&storyboard、masonry設(shè)置動(dòng)畫
- (void)updateViewConstraints {
//更新約束
if (self.bAnimaiton) {
self.viewLeft.constant = 100;
}else{
self.viewLeft.constant = 20;
}
[super updateViewConstraints];
}
- (IBAction)btnClick:(id)sender {
// 通知需要更新約束矮锈,但是不立即執(zhí)行
[self.view setNeedsUpdateConstraints];
// 立即更新約束霉翔,以執(zhí)行動(dòng)態(tài)變換
[self.view updateConstraintsIfNeeded];
// 執(zhí)行動(dòng)畫效果, 設(shè)置動(dòng)畫時(shí)間
[UIView animateWithDuration:0.4 animations:^{
[self.view layoutIfNeeded];
}];
self.bAnimaiton = !self.bAnimaiton;
}
實(shí)際使用中發(fā)小不調(diào)用setNeedsUpdateConstraints和updateConstraintsIfNeeded也可以,只不過需要在觸發(fā)事件中就更新約束
- (IBAction)btnClick:(id)sender {
self.bAnimaiton = !self.bAnimaiton;
if (self.bAnimaiton) {
self.viewLeft.constant = 100;
}else{
self.viewLeft.constant = 20;
}
[UIView animateWithDuration:0.4 animations:^{
// [self.view setNeedsLayout]; 最好配套使用
[self.view layoutIfNeeded];
}];
}
也可以在layoutSubview里處理約束
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
if (self.bAnimaiton) {
self.viewLeft.constant = 100;
}else{
self.viewLeft.constant = 20;
}
}
- (IBAction)btnClick:(id)sender {
[UIView animateWithDuration:0.4 animations:^{
[self.view setNeedsLayout];//不調(diào)用此方法不會(huì)調(diào)用layoutSubviews
[self.view layoutIfNeeded];
}];
self.bAnimaiton = !self.bAnimaiton;
}
以上三種辦法都可以處理masonry&xib&stroyboard的動(dòng)畫苞笨,推薦第二種方式债朵。
參考
Masonry
http://www.reibang.com/p/d46bcc656e04
http://www.reibang.com/p/a84f85729952
http://www.reibang.com/p/1d1a1165bb04
https://juejin.im/post/5a30f24bf265da432e5c0070