天天看點

Auto Layout深入了解,及masonry簡單介紹

本篇部落格是本人在學習自己主動布局過程中對自己主動布局的了解和整理,分三部分介紹,内容可能會有所反複。見諒。

一、autosizing與Auto Layout對照,及Auto Layout簡單介紹

1、springs&struts簡單介紹及問題

你肯定非常熟悉autosizing masks-也被覺得是springs&struts模式。autosizing

mask決定了當一個視圖的父視圖大小改變時,其自身須要做出什麼改變。它有一個靈活的或固定不變的margins(struts)嗎?它的寬和高要做出什麼改變(springs)?

可是畢竟不像自己主動布局一樣對全部視圖進行全面限制,在某些情況下的不能依照開發人員的意願完美的布局視圖,比如http://www.cocoachina.com/industry/20131203/7462.html中第一個樣例就是autosizing布局失敗的樣例,此時須要在viewWillLayoutSubviews中依據裝置方向對frame進行設定,可是适配是一個令人很煩躁的事情。稍不注意就會出錯,是以産生了後來的自己主動布局。

2、自己主動布局簡單介紹及優勢

自己主動布局使用限制去說明視圖間的布局情況,使用限制最大的優勢就是你再也不須要把時間浪費在坐标上了。

相反,你能夠向自己主動布局描寫叙述視圖怎樣和其它視圖相關聯,自己主動布局将會為你完畢全部困難的工作。這叫做依據目的設計(designing by intent)。

當你依據目的設計時,你表達的是你想要實作什麼,而不須要關心它怎樣實作。在autosizing中我們描寫叙述布局時說”button的左上角坐标為(20,230)",如今你能夠這麼說了:”button是垂直居中于它的父視圖。而且相對于父視圖的左邊緣有一個固定的距離”,使用這個描寫叙述。無論父視圖多大或多小,自己主動布局都能夠自己主動計算出你的button須要在哪兒出現。這使得你使用者界面的設定更具描寫叙述性.你僅僅需簡單的定義限制。系統會為你自己主動計算frames。

使用自己主動布局還有一個重要的優點就是本地化。

比方德語中的文本。出了名的比老奶奶的裹腳布還要長。适配起來是一件非常麻煩的事。再次。自己主動布局解救了猿,由于它能依據label須要顯示的内容自己主動改變label的大小。

自己主動布局不僅對旋轉有作用。它還能輕易的縮放你UI的大小進而适應不同尺寸的螢幕。

比如iphone5比iphone4s的高度變高了,程式在iPhone5中顯示時是否會出現故障呢。不用操心。自己主動布局能輕易的拉伸你程式的使用者界面。進而充滿iPhone5垂直方向上多出來的空間。

注意:标示自己主動布局是否有效的T-bars是橘黃色時,意味着你的布局沒有完畢,即自己主動布局沒有足夠的限制條件計算出視圖的位置和大小。解決的方法便是添加很多其它限制,直到他們變藍。

3、擁抱限制

1)、自己主動限制

假設你根本不提供不論什麼限制,Xcode自己主動配置設定一套預設的限制,正是我們所知的自己主動限制。它會在程式built的編譯時間中去完畢這些事。而不是設計時間。

當你設計你的使用者界面時,Xcode5中的自己主動布局為了不參與你的設計方法而努力工作,這這是我們喜歡它的原因。自己主動限制為你的視圖提供一個固定尺寸和位置。換句話說,視圖總是擁有跟你在storyboard中看到的一樣的坐标。這是很友善的,由于這就意味着你能夠大量的忽視自己主動布局。你能夠為那些擁有充分限制的控件不添加限制,僅僅為那些須要特殊規則的視圖建立限制。

2)、Xcode建立自己主動限制的規則

Xcode僅僅為那些你沒有設定不論什麼限制的對象建立自己主動限制。

一旦你添加一個限制。你便是告訴Xcode你接管了這個視圖。

Xcode将不再添加不論什麼自己主動限制,并希望你為這個視圖添加須要的限制。

3)、不完整限制的影響(限制過多或不足)

盡管Xcode5以後再也不強制你總是有一個有效的布局,可是執行一個無效布局的程式是不明智的,由于自己主動布局可能不能正确的計算須要将視圖放在哪兒,要麼視圖的位置是不可預知的(限制不夠)。要麼程式将會崩潰(限制過多)。

4)、錯位的視圖

錯位的視圖,即依據自己主動布局顯示視圖的frame和你在螢幕上放置的視圖的frame不在同一位置。此時依據自己主動布局顯示視圖的frame是橙色的虛線邊框,你在螢幕上放置的視圖的frame是橙色的實線邊框(當談到自己主動布局。橙色代表壞的)。

====假設你在螢幕上放置的視圖的frame是你想要的位置,此時點選Editor菜單->Resolve Auto Layout Issues菜單->Update Constraints就能夠更新限制。

二、自己主動布局介紹,及為何引入masonry

說明:在xib和storyboard中因為蘋果将限制可視化呈現給開發人員使得自己主動布局非常easy使用;通過代碼建構界面時假設想使用自己主動布局就必須通過蘋果提供的接口建立限制。因為自己主動布局的接口不易使用。是以就産生了masonry,masonry将蘋果提供的自己主動布局接口封裝的易于使用。

1、Auto Layout是什麼

​​Auto Layout​

​是一個基于constraint(限制)的布局系統。它依據UI元素之間限制關系來調整UI元素的位置和大小。

2、Auto Layout解決什麼問題

  • 更easy适配不同分辨率裝置的螢幕(iPhone 6 Plus, iPhone 6, iPhone 5s/5, iPhone 4s/4)
  • 當裝置旋轉時不須要做額外處理
  • 使用constraint來描寫叙述布局邏輯,更利于了解和清晰

3、代碼中怎樣使用Auto Layout

Auto Layout中限制的類相應是​​NSLayoutConstraint​​, 而建立NSLayoutConstraint對象主要有兩種方式,第一種是

+ (id)constraintWithItem:(id)view1

               attribute:(NSLayoutAttribute)attribute1

               relatedBy:(NSLayoutRelation)relation

                  toItem:(id)view2

               attribute:(NSLayoutAttribute)attribute2

              multiplier:(CGFloat)multiplier

                constant:(CGFloat)constant;

上面方法主要意思是,某個view1的attribute1等于(小于或等于/大于或等于)某個view2的attribute2的multiplier倍加上constant。

而attribute主要由表示位置(上/下/左/右)和大小(寬/高)的下面幾個值:

typedef enum: NSInteger {

   NSLayoutAttributeLeft = 1,

   NSLayoutAttributeRight,

   NSLayoutAttributeTop,

   NSLayoutAttributeBottom,

   NSLayoutAttributeLeading,

   NSLayoutAttributeTrailing,

   NSLayoutAttributeWidth,

   NSLayoutAttributeHeight,

   NSLayoutAttributeCenterX,

   NSLayoutAttributeCenterY,

   NSLayoutAttributeBaseline,

   NSLayoutAttributeNotAnAttribute = 0

} NSLayoutAttribute;

簡化一下。使用公式能夠表達為:

view1.attribute1 = view2.attribute2 * multiplier + constant

另外一種方式是:

+ (NSArray *)constraintsWithVisualFormat:(NSString *)format 

                                 options:(NSLayoutFormatOptions)opts 

                                 metrics:(NSDictionary *)metrics 

                                   views:(NSDictionary *)views;

這樣的方式主要是採用​​Visual Format Language​

​(可視化格式語言)來描寫叙述限制布局。盡管文法比較簡潔。可是可讀性比較差和easy出錯。

4、Auto Layout存在問題

盡管Auto Layout在布局view方面是很強大和靈活,可是建立constraint的文法過于繁雜,引用​​Masonry​​一個樣例:

UIView *superview = self;

UIView *view1 = [[UIView alloc] init];

view1.translatesAutoresizingMaskIntoConstraints = NO;

view1.backgroundColor = [UIColor greenColor];

[superview addSubview:view1];

UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);

[superview addConstraints:@[

    //view1 constraints

    [NSLayoutConstraint constraintWithItem:view1

                                 attribute:NSLayoutAttributeTop

                                 relatedBy:NSLayoutRelationEqual

                                    toItem:superview

                                multiplier:1.0

                                  constant:padding.top],

                                 attribute:NSLayoutAttributeLeft

                                  constant:padding.left],

                                 attribute:NSLayoutAttributeBottom

                                  constant:-padding.bottom],

                                 attribute:NSLayoutAttributeRight

                                multiplier:1

                                  constant:-padding.right],

 ]];

如此簡單的一個樣例都要編寫這麼多行代碼。想象一下假設建立多個view的constraint時會多麼痛苦啊。

還有一個方式是採用Visual Format Language (VFL),盡管文法比較簡潔,可是可讀性比較差和easy出錯。

5、為什麼使用Masonry

​​Masonry​​是採用鍊式​​DSL(Domain-specific language)​

​來封裝NSLayoutConstraint,通過這樣的方式編寫Auto Layout布局代碼更加易讀和簡潔。

6、Masonry怎樣使用

使用Masonry建立constraint來定義布局的方式有三種:mas_makeConstraints。mas_updateConstraints,mas_remakeConstraints。

1). mas_makeConstraints

使用mas_makeConstraints建立constraint後,你能夠使用局部變量或屬性來儲存以便下次引用它;假設建立多個constraints,你能夠採用數組來儲存它們。

// in public/private interface

@property (nonatomic, strong) MASConstraint *topConstraint;

...

// when making constraints

[view1 mas_makeConstraints:^(MASConstraintMaker *make) {

    self.topConstraint = make.top.equalTo(superview.mas_top).with.offset(padding.top);

    make.left.equalTo(superview.mas_left).with.offset(padding.left);

}];

// then later you can call

[self.topConstraint uninstall];

2). mas_updateConstraints

有時你須要更新constraint(比如,動畫和調試)而不是建立固定constraint,能夠使用mas_updateConstraints方法

// this is Apple's recommended place for adding/updating constraints

// this method can get called multiple times in response to setNeedsUpdateConstraints

// which can be called by UIKit internally or in your code if you need to trigger an update to your constraints

- (void)updateConstraints {

    [self.growingButton mas_updateConstraints:^(MASConstraintMaker *make) {

        make.center.equalTo(self);

        make.width.equalTo(@(self.buttonSize.width)).priorityLow();

        make.height.equalTo(@(self.buttonSize.height)).priorityLow();

        make.width.lessThanOrEqualTo(self);

        make.height.lessThanOrEqualTo(self);

    }];

    //according to apple super should be called at end of method

    [super updateConstraints];

}

3). mas_remakeConstraints

mas_remakeConstraints與mas_updateConstraints比較相似,都是更新constraint。隻是。mas_remakeConstraints是删除之前constraint。然後再加入新的constraint(适用于移動動畫);而mas_updateConstraints僅僅是更新constraint的值。

- (void)changeButtonPosition {

    [self.button mas_remakeConstraints:^(MASConstraintMaker *make) {

        make.size.equalTo(self.buttonSize);

        if (topLeft) {

            make.top.and.left.offset(10);

        } else {

            make.bottom.and.right.offset(-10);

        }

想了解以上三個代碼片段的很多其它細節,能夠下載下傳Masonry iOS Examplesproject查閱。

7、masonry使用舉例

1)、三個控件等高

     make.height.mas_equalTo(@[redView,

blueView]);

2)、讓控件始終居中顯示

make.center.mas_equalTo(self.view);

3)、設定限制的優先級為最低

make.width.height.mas_equalTo(100 * self.scacle).priorityLow();

4)、讓控件的寬和高小于或者等于self.view的寬和高

make.width.height.lessThanOrEqualTo(self.view);

8、此外自己主動布局的第三方庫還有Classy。因為沒實用過,就不再叙述了。有興趣的話能夠參考http://blog.csdn.net/zhang_red/article/details/45503683,或者谷歌一下。

三、Auto Layout使用總結

1、自己主動布局經常使用函數

1)、setNeedsUpdateConstraints

當一個自己定義view的某個屬性發生改變。而且可能影響到constraint時,須要調用此方法去标記constraints須要在未來的某個點更新。系統然後調用updateConstraints.

2)、needsUpdateConstraints

constraint-based layout system使用此傳回值去決定是否須要調用updateConstraints作為正常布局過程的一部分。

3)、updateConstraintsIfNeeded

馬上觸發限制更新。自己主動更新布局。

4)、updateConstraints

自己定義view應該重寫此方法在當中建立constraints. 注意:要在實如今最後調用[super updateConstraints]

5)、updateViewConstraints

自己定義UIViewcontroller應該重寫此方法在當中建立constraints. 注意:要在實如今最後調用[super updateConstraints]

2、要使用AutoLayout,請先設定要限制的view的translatesAutoresizingMaskIntoConstraints 屬性為 NO 。

在xib或者sb中勾選Use Auto Layout。全部在xib或者sb中出現的view都已經預設将translatesAutoresizingMaskIntoConstraints設定為NO。

3、在使用AutoLayout布局的view中,代碼中避免出現設定其frame相關屬性(如center)的代碼 ,可是能夠擷取其frame;

4、通過代碼為xib或sb中view添加限制時。盡量避免在 viewDidLoad中運作,最好放在updateViewConstraints[UIViewcontroller]或者updateConstraints[UIView]中 ,記得調用[super updateViewConstraints]或者[super updateConstraints];

注意:在updateViewConstraints為view加入限制,請確定該view的translatesAutoresizingMaskIntoConstraints屬性已設定為NO。假設你真的寫在viewDidLoad裡了。那麼可能會遇到這樣的崩潰錯誤Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Impossible

to set up layout with view hierarchy unprepared for constraint.'

5、 假設須要在控制器中動态加入或者移除視圖,在控制器中為新加入的視圖添加限制,在updateViewConstrains中實作,然後調用[super updateViewConstrains];同理,在view中動态加入或者移除視圖。在updateConstrains中實作,然後調用[super

updateConstrains]

6、 控制器在其view須要又一次布局時會運作下面過程:

① 控制器的視圖調整到新的尺寸 - 控制器會依據目前狀态欄、導覽列等其他因素的狀态來調整其view的位置尺寸

② 假設沒有使用autolayout,全部子視圖會依據autoresizeing mask調整

③ 調用viewWillLayoutSubviews

④ 調用控制器視圖的layoutSubviews,假設是使用autolayout,則會調用updateViewConstrains -> 該方法的實作會調用全部子視圖的updateConstraints -> 更新完限制之後,全部視圖會依據計算出來的新的布局更新位置

⑤ 調用控制器的viewDidLayoutSubviews

7、自己定義view須要又一次布局時會運作下面過程:

與使用springs and struts(autoresizingMask)比較。Auto layout在view顯示之前。多引入了兩個步驟:updating

constraints 和laying out views。

每個步驟都依賴于上一個。display依賴layout,而layout依賴updating constraints。

 updating constraints->layout->display

第一步:updating constraints。被稱為測量階段。其從下向上(from subview to super view),為下一步layout準備資訊。

能夠通過調用方法setNeedUpdateConstraints去觸發此步。

constraints的改變也會自己主動的觸發此步。可是,當你自己定義view的時候。假設一些改變可能會影響到布局的時候。通常須要自己去通知Auto layout,updateConstraintsIfNeeded。

自己定義view的話,通常能夠重寫updateConstraints方法。在當中能夠加入view須要的局部的contraints。

第二步:layout,其從上向下(from super view to subview),此步主要應用上一步的資訊去設定view的center和bounds。能夠通過調用setNeedsLayout去觸發此步驟。此方法不會馬上應用layout。假設想要系統馬上的更新layout,能夠調用layoutIfNeeded。另外。自己定義view能夠重寫方法layoutSubViews來在layout的project中得到很多其它的定制化效果。

第三步:display,此步時把view渲染到螢幕上,它與你是否使用Auto layout無關。其操作是從上向下(from super view to subview),通過調用setNeedsDisplay觸發。

由于每一步都依賴前一步,是以一個display可能會觸發layout。當有不論什麼layout沒有被處理的時候,同理,layout可能會觸發updating constraints,當constraint system更新改變的時候。

須要注意的是。這三步不是單向的。constraint-based layout是一個疊代的過程。layout過程中,可能去改變constraints,有一次觸發updating constraints。進行一輪layout過程。

注意:假設你每一次調用自己定義layoutSubviews都會導緻還有一個布局傳遞,那麼你将會陷入一個無限循環中。