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
2、自動佈局簡介及優勢
自動佈局使用約束去說明檢視間的佈局情況,使用約束最大的優勢就是你再也不需要把時間浪費在座標上了。相反,你可以向自動佈局描述檢視如何和其他檢視相關聯,自動佈局將會為你完成所有困難的工作。這叫做根據目的設計(designing by intent)。
當你根據目的設計時,你表達的是你想要實現什麼,而不需要關心它如何實現。在autosizing中我們描述佈局時說”button的左上角座標為(20
使用自動佈局另一個重要的好處就是本地化。比如德語中的文字,出了名的比老奶奶的裹腳布還要長,適配起來是一件很麻煩的事。再次,自動佈局拯救了猿,因為它能根據label需要顯示的內容自動改變label的大小。
自動佈局不僅對旋轉有作用;它還能輕易的縮放你UI的大小從而適應不同尺寸的螢幕。例如iphone5比iphone4s的高度變高了,程式在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中由於蘋果將約束視覺化呈現給開發者使得自動佈局很容易使用;通過程式碼構建介面時如果想使用自動佈局就必須通過蘋果提供的介面建立約束,由於自動佈局的介面不易使用,所以就產生了masonry,masonry將蘋果提供的自動佈局介面封裝的易於使用。
1、Auto Layout是什麼
是一個基於constraint(約束)的佈局系統,它根據UI元素之間約束關係來調整UI元素的位置和大小。
2、Auto Layout解決什麼問題
- 更容易適配不同解析度裝置的螢幕(iPhone 6 Plus, iPhone 6, iPhone 5s/5, iPhone 4s/4)
- 當裝置旋轉時不需要做額外處理
- 使用constraint來描述佈局邏輯,更利於理解和清晰
3、程式碼中如何使用Auto Layout
Auto Layout, 而建立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(視覺化格式語言)來描述約束佈局,雖然語法比較簡潔,但是可讀性比較差和容易出錯。
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
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:padding.top],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:padding.left],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:-padding.bottom],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeRight
multiplier:1
constant:-padding.right],
]];
如此簡單的一個例子都要編寫這麼多行程式碼,想象一下如果建立多個view的constraint時會多麼痛苦啊。另一個方式是採用Visual Format Language (VFL),雖然語法比較簡潔,但是可讀性比較差和容易出錯。
5、為什麼使用Masonry
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 Examples工程查閱。
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,而layou