Autolayout的第一次親密接觸

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

初識Autolayout

iOS中,會將View的布局定義為一系列的線性方程,存放在UIView的屬性中。在View布局的時(shí)候,通過這些方程式,計(jì)算出每一個view的frame來完成布局。這就是Autolayout。

Constraint

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

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

每一個constraint,表示一個相等或不等(大于小于)關(guān)系



上面的這個constraint表示紅色view的頭部距離藍(lán)色view的尾部8個點(diǎn)。
Item: 一般的item都是View
Multiplier: 系數(shù),一般對寬高的屬性用得比較多
Constant: 常量,一般為設(shè)置距離,大小什么的
Relationship: 關(guān)系,上圖中表示的是相等關(guān)系,除此之外也可以用不等關(guān)系表示,例如:>=, <=
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。

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

IntrinsicContentSize

使用Autolayout之后,一個比較爽的地方就是UIlabel,UIButton, UIImageView有了IntrinsicContentSize的屬性。他們可以自己根據(jù)內(nèi)容調(diào)整大小,再也不用量寬和高了。設(shè)置好位置之后,就讓他們自己浪吧,文字有多長就顯示多長,圖片有多大,就顯示多大,真是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也挺好,在有內(nèi)容的時(shí)候支持,沒有內(nèi)容的時(shí)候不支持。這也正是我們想要的

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

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

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


  1. ContentHugging我翻譯過來是內(nèi)容凝聚力,表示View的寬度和高度緊靠內(nèi)容,不讓其擴(kuò)展的力量
  2. CompressionResistance是指壓縮阻力,表示當(dāng)有力量要對其進(jìn)行壓縮的時(shí)候,其阻力的大小

對于同一個View,ContentHugging和CompressionResistance不會同時(shí)起作用。當(dāng)一個Label有文字的時(shí)候,label會存在一個內(nèi)容的Size。
如果有外力讓其size擴(kuò)張,ContentHugging會起作用,外力大于ContentHugging的力量,label的size由外力決定,反之,label的Size由內(nèi)容決定。
如果有外力讓其size壓縮,CompressionResistance會起作用,外力大于CompressionResistance的力量,label的size由外力決定,反之,label的Size由內(nèi)容決定。

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,一般平時(shí)定義約束,默認(rèn)都是這個優(yōu)先級。
  2. UILayoutPriorityDefaultHigh: 高優(yōu)先級,值為750,CompressionResistance的默認(rèn)優(yōu)先級是這個。
  3. UILayoutPriorityDefaultLow: 低優(yōu)先級,值為250,ContentHugging的默認(rèn)優(yōu)先級是這個
  4. UILayoutPriorityFittingSizeLevel: 極低的優(yōu)先級,讓系統(tǒng)估算Size的時(shí)候使用,不適合做約束

知道了各個屬性的默認(rèn)優(yōu)先級之后,就可以解釋為什么一般情況我們給Lable設(shè)置Size約束之后,Label由我們設(shè)置的Size決定,而不是由其內(nèi)容決定。因?yàn)槲覀儧]有特意設(shè)置優(yōu)先級,用的都是默認(rèn)優(yōu)先級。Size約束的優(yōu)先級比CompressionResistance和ContentHugging的優(yōu)先級高。如果我們想讓Label由內(nèi)容決定,我們可以不設(shè)置Size約束或者調(diào)低自己Size約束的優(yōu)先級。

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

IntrinsicContentSize舉例(12月6日新增)

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



一、那么我們設(shè)置一個優(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小,所以當(dāng)添加一個寬度為100的約束時(shí),ContentHugging在起作用。ContentHugging的優(yōu)先級為250。寬度為100的約束優(yōu)先級為500大于ContentHugging。所以寬度為100.
  2. Label2的IntrinsicContentSize寬度比100大。所以當(dāng)添加一個寬度為100的約束時(shí),CompressionResistance在起作用,CompressionResistance的優(yōu)先級為750。寬度為100的約束優(yōu)先級為500小于CompressionResistance。所以寬度還是IntrinsicContentSize的寬度。

根據(jù)這個例子,大家應(yīng)該能明白ContentHugging和CompressionResistance是什么意思了吧。這里留個問題給大家,如果設(shè)置的不是寬度為100,而是Label1寬度等于Label2寬度,那么會出現(xiàn)什么情況?是都變成Label1的寬度了,還是都變成Label2的寬度了?還是不變?

調(diào)教Autolayout

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

Log

寫布局的時(shí)候我們經(jīng)常會遇到布局沖突,一般沖突都會有拋出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。我們運(yùn)行一下看看~

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說,我們不能同時(shí)滿足下面的約束,你寫的這些約束里面肯定有某個約束有問題,你好好改改:

"<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>"

我們這次的運(yùn)行結(jié)果是把這條約束干掉了:

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

根據(jù)Log的信息,肯定是約束有地方?jīng)_突了,而且重點(diǎn)還是寬度相關(guān)的約束,因?yàn)檫\(yùn)行的時(shí)候把UIView:view.width == 300干掉了。讓我們來看看這些約束:
view的左邊 = self.view左邊+100
view的右邊 = self.view右邊-100
view的寬度 = 300
self.view的寬度 = 375
根據(jù)上面的約束關(guān)系,view的左邊右邊的都是參照self.view的,那么view的寬度應(yīng)該是375(self.view寬度) - 100(左邊) -100(右邊) = 175。而我們又給view的寬度賦值了300。所以這個地方?jīng)_突了。
Ok,我們把make.width.equalTo(@300);這句話干掉,再次運(yùn)行一下。Nice,已經(jīng)沒有沖突了~

Visualizing Views and Constraints

沖突問題根據(jù)log搞定了,不過你以為這樣就完事了么?那就too young too simple了。有的時(shí)候我們寫完布局,以為一切ok了,一運(yùn)行,唉媽呀,咋這樣啦?也許是view不見了,也許是view布局不對了,反正有可能是各種摸不著頭腦的問題,感覺明明是對的,一運(yùn)行就錯了。
對于這一類布局和期望不一致的問題。我們還有一個大招,使用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);

運(yùn)行一下,看看效果:


咦,咋這樣了呢?按鈕去哪兒了?redview也沒有居中?檢查一下代碼....沒問題呀!!!!難道是是手機(jī)出bug了?要不要摔一下試試?
還是不要輕易摔手機(jī),我們來看看View的布局。點(diǎn)擊查看View層級的按鈕。(Xcode下方,代碼和控制臺的中間)



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



點(diǎn)擊一下,我們還可以看到他的文字。哎呀,原來是我們忘了給他的文字調(diào)整顏色了,背景是白色,文字也是白色,所以被"隱藏"了。
那我們的redView是怎么回事呢?
點(diǎn)擊一下工具欄最左邊的按鈕,下圖紅色框起來的就是工具欄。

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



我們看到,原來他們的相對位置被設(shè)置成這樣了,那到底是哪里的設(shè)置出了問題呢?
點(diǎn)擊工具欄左邊第二個按鈕,這個按鈕可以顯示出布局的約束。

我們可以看到:
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);

運(yùn)行一下看看:



very Good! 跟我們期望一致了

Autolayout深層次的探索

Autolayout什么時(shí)候計(jì)算frame

根據(jù)最初的介紹,Autolayout是在設(shè)置constraint的時(shí)候,將constraints存放在View的屬性中,在真正布局的時(shí)候去計(jì)算出view的frame,完成布局。那么他到底是在哪個方法中進(jìn)行計(jì)算的呢?
我們知道,計(jì)算出來結(jié)果之后,必定會改變View的位置。由于Autolayout不通過frame布局,而是直接設(shè)置center和bounds。我們給-setCenter:打一個斷點(diǎn)。



通過斷點(diǎn),我們可以看到在layoutSubview的時(shí)候,如果使用了約束會調(diào)用_updateConstraintsAsNecessaryAndApplyLayoutFromEngine。在這個方法里面,系統(tǒng)會先看看是否需要更新約束,如果需要,則調(diào)用-updateConstraints更新約束。
跟新結(jié)束之后,會調(diào)用到_resizeWithOldSuperviewSize:,根據(jù)這個方法名,我們可以猜到,是在這個方法里面根據(jù)約束,計(jì)算出來布局的位置。
計(jì)算完成之后調(diào)用_applyISEngineLayoutValues。應(yīng)用布局,調(diào)整center和bounds。

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

對于Autolayout,有一個問題就是怎么做動畫?
使用Frame的時(shí)候,我們做動畫一般都調(diào)用-animateWithDuration:animations:方法

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

在animations的block里面調(diào)整Frame即可,使用Autolayout之后,由于Autolayout是延遲布局的,并不是約束更新之后就立刻布局,所以大家可以發(fā)現(xiàn)。在-animateWithDuration:animations方法里面修改約束是不能實(shí)現(xiàn)動畫的。

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

查閱文檔我們知道,iOS中不建議直接調(diào)用-layoutSubview,如果要更新布局。可以調(diào)用-layoutIfNeeded。調(diào)用-layoutIfNeeded之后,會同步執(zhí)行-layoutSubview。
所以如果我們要做動畫可以用下面這種方法:

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

當(dāng)View約束發(fā)生變化時(shí),是怎么調(diào)整布局的

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



當(dāng)因?yàn)槟撤N原因,view2的約束發(fā)生了變化,我們來看看會發(fā)生什么:


  1. view2由于自身約束發(fā)生了改變,需要重新布局。會調(diào)用父view:view1的setNeedLayout。告訴View1,我需要重新布局了,趕緊調(diào)用layoutSubviews
  2. view1根據(jù)已有約束,看看自身布局是否需要改變,如果需要改變,則繼續(xù)調(diào)用父view的setNeedLayout。如果不需要改變,直接調(diào)用自己的layoutSubviews
  3. 在view1的layoutSubviews中,完成了view2的布局,這時(shí)候view2的布局發(fā)生了改變,繼續(xù)調(diào)用view2的layoutSubview
  4. 在view2的layoutSubview中,view3的布局沒有發(fā)生改變,所以不需要繼續(xù)調(diào)用layoutSubview,結(jié)束

在Autolayout下使用Frame

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

  1. Autolayout生效之前使用frame。這種情況比較常見,比如在viewDidLoad中對一個view添加了約束,之后又通過Frame調(diào)整他的位置。
    這種情況下,通過Frame調(diào)整位置的代碼是無效的。因?yàn)樵谡嬲季诛@示到頻幕上的時(shí)候,系統(tǒng)會根據(jù)約束,重新計(jì)算Frame,之前設(shè)置的Frame會被沖掉
  2. Autolayout生效之后,使用frame。這種情況稍微少一些,比如View之前就設(shè)置了約束,點(diǎn)擊某個按鈕,需要改變View的Frame。這時(shí)候不使用約束,直接setFrame:
    這種情況下,setFrame:是可以生效的,不過由于是直接setFrame,不是根據(jù)約束計(jì)算的。所以他的子View,父View,以及同級的約束依賴的View,都不會跟著改變。而且如果他的superView被觸發(fā)了layoutSubviews,又會自動根據(jù)約束設(shè)置成約束的Frame,后患無窮。所以一個View使用約束之后,強(qiáng)烈建議不要再對他使用frame。

第二種情況下,如果是需要做一個動畫,動畫結(jié)束后,又會恢復(fù)到原有位置。可以使用Frame

Autolayout的小情緒

Autolayout雖然好用,但是有的時(shí)候會有一些小情緒,特別在iOS6上。那時(shí)候Autolayout還不完善。

UITableView的layoutSubviews沒有調(diào)用[super layoutSubviews]

在iOS6上,UITableView的-layoutSubviews中沒有到調(diào)用UIView的layoutSubviews。
根據(jù)前面的介紹,Autolayout自動布局是在UIView的layoutSubviews中,所以TableView上的子view(如:cell,headerView,footerView)使用了Autolayout,tableView在布局的時(shí)候調(diào)用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是否調(diào)用layoutSubviews在于他上面的子view是否使用Autolayout,而不是他本身。詳細(xì)原因見當(dāng)View約束發(fā)生變化時(shí),是怎么調(diào)整布局的

解決方案:

如果是cell,我們經(jīng)常使用[cell addSubview:view]再對view做一個相對cell的約束,這時(shí)候就會出現(xiàn)問題。解決方案就是使用[cell.contentView addSubview:view]。我們約束是對cell的contentView添加,跟cell無關(guān)。tableView就不會調(diào)用layoutSubviews了

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

ScrollView的相對布局

scrollView是一個特殊的View,因?yàn)樗宋恢么笮≈膺€有content內(nèi)容。scrollView的attribute分為2種:

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

所以在scrollView布局的時(shí)候,如果想讓contentSize跟著里面的子view變化。一定要將edge設(shè)置完整。當(dāng)然直接設(shè)置ContentSize也是可以的

不同性質(zhì)的Attribute不能參照

Autolayout中,不同性質(zhì)的Attribute是不能參照的,就像你不能設(shè)置View1的left距離view2的top為10像素。這明顯是不合適的,因?yàn)閘eft和top是不能對比的。
那么問題就來了,哪些東西可以和哪些東西對比呢?我整理了一張表


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

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

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

我們用>符號表示包含關(guān)系,viewA和ViewB都是self.View的子view。ViewC是ViewB的子View,ViewD是ViewC的子View。ViewA和ViewD是可以相互參照的,因?yàn)樗麄兡苷业焦餐母竀iew:self.View

在View沒有被addSubView之前。他是不能跟其他View做對比的。因?yàn)樗魏蔚腣iew(他自己的子view除外)都找不到共同的父view,也就是說他跟任何View都不在同一的坐標(biāo)系統(tǒng)下。

何時(shí)使用updateConstraints

使用Autolayout之后,系統(tǒng)中多了一個更新約束的方法updateConstraints。看這個方法名,在自定義View的時(shí)候,是不是把約束相關(guān)的代碼放這里面會更好一些呢?
2015年的WWDC技術(shù)講座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.

簡單總結(jié)一下就是:
初始化constraint的代碼放在viewDidLoad等初始化方法中更好。
updateConstraints方法僅用于提升性能。當(dāng)你更新大量約束,發(fā)現(xiàn)由于約束太多,布局有點(diǎn)卡。這時(shí)候你可以使用updateConstraints,因?yàn)樵趗pdateConstraints中更新約束會批量操作,能獲得更好的性能(一般不會遇到這種情況)
所以正常情況下,我們直接在初始化的方法中寫約束就好。詳細(xì)資料參考何時(shí)使用updateConstraints

Reference

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

About me

我的博客
我的微博

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,797評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,179評論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,628評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,642評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,444評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,948評論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,040評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,185評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,717評論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,794評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,418評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,414評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,750評論 2 370

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