Autolayout的第一次親密接觸

項目里的布局一直都是純代碼流帘撰,順帶著Autolayout也一直沒有使用,直到遇到了masonry万皿,讓我看到了希望摧找,我決定將Autolayout引入到項目中。masonry的基本用法網上已經很多了牢硅,我就先不具體介紹了蹬耘。大家如果需要了解,可以去看看masonry的demo或者里脊串的Masonry介紹與使用實踐(快速上手Autolayout)减余。
masonry只是給Autolayout披了一層華麗的外衣综苔,讓他更好用了,但真正實現(xiàn)布局的還是Autolayout,文章主要介紹Autolayout如筛,部分code用的是masonry堡牡,但應該不會影響理解

初識Autolayout

iOS中,會將View的布局定義為一系列的線性方程杨刨,存放在UIView的屬性中晤柄。在View布局的時候,通過這些方程式妖胀,計算出每一個view的frame來完成布局芥颈。這就是Autolayout。

Constraint

Autolayout將所有的方程式用constraint表示赚抡,存放在View的屬性constraints下

@property(nonatomic,readonly) NSArray<__kindof NSLayoutConstraint *> *constraints NS_AVAILABLE_IOS(6_0);

每一個constraint爬坑,表示一個相等或不等(大于小于)關系



上面的這個constraint表示紅色view的頭部距離藍色view的尾部8個點。
Item: 一般的item都是View
Multiplier: 系數(shù)涂臣,一般對寬高的屬性用得比較多
Constant: 常量妇垢,一般為設置距離,大小什么的
Relationship: 關系肉康,上圖中表示的是相等關系,除此之外也可以用不等關系表示灼舍,例如:>=, <=
Attribute: 屬性吼和,iOS中主要的屬性有上,下骑素,左炫乓,右,前献丑,后末捣,寬,高创橄,Y軸中心箩做,X軸中心。

NSLayoutAttributeLeft,      //左邊
NSLayoutAttributeRight,     //右邊
NSLayoutAttributeTop,       //上邊    
NSLayoutAttributeBottom,    //下面
NSLayoutAttributeLeading,   //前面
NSLayoutAttributeTrailing,  //后面
NSLayoutAttributeWidth,     //寬度
NSLayoutAttributeHeight,    //高度
NSLayoutAttributeCenterX,   //X軸中心
NSLayoutAttributeCenterY,   //Y軸中心

NSLayoutAttributeLeading和NSLayoutAttributeTrailing可能不太好理解妥畏。
一般正常情況下邦邦,我們的文字順序是從左到由,所以Label的Leading=Left醉蚁,Trailing=Right燃辖,但是如果有的語言,文字的順序是從右往左(傳說古代的文字順序就是從右往左),那么就是Leading=Right网棍,Trailing=Left黔龟。

由于國際化的關系,Apple推薦使用Leading和Trailing代替Left和Right。但是個人感覺Left和Right比較好理解氏身,而且項目支持的文字也都是從左到右的巍棱,所以Left和Right反而用的比較多

IntrinsicContentSize

使用Autolayout之后,一個比較爽的地方就是UIlabel观谦,UIButton, UIImageView有了IntrinsicContentSize的屬性拉盾。他們可以自己根據(jù)內容調整大小,再也不用量寬和高了豁状。設置好位置之后捉偏,就讓他們自己浪吧,文字有多長就顯示多長泻红,圖片有多大夭禽,就顯示多大,真是Very Nice~~
對于哪些View有IntrinsicContentSize谊路,Apple給了一張表:


  1. UIView和NSView是沒有IntrinsicContentSize的讹躯。
  2. Sliders只有with有這個屬性。 Sliders只能定義width缠劝。Sliders的height擁有IntrinsicContentSize(感謝@凸小布潮梯,發(fā)現(xiàn)了這個問題)
  3. Labels, buttons, switches, text fields比較棒,屬性完美支持
  4. Text views和image views也挺好惨恭,在有內容的時候支持秉馏,沒有內容的時候不支持。這也正是我們想要的

從上面我們可以看到UIView是沒有IntrinsicContentSize的脱羡,如果我們自定義一個View萝究,想要他擁有默認寬高,只需要重寫-IntrinsicContentSize方法锉罐,即可讓其擁有默認的寬高帆竹。
- (CGSize)intrinsicContentSize
{
return CGSizeMake(100, 150);
}

由于View只有被addSubview之后才能設置約束,所以一直在為怎么讓自定義的View擁有默認Size而煩惱脓规。重寫intrinsicContentSize可能是最好的讓其擁有默認Size的方法了栽连,感謝@里脊串的指點

對于IntrinsicContentSize,Autolayout又把他分成了2個部分:ContentHugging和CompressionResistance:


  1. ContentHugging我翻譯過來是內容凝聚力侨舆,表示View的寬度和高度緊靠內容升酣,不讓其擴展的力量
  2. CompressionResistance是指壓縮阻力,表示當有力量要對其進行壓縮的時候态罪,其阻力的大小

對于同一個View噩茄,ContentHugging和CompressionResistance不會同時起作用绩聘。當一個Label有文字的時候,label會存在一個內容的Size椒拗。
如果有外力讓其size擴張玷氏,ContentHugging會起作用渗蟹,外力大于ContentHugging的力量辨嗽,label的size由外力決定来破,反之,label的Size由內容決定。
如果有外力讓其size壓縮,CompressionResistance會起作用,外力大于CompressionResistance的力量轻要,label的size由外力決定壁涎,反之,label的Size由內容決定。

Priorities

各個約束力量的大小,由constraint的優(yōu)先級(Priorities)決定,優(yōu)先級越高,力量越大。系統(tǒng)的優(yōu)先級由1~1000的數(shù)字表示,值越大,優(yōu)先級越高磕蛇。NSLayoutConstraint中一共定義了4種比較常用的優(yōu)先級

typedef float UILayoutPriority;
static const UILayoutPriority UILayoutPriorityRequired NS_AVAILABLE_IOS(6_0) = 1000; // A required constraint.  Do not exceed this.
static const UILayoutPriority UILayoutPriorityDefaultHigh NS_AVAILABLE_IOS(6_0) = 750; // This is the priority level with which a button resists compressing its content.
static const UILayoutPriority UILayoutPriorityDefaultLow NS_AVAILABLE_IOS(6_0) = 250; // This is the priority level at which a button hugs its contents horizontally.
static const UILayoutPriority UILayoutPriorityFittingSizeLevel NS_AVAILABLE_IOS(6_0) = 50; // When you send -[UIView systemLayoutSizeFittingSize:], the size fitting most closely to the target size (the argument) is computed.  UILayoutPriorityFittingSizeLevel is the priority level with which the view wants to conform to the target size in that computation.  It's quite low.  It is generally not appropriate to make a constraint at exactly this priority.  You want to be higher or lower.
  1. UILayoutPriorityRequired: 必須級別優(yōu)先級说搅,值為最高值1000,一般平時定義約束澄干,默認都是這個優(yōu)先級。
  2. UILayoutPriorityDefaultHigh: 高優(yōu)先級拜效,值為750稻励,CompressionResistance的默認優(yōu)先級是這個履婉。
  3. UILayoutPriorityDefaultLow: 低優(yōu)先級鸠窗,值為250净刮,ContentHugging的默認優(yōu)先級是這個
  4. UILayoutPriorityFittingSizeLevel: 極低的優(yōu)先級暑认,讓系統(tǒng)估算Size的時候使用,不適合做約束

知道了各個屬性的默認優(yōu)先級之后乍迄,就可以解釋為什么一般情況我們給Lable設置Size約束之后,Label由我們設置的Size決定谅将,而不是由其內容決定漾狼。因為我們沒有特意設置優(yōu)先級,用的都是默認優(yōu)先級饥臂。Size約束的優(yōu)先級比CompressionResistance和ContentHugging的優(yōu)先級高逊躁。如果我們想讓Label由內容決定,我們可以不設置Size約束或者調低自己Size約束的優(yōu)先級隅熙。

有了優(yōu)先級之后稽煤,我們就可以處理很多復雜情況了核芽。比如2個Label排列在一起,寬度都由內容決定酵熙,父view寬度不夠的時候轧简,我們需要優(yōu)先顯示某個Label的內容。這時候我們就可以設置2個Label的CompressionResistance優(yōu)先級匾二,
優(yōu)先級高的Label吉懊,會優(yōu)先顯示~~~
更多例子可以看土土哥的有趣的Autolayout示例-Masonry實現(xiàn)

IntrinsicContentSize舉例(12月6日新增)

由于IntrinsicContentSize的ContentHugging和CompressionResistance比較抽象,很多人沒怎么看明白假勿。所以舉幾個例子借嗽,幫助大家理解。(感謝@里脊串的提醒)
假設有2個Label转培,并列放著恶导,他們都是使用IntrinsicContentSize自動根據(jù)文字適應寬度。效果如圖所示:



一浸须、那么我們設置一個優(yōu)先級為500寬度為100的約束(100小于Label2的寬度惨寿,大于Label1的寬度)

[@[label1,label2] mas_makeConstraints:^(MASConstraintMaker *make) {
    make.width.equalTo(@100).priority(500);
}];

大家猜猜,會怎么樣删窒?是2個Label都變成100的寬度裂垦,還是都保持原來的寬度不變?還是一個變成100肌索,一個保持原來的寬度蕉拢?
我們Run一下:



咦!Label1變成了100诚亚,Label2還是原來的寬度晕换,為什么呢?

  1. Label1的IntrinsicContentSize寬度比100小站宗,所以當添加一個寬度為100的約束時闸准,ContentHugging在起作用。ContentHugging的優(yōu)先級為250梢灭。寬度為100的約束優(yōu)先級為500大于ContentHugging夷家。所以寬度為100.
  2. Label2的IntrinsicContentSize寬度比100大。所以當添加一個寬度為100的約束時敏释,CompressionResistance在起作用库快,CompressionResistance的優(yōu)先級為750。寬度為100的約束優(yōu)先級為500小于CompressionResistance颂暇。所以寬度還是IntrinsicContentSize的寬度缺谴。

根據(jù)這個例子但惶,大家應該能明白ContentHugging和CompressionResistance是什么意思了吧耳鸯。這里留個問題給大家湿蛔,如果設置的不是寬度為100,而是Label1寬度等于Label2寬度县爬,那么會出現(xiàn)什么情況阳啥?是都變成Label1的寬度了,還是都變成Label2的寬度了财喳?還是不變察迟?

調教Autolayout

Autolayout的動態(tài)布局雖然感覺很酷炫,但是真正用起來可能會遇到各種問題:動不動就拋了個異常耳高,一不小心就布局沖突了扎瓶。布局完成之后,突然有個View不見了泌枪,View的位置完全不正確等等概荷。這一點也正是一直被人嫌棄的地方。
Autolayout就像一個長得漂亮但性格暴躁的姑娘碌燕,需要我們好好調教误证,才能成為一個合格的女票~~

Log

寫布局的時候我們經常會遇到布局沖突,一般沖突都會有拋出log修壕。
下面我們是以Masonry為例看看Log愈捅,masonry重寫了constriant的-description方法,讓log更易懂了慈鸠。
首先我們看來一段代碼

UIView *view = ({
    UIView *view = [UIView new];
    view.backgroundColor = [UIColor grayColor];
    [self.view addSubview:view];
    [view mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.insets(UIEdgeInsetsMake(100, 100, 100, 100));
        make.width.equalTo(@300);
    }];
    view;
});
MASAttachKeys(view);

一個View被加在了self.view上蓝谨,他的上下左右都距離父View100,然后我們又讓他的寬度等于300青团。我們運行一下看看~

2015-12-01 10:04:25.558 TAutolayout[1034:32111] 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. 
(
    "<MASLayoutConstraint:0x7f8fe9e251c0 UIView:view.left == UIView:self.view.left + 100>",
    "<MASLayoutConstraint:0x7f8fe9e23630 UIView:view.right == UIView:self.view.right - 100>",
    "<MASLayoutConstraint:0x7f8fe9e1c430 UIView:view.width == 300>",
    "<NSLayoutConstraint:0x7f8fe9d306c0 UIView:self.view.width == 375>"
)

Will attempt to recover by breaking constraint 
<MASLayoutConstraint:0x7f8fe9e1c430 UIView:view.width == 300>

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.

唉媽呀像棘,出現(xiàn)了這么大一堆log。
Log說壶冒,我們不能同時滿足下面的約束缕题,你寫的這些約束里面肯定有某個約束有問題,你好好改改:

"<MASLayoutConstraint:0x7f8fe9e251c0 UIView:view.left == UIView:self.view.left + 100>",
"<MASLayoutConstraint:0x7f8fe9e23630 UIView:view.right == UIView:self.view.right - 100>",
"<MASLayoutConstraint:0x7f8fe9e1c430 UIView:view.width == 300>",
"<NSLayoutConstraint:0x7f8fe9d306c0 UIView:self.view.width == 375>"

我們這次的運行結果是把這條約束干掉了:

<MASLayoutConstraint:0x7f8fe9e1c430 UIView:view.width == 300>

根據(jù)Log的信息胖腾,肯定是約束有地方沖突了烟零,而且重點還是寬度相關的約束,因為運行的時候把UIView:view.width == 300干掉了咸作。讓我們來看看這些約束:
view的左邊 = self.view左邊+100
view的右邊 = self.view右邊-100
view的寬度 = 300
self.view的寬度 = 375
根據(jù)上面的約束關系锨阿,view的左邊右邊的都是參照self.view的,那么view的寬度應該是375(self.view寬度) - 100(左邊) -100(右邊) = 175墅诡。而我們又給view的寬度賦值了300桐智。所以這個地方沖突了烟馅。
Ok,我們把make.width.equalTo(@300);這句話干掉郑趁,再次運行一下姿搜。Nice寡润,已經沒有沖突了~

Visualizing Views and Constraints

沖突問題根據(jù)log搞定了,不過你以為這樣就完事了么舅柜?那就too young too simple了致份。有的時候我們寫完布局知举,以為一切ok了,一運行逛钻,唉媽呀锰提,咋這樣啦立肘?也許是view不見了谅年,也許是view布局不對了,反正有可能是各種摸不著頭腦的問題旺订,感覺明明是對的区拳,一運行就錯了樱调。
對于這一類布局和期望不一致的問題。我們還有一個大招圣猎,使用Xcode查看布局的工具
我們想要一個redView整以,在self.view里面包著寇漫,并且距離self.view的邊框上下左右均為100阵难。還有一個button鞋喇,靠著self.view左邊侦香。
于是我們寫下了代碼:

UIView *redView = ({
    UIView *view = [UIView new];
    view.backgroundColor = [UIColor redColor];
    [self.view addSubview:view];
    [view mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.right.top.bottom.offset(100);
    }];
    view;
});

UIButton *button = ({
    UIButton *button = [UIButton new];
    [self.view addSubview:button];
    [button setTitle:@"button" forState:UIControlStateNormal];
    [button mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.offset(0);
        make.top.equalTo(redView);
        make.size.mas_equalTo(CGSizeMake(100, 50));
    }];
    button;
});
MASAttachKeys(redView,button);

運行一下罐韩,看看效果:


咦散吵,咋這樣了呢矾睦?按鈕去哪兒了枚冗?redview也沒有居中?檢查一下代碼....沒問題呀8厮巍T统隆3涟铩穆壕!難道是是手機出bug了?要不要摔一下試試缨该?
還是不要輕易摔手機贰拿,我們來看看View的布局膨更。點擊查看View層級的按鈕缴允。(Xcode下方练般,代碼和控制臺的中間)



出來了view的層級視圖薄料,大家可以看到都办,這個地方就是我們的button琳钉,位置并沒有錯



點擊一下歌懒,我們還可以看到他的文字及皂。哎呀,原來是我們忘了給他的文字調整顏色了板驳,背景是白色若治,文字也是白色端幼,所以被"隱藏"了婆跑。
那我們的redView是怎么回事呢?
點擊一下工具欄最左邊的按鈕滑进,下圖紅色框起來的就是工具欄郊供。

這個按鈕會顯示出超出屏幕外的視圖



我們看到,原來他們的相對位置被設置成這樣了疯淫,那到底是哪里的設置出了問題呢戳玫?
點擊工具欄左邊第二個按鈕咕宿,這個按鈕可以顯示出布局的約束府阀。

我們可以看到:
self.right = superview.right + 100
self.bottom = superview.bottom +100
原來+100是往右往下试浙,我們要讓redView被self.view包著田巴,并距離left壹哺,bottom為100,需要用-100管宵。
ok,讓我們改改代碼:
UIView *redView = ({
    UIView *view = [UIView new];
    view.backgroundColor = [UIColor redColor];
    [self.view addSubview:view];
    [view mas_makeConstraints:^(MASConstraintMaker *make) {
        /********修改*********/
        make.left.top.offset(100);
        make.bottom.right.offset(-100);
        /*****************/
    }];
    view;
});

UIButton *button = ({
    UIButton *button = [UIButton new];
    [self.view addSubview:button];
    [button setTitle:@"button" forState:UIControlStateNormal];
    /********修改*********/
    [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    /*****************/
    [button mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.offset(0);
        make.top.equalTo(redView);
        make.size.mas_equalTo(CGSizeMake(100, 50));
    }];
    button;
});
MASAttachKeys(redView,button);

運行一下看看:



very Good云稚! 跟我們期望一致了

Autolayout深層次的探索

Autolayout什么時候計算frame

根據(jù)最初的介紹静陈,Autolayout是在設置constraint的時候鲸拥,將constraints存放在View的屬性中刑赶,在真正布局的時候去計算出view的frame撞叨,完成布局牵敷。那么他到底是在哪個方法中進行計算的呢法希?
我們知道苫亦,計算出來結果之后屋剑,必定會改變View的位置饼丘。由于Autolayout不通過frame布局肄鸽,而是直接設置center和bounds典徘。我們給-setCenter:打一個斷點逮诲。



通過斷點,我們可以看到在layoutSubview的時候冗锁,如果使用了約束會調用_updateConstraintsAsNecessaryAndApplyLayoutFromEngine嗤栓。在這個方法里面茉帅,系統(tǒng)會先看看是否需要更新約束堪澎,如果需要樱蛤,則調用-updateConstraints更新約束刹悴。
跟新結束之后土匀,會調用到_resizeWithOldSuperviewSize:就轧,根據(jù)這個方法名妒御,我們可以猜到乎莉,是在這個方法里面根據(jù)約束奸笤,計算出來布局的位置监右。
計算完成之后調用_applyISEngineLayoutValues健盒。應用布局,調整center和bounds憨降。

Autolayout做動畫(12月3日新增)

對于Autolayout券册,有一個問題就是怎么做動畫烁焙?
使用Frame的時候骄蝇,我們做動畫一般都調用-animateWithDuration:animations:方法

[UIView animateWithDuration:1 animations:^{
    self.redView.frame = CGRectMake(0, 0, 100, 100);
}];

在animations的block里面調整Frame即可九火,使用Autolayout之后岔激,由于Autolayout是延遲布局的虑鼎,并不是約束更新之后就立刻布局炫彩,所以大家可以發(fā)現(xiàn)江兢。在-animateWithDuration:animations方法里面修改約束是不能實現(xiàn)動畫的杉允。

實現(xiàn)動畫的關鍵在于把更新Frame的操作在block中調用叔磷。根據(jù)前面我們知道世澜,Autolayout是在父view的-layoutSubview中更新Frame的寥裂。我們只需要讓父View的-layoutSubview方法在block中執(zhí)行即可封恰。

查閱文檔我們知道诺舔,iOS中不建議直接調用-layoutSubview低飒,如果要更新布局褥赊“韬恚可以調用-layoutIfNeeded尿背。調用-layoutIfNeeded之后田藐,會同步執(zhí)行-layoutSubview坞淮。
所以如果我們要做動畫可以用下面這種方法:

[self.redView mas_updateConstraints:^(MASConstraintMaker *make) {
    make.width.equalTo(@500);
}];
[UIView animateWithDuration:1 animations:^{
    [self.redView.superview layoutIfNeeded];
}];

當View約束發(fā)生變化時回窘,是怎么調整布局的

當一個view的約束發(fā)生變化的時候啡直,他又是怎么響應調整父view自身以及子view的布局的呢酒觅?
我們現(xiàn)在有3個view: view1, view2, view3



當因為某種原因,view2的約束發(fā)生了變化颜凯,我們來看看會發(fā)生什么:


  1. view2由于自身約束發(fā)生了改變症概,需要重新布局彼城。會調用父view:view1的setNeedLayout调炬。告訴View1筐眷,我需要重新布局了匀谣,趕緊調用layoutSubviews
  2. view1根據(jù)已有約束武翎,看看自身布局是否需要改變宝恶,如果需要改變垫毙,則繼續(xù)調用父view的setNeedLayout综芥。如果不需要改變膀藐,直接調用自己的layoutSubviews
  3. 在view1的layoutSubviews中额各,完成了view2的布局虾啦,這時候view2的布局發(fā)生了改變傲醉,繼續(xù)調用view2的layoutSubview
  4. 在view2的layoutSubview中丁眼,view3的布局沒有發(fā)生改變昭殉,所以不需要繼續(xù)調用layoutSubview苞七,結束

在Autolayout下使用Frame

在Autolayout下使用Frame分為2中情況

  1. Autolayout生效之前使用frame。這種情況比較常見挪丢,比如在viewDidLoad中對一個view添加了約束蹂风,之后又通過Frame調整他的位置。
    這種情況下乾蓬,通過Frame調整位置的代碼是無效的惠啄。因為在真正布局顯示到頻幕上的時候,系統(tǒng)會根據(jù)約束撵渡,重新計算Frame,之前設置的Frame會被沖掉
  2. Autolayout生效之后死嗦,使用frame趋距。這種情況稍微少一些,比如View之前就設置了約束越除,點擊某個按鈕节腐,需要改變View的Frame。這時候不使用約束摘盆,直接setFrame:
    這種情況下翼雀,setFrame:是可以生效的,不過由于是直接setFrame孩擂,不是根據(jù)約束計算的狼渊。所以他的子View,父View肋殴,以及同級的約束依賴的View囤锉,都不會跟著改變。而且如果他的superView被觸發(fā)了layoutSubviews护锤,又會自動根據(jù)約束設置成約束的Frame,后患無窮酿傍。所以一個View使用約束之后烙懦,強烈建議不要再對他使用frame。

第二種情況下赤炒,如果是需要做一個動畫氯析,動畫結束后亏较,又會恢復到原有位置⊙诨海可以使用Frame

Autolayout的小情緒

Autolayout雖然好用雪情,但是有的時候會有一些小情緒,特別在iOS6上你辣。那時候Autolayout還不完善巡通。

UITableView的layoutSubviews沒有調用[super layoutSubviews]

在iOS6上,UITableView的-layoutSubviews中沒有到調用UIView的layoutSubviews舍哄。
根據(jù)前面的介紹宴凉,Autolayout自動布局是在UIView的layoutSubviews中,所以TableView上的子view(如:cell表悬,headerView弥锄,footerView)使用了Autolayout,tableView在布局的時候調用layoutSubviews蟆沫,就會拋出異常籽暇。

2015-12-01 21:41:55.143 TAutolayout[9186:907] *** Assertion failure in -[UITableView layoutSublayersOfLayer:], /SourceCache/UIKit/UIKit-2372/UIView.m:5776
2015-12-01 21:41:55.145 TAutolayout[9186:907] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Auto Layout still required after executing -layoutSubviews. UITableView's implementation of -layoutSubviews needs to call super.'

注意:直接對TableView使用Autolayout是不會有問題的,TableView是否調用layoutSubviews在于他上面的子view是否使用Autolayout饭庞,而不是他本身图仓。詳細原因見當View約束發(fā)生變化時,是怎么調整布局的

解決方案:

如果是cell但绕,我們經常使用[cell addSubview:view]再對view做一個相對cell的約束救崔,這時候就會出現(xiàn)問題。解決方案就是使用[cell.contentView addSubview:view]捏顺。我們約束是對cell的contentView添加六孵,跟cell無關。tableView就不會調用layoutSubviews了

如果是headerView或者footView幅骄。解決方案是直接使用frame劫窒,或者自己定義一個類似Cell的contentView的view,子view相對contentView布局使用Autolayout拆座,contentView對headerView布局使用frame

ScrollView的相對布局

scrollView是一個特殊的View主巍,因為他除了位置大小之外還有content內容。scrollView的attribute分為2種:

  1. width挪凑,height孕索,center用來表示scrollView的frame。
  2. edge 和 margin用來表示scrollView的content

所以在scrollView布局的時候躏碳,如果想讓contentSize跟著里面的子view變化搞旭。一定要將edge設置完整。當然直接設置ContentSize也是可以的

不同性質的Attribute不能參照

Autolayout中,不同性質的Attribute是不能參照的肄渗,就像你不能設置View1的left距離view2的top為10像素镇眷。這明顯是不合適的,因為left和top是不能對比的翎嫡。
那么問題就來了欠动,哪些東西可以和哪些東西對比呢?我整理了一張表


View不在同一坐標系統(tǒng)下不能參照

所謂同一坐標系統(tǒng)惑申,是指他們是否能找到共同的父view(我們把父view的父view也稱為父View)具伍。
舉個例子:

self.view > viewA;
self.view > viewB > viewC > viewD

我們用>符號表示包含關系,viewA和ViewB都是self.View的子view硝桩。ViewC是ViewB的子View沿猜,ViewD是ViewC的子View。ViewA和ViewD是可以相互參照的碗脊,因為他們能找到共同的父View:self.View

在View沒有被addSubView之前啼肩。他是不能跟其他View做對比的。因為他跟任何的View(他自己的子view除外)都找不到共同的父view衙伶,也就是說他跟任何View都不在同一的坐標系統(tǒng)下祈坠。

何時使用updateConstraints

使用Autolayout之后,系統(tǒng)中多了一個更新約束的方法updateConstraints矢劲∩饩校看這個方法名,在自定義View的時候芬沉,是不是把約束相關的代碼放這里面會更好一些呢躺同?
2015年的WWDC技術講座Mysteries of Auto Layout (Part 2)給出了一些意見:

Really, all this is is a way for views to have a chance to make changes to constraints just in time for the next layout pass, but it’s often not actually needed.
All of your initial constraint setup should ideally happen inside Interface Builder. Or if you really find that you need to allocate your constraints programmatically, some place like viewDidLoad is much better. updateConstraints is really just for work that needs to be repeated periodically.
Also, it’s pretty straightforward to just change constraints when you find the need to do that; whereas, if you take that logic apart from the other code that’s related to it and you move it into a separate method that gets executed at a later time, your code becomes a lot harder to follow, so it will be harder for you to maintain, it will be a lot harder for other people to understand.
So when would you need to use updateConstraints? Well, it boils down to performance. If you find that just changing your constraints in place is too slow, then update constraints might be able to help you out. It turns out that changing a constraint inside updateConstraints is actually faster than changing a constraint at other times. The reason for that is because the engine is able to treat all the constraint changes that happen in this pass as a batch.

簡單總結一下就是:
初始化constraint的代碼放在viewDidLoad等初始化方法中更好。
updateConstraints方法僅用于提升性能丸逸。當你更新大量約束蹋艺,發(fā)現(xiàn)由于約束太多,布局有點卡黄刚。這時候你可以使用updateConstraints捎谨,因為在updateConstraints中更新約束會批量操作,能獲得更好的性能(一般不會遇到這種情況)
所以正常情況下憔维,我們直接在初始化的方法中寫約束就好涛救。詳細資料參考何時使用updateConstraints

Reference

Anatomy of a Constraint
Masonry
Masonry介紹與使用實踐(快速上手Autolayout)
有趣的Autolayout示例-Masonry實現(xiàn)
何時使用updateConstraints

About me

我的博客
我的微博

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市业扒,隨后出現(xiàn)的幾起案子检吆,更是在濱河造成了極大的恐慌,老刑警劉巖凶赁,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咧栗,死亡現(xiàn)場離奇詭異逆甜,居然都是意外死亡虱肄,警方通過查閱死者的電腦和手機致板,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來咏窿,“玉大人斟或,你說我怎么就攤上這事〖叮” “怎么了萝挤?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長根欧。 經常有香客問我怜珍,道長,這世上最難降的妖魔是什么凤粗? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任酥泛,我火速辦了婚禮,結果婚禮上嫌拣,老公的妹妹穿的比我還像新娘柔袁。我一直安慰自己,他們只是感情好异逐,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布捶索。 她就那樣靜靜地躺著,像睡著了一般灰瞻。 火紅的嫁衣襯著肌膚如雪腥例。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天酝润,我揣著相機與錄音燎竖,去河邊找鬼。 笑死袍祖,一個胖子當著我的面吹牛底瓣,可吹牛的內容都是我干的。 我是一名探鬼主播蕉陋,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼捐凭,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了凳鬓?” 一聲冷哼從身側響起茁肠,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎缩举,沒想到半個月后垦梆,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體匹颤,經...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年托猩,在試婚紗的時候發(fā)現(xiàn)自己被綠了印蓖。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡京腥,死狀恐怖赦肃,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情公浪,我是刑警寧澤他宛,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站欠气,受9級特大地震影響厅各,放射性物質發(fā)生泄漏。R本人自食惡果不足惜预柒,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一队塘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧卫旱,春花似錦人灼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至适贸,卻和暖如春灸芳,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背拜姿。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工烙样, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蕊肥。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓谒获,卻偏偏與公主長得像,于是被迫代替她去往敵國和親壁却。 傳聞我的和親對象是個殘疾皇子批狱,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內容