談?wù)?Autolayout

關(guān)于Autolayout的調(diào)試

剛開始使用 Autolayout 遇到下面的警告人容易讓人氣餒,經(jīng)常不知所措而放棄了使用 Autolayout缰犁。

Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:

(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
(...........)

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

正如輸出中所述,Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger,現(xiàn)在介紹下使用 UIViewAlertForUnsatisfiableConstraints 的調(diào)試方法。

UIViewAlertForUnsatisfiableConstraints 添加 symbolic breakpoint

  • 打開斷點(diǎn)導(dǎo)航(cmd+7)
  • 點(diǎn)擊左下角的+按鈕
  • 選擇Add Symbolic Breakpoint
  • 在Symbol添加UIViewAlertForUnsatisfiableConstraints
image.png

再次調(diào)試的時候就可以通過 lldb 來調(diào)試了辨绊,然并卵,如果你不知道 lldb 的話匹表。

所以交給你一個小技巧门坷,添加

po [[UIWindow keyWindow] _autolayoutTrace] // OC項目
expr -l objc++ -O -- [[UIWindow keyWindow] _autolayoutTrace] // Swift項目
image.png

這樣就可以直接看到輸出:

(lldb) po [[UIWindow keyWindow] _autolayoutTrace]
UIWindow:0x7f9481c93360
|   ?UIView:0x7f9481c9d680
|   |   *UIView:0x7f9481c9d990- AMBIGUOUS LAYOUT for UIView:0x7f9481c9d990.minX{id: 13}, UIView:0x7f9481c9d990.minY{id: 16}
|   |   *_UILayoutGuide:0x7f9481c9e160- AMBIGUOUS LAYOUT for _UILayoutGuide:0x7f9481c9e160.minY{id: 17}
|   |   *_UILayoutGuide:0x7f9481c9ebb0- AMBIGUOUS LAYOUT for _UILayoutGuide:0x7f9481c9ebb0.minY{id: 27}

其中 AMBIGUOUS 相關(guān)的視圖就是約束有問題的。0x7f9481c9d990就是有問題視圖的首地址袍镀。

當(dāng)然進(jìn)一步的調(diào)試需要 lldb 的命令默蚌。比如,打印視圖對象

(lldb) po 0x7f9481c9d990
<UIView: 0x7f9481c9d990; frame = (0 0; 768 359); autoresize = RM+BM; layer = <CALayer: 0x7fc82d338960>>

改變顏色:

(lldb) expr ((UIView *)0x174197010).backgroundColor = [UIColor redColor]
(UICachedDeviceRGBColor *) $4 = 0x0000000174469cc0

剩下的就是去代碼中找到這個視圖流椒,然后修改其約束了敏簿。

AutoLayout 關(guān)于 update 的幾個方法

UIView 是我們經(jīng)常使用的一個基本控件明也,其中有幾個基本的布局方法需要清楚宣虾。

  • layoutSubViews:

當(dāng) View 及其所有子視圖的 frame 發(fā)生改變的時候,會調(diào)用 layoutSubviews温数,所以在需要更新 frame 來重新定位或更改大小時重載它绣硝。這個方法很開銷很大,因?yàn)樗鼤诿總€子視圖上起作用并且調(diào)用它們相應(yīng)的 layoutSubviews 方法撑刺。注意:最好不要在代碼中手動調(diào)用 layoutSubviews 方法鹉胖。當(dāng) layoutSubviews 完成后,在 view 的所有者 view controller 上够傍,會觸發(fā) viewDidLayoutSubviews 調(diào)用甫菠。因?yàn)?viewDidLayoutSubviewsview 布局更新后會被唯一可靠調(diào)用的方法,所以你應(yīng)該把所有依賴于布局或者大小的代碼放在 viewDidLayoutSubviews 中冕屯,而不是放在 viewDidLoad 或者 viewDidAppear 中寂诱。

觸發(fā) layoutSubviews 的時機(jī):

  • addSubview 方法會觸發(fā) layoutSubviews

  • 當(dāng) viewFrame 發(fā)生變化也會觸發(fā)layoutSubviews安聘。

  • 滾動一個 UIScrollView 會觸發(fā) layoutSubviews痰洒。

  • 旋轉(zhuǎn)屏幕會觸發(fā)父 View 上的 layoutSubviews瓢棒。

  • 改變一個 View 大小的時候也會觸發(fā)父 View 上的 layoutSubviews

  • setNeedsLayout
    觸發(fā) layoutSubviews 調(diào)用的最省資源的方法就是在你的視圖上調(diào)用 setNeedsLaylout 方法丘喻,表示視圖的布局需要重新計算脯宿。告知頁面需要更新,但是不會立刻開始更新視圖泉粉,視圖會在下一個 runloop 中更新连霉,調(diào)用 setNeedsLaylout 方法視圖被重新繪制并布局之間會有一段任意時間的間隔。

  • layoutIfNeeded
    調(diào)用 layoutIfNeeded 會觸發(fā) layoutSubviews嗡靡,告知頁面布局立刻更新窘面,所以一般都會和 setNeedsLayout 一起使用。如果希望立刻生成新的 frame 需要調(diào)用此方法叽躯,利用這點(diǎn)一般布局動畫可以在更新布局后直接使用這個方法讓動畫生效财边。

  • setNeedsUpdateConstraints
    告知需要更新約束,但是不會立刻開始点骑,在下一次 runloop 中更新約束酣难,通過標(biāo)記 update constraints 來觸發(fā) updateConstraints

  • updateConstraintsIfNeeded
    告知立刻更新約束黑滴,這個方法與 layoutIfNeeded 等價憨募。它會檢查 update constraints 標(biāo)記。如果認(rèn)為這些約束需要被更新袁辈,它會立即觸發(fā) updateConstraints菜谣,而不會等到 run loop 的末尾。

  • updateConstraints
    系統(tǒng)更新約束晚缩,注意:最好不要在代碼中手動調(diào)用 updateConstraints 方法尾膊。通常在 updateConstraints 方法中實(shí)現(xiàn)必須要更新的約束,在設(shè)置或者解除約束荞彼、更改約束的優(yōu)先級或者常量值冈敛,或者從視圖層級中移除一個視圖時都會設(shè)置一個內(nèi)部的標(biāo)記 update constarints,這個標(biāo)記會在下一個更新周期中觸發(fā)調(diào)用 updateConstrains鸣皂。

注意:layoutSubViews 在 drawRect 之前調(diào)用抓谴。

AutoLayout 與 Frame

在使用 AutoLayout 的時候可能也會同時也會用到 frame,比如需要用到 layer 的時候寞缝,想讓 layer 的尺寸是由其它視圖尺寸設(shè)定的癌压,而這個視圖又是由約束控制布局的,如果將 layer 的初始化與 view 的初始化放在一個方法中荆陆;
比如:

layer.bounds = CGRectMake(0,0,view.bounds.size.widith * 0.5,50)

那么很可能拿到 layer 的寬度是0滩届。

比如:

UIView *redView = [[UIView alloc] init];
redView.backgroundColor = [UIColor redColor];
[self.view addSubview:redView];
self.redView = redView;
    
// 設(shè)置約束
[redView mas_makeConstraints:^(MASConstraintMaker *make) {
  make.centerX.equalTo(self.view.mas_centerX);
  make.centerY.equalTo(self.view.mas_centerY);
  make.size.mas_equalTo(CGSizeMake(150, 80));
}];
    
NSLog(@"self.view 的尺寸%@,redView 的尺寸%@",self.view,redView);
2017-06-08 15:32:51.815107+0800 MasonryDemo[42940:1076244] self.view 的尺寸<UIView: 0x7fd8cd408960; frame = (0 0; 414 736); autoresize = W+H; layer = <CALayer: 0x604000227200>>慎宾,redView 的尺寸<UIView: 0x7fd8cd407650; frame = (0 0; 0 0); layer = <CALayer: 0x6040002274a0>>

這個時候丐吓,看到為什么設(shè)置了約束浅悉,而打印出來的 frame 是 (0 0; 0 0),是因?yàn)榧s束被設(shè)置之后它并不會立即對 view 作出改變券犁,而是要等到 layout 時术健,才會對視圖的尺寸進(jìn)行修改,而 layout 通常是在視圖已經(jīng)加載到父視圖上面時做出響應(yīng)粘衬。

所以如果在 viewDidLoad 中設(shè)置了約束荞估,那么要等到 viewDidAppear 時 view 的尺寸才會真正改變。

解決辦法:

- (void)testLayout {
    
    UIView *redView = [[UIView alloc] init];
    redView.backgroundColor = [UIColor redColor];
    [self.view addSubview:redView];
    self.redView = redView;
    
    // 設(shè)置約束
    [redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerX.equalTo(self.view.mas_centerX);
        make.centerY.equalTo(self.view.mas_centerY);
        make.size.mas_equalTo(CGSizeMake(150, 80));
    }];
}

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];
    
    NSLog(@"self.view 的尺寸%@稚新,redView 的尺寸%@",self.view,self.redView);
}

2017-06-08 15:50:41.621147+0800 MasonryDemo[43363:1089098] self.view 的尺寸<UIView: 0x7fe412f0f780; frame = (0 0; 414 736); autoresize = W+H; layer = <CALayer: 0x604000238b00>>勘伺,redView 的尺寸<UIView: 0x7fe412e045b0; frame = (132 328; 150 80); layer = <CALayer: 0x60000003c460>>

1、把獲取 frame 的設(shè)置寫到 layoutSubviews 中或者寫到 viewDidLayoutSubviews 中即可褂删。因?yàn)?layout 約束生效時 view 的 center 或者 bounds 就會被修改飞醉,當(dāng) center 或者 bounds 被修改時layoutSubview 就會被調(diào)用,隨后 viewDidLayoutSubviews 就回被調(diào)用屯阀。這個時候缅帘,設(shè)置約束的視圖 frame 就不再是 (0,0,0,0) 了。

- (void)testLayout {
    
    UIView *redView = [[UIView alloc] init];
    redView.backgroundColor = [UIColor redColor];
    [self.view addSubview:redView];
    self.redView = redView;
    
    // 設(shè)置約束
    [redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerX.equalTo(self.view.mas_centerX);
        make.centerY.equalTo(self.view.mas_centerY);
        make.size.mas_equalTo(CGSizeMake(150, 80));
    }];
    [redView setNeedsLayout];
    [redView layoutIfNeeded];
    NSLog(@"self.view 的尺寸%@难衰,redView 的尺寸%@",self.view,redView);
}
2017-06-08 15:52:32.749105+0800 MasonryDemo[43419:1090641] self.view 的尺寸<UIView: 0x7fe36440b5f0; frame = (0 0; 414 736); autoresize = W+H; layer = <CALayer: 0x604000422100>>钦无,redView 的尺寸<UIView: 0x7fe364405040; frame = (-75 -40; 150 80); layer = <CALayer: 0x6040004207a0>>

2、如果將約束和 frame 寫在同一方法中盖袭,寫完約束就設(shè)置 frame失暂,而不是想把 frame 的設(shè)置放到 layoutSubview 中,比如設(shè)置好約束后馬上就想根據(jù)約束的結(jié)果計算高度鳄虱,那么必須在設(shè)置完約束之后手動調(diào)用
setNeedsLayout 和 layoutIfNeeded 方法弟塞,讓視圖立即 layout,更新 frame醇蝴,但是這個時候就可以拿到真實(shí)的 size 并不能拿到真實(shí)的 center 宣肚,不建議這么使用想罕。

- (void)testLayout {
    
    UIView *redView = [[UIView alloc] init];
    redView.backgroundColor = [UIColor redColor];
    [self.view addSubview:redView];
    self.redView = redView;
    
    // 設(shè)置約束
    [redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerX.equalTo(self.view.mas_centerX);
        make.centerY.equalTo(self.view.mas_centerY);
        make.size.mas_equalTo(CGSizeMake(150, 80));
    }];
   
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"self.view 的尺寸%@悠栓,redView 的尺寸%@",self.view,redView);
    });
}
2017-06-08 15:55:56.282546+0800 MasonryDemo[43500:1092911] self.view 的尺寸<UIView: 0x7fda85e0d540; frame = (0 0; 414 736); autoresize = W+H; layer = <CALayer: 0x600000233620>>,redView 的尺寸<UIView: 0x7fda85e0c770; frame = (132 328; 150 80); layer = <CALayer: 0x600000233540>>

3按价、在 dispatch_after 里面可以拿到真實(shí)的 frame 惭适,或許是因?yàn)樵O(shè)置約束和獲取 frame 不在同一個 runloop 的原因吧。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末楼镐,一起剝皮案震驚了整個濱河市癞志,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌罐寨,老刑警劉巖会傲,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異疙剑,居然都是意外死亡戒突,警方通過查閱死者的電腦和手機(jī)屯碴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來膊存,“玉大人导而,你說我怎么就攤上這事「羝椋” “怎么了今艺?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長爵卒。 經(jīng)常有香客問我虚缎,道長,這世上最難降的妖魔是什么钓株? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任遥巴,我火速辦了婚禮,結(jié)果婚禮上享幽,老公的妹妹穿的比我還像新娘铲掐。我一直安慰自己,他們只是感情好值桩,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布摆霉。 她就那樣靜靜地躺著,像睡著了一般奔坟。 火紅的嫁衣襯著肌膚如雪携栋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天咳秉,我揣著相機(jī)與錄音婉支,去河邊找鬼。 笑死澜建,一個胖子當(dāng)著我的面吹牛向挖,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播炕舵,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼何之,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了咽筋?” 一聲冷哼從身側(cè)響起溶推,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蒜危,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體虱痕,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年辐赞,在試婚紗的時候發(fā)現(xiàn)自己被綠了皆疹。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡占拍,死狀恐怖略就,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情晃酒,我是刑警寧澤表牢,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站贝次,受9級特大地震影響崔兴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蛔翅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一敲茄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧山析,春花似錦堰燎、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至爵政,卻和暖如春仅讽,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背钾挟。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工洁灵, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人掺出。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓徽千,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蛛砰。 傳聞我的和親對象是個殘疾皇子罐栈,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容