本篇博客是本人在學習本身主動佈局過程當中對本身主動佈局的理解和整理,分三部分介紹,內容可能會有所反覆。見諒。html
1、springs&struts簡單介紹及問題ios
你確定很是熟悉autosizing masks-也被以爲是springs&struts模式。autosizing mask決定了當一個視圖的父視圖大小改變時,其自身需要作出什麼改變。它有一個靈活的或固定不變的margins(struts)嗎?它的寬和高要作出什麼改變(springs)?git
但是畢竟不像本身主動佈局同樣對所有視圖進行全面約束,在某些狀況下的不能依照開發人員的意願完美的佈局視圖,好比http://www.cocoachina.com/industry/20131203/7462.html中第一個樣例就是autosizing佈局失敗的樣例,此時需要在viewWillLayoutSubviews中依據設備方向對frame進行設定,但是適配是一個使人很煩躁的事情。稍不注意就會出錯,因此產生了後來的本身主動佈局。github
二、本身主動佈局簡單介紹及優點spring
本身主動佈局使用約束去說明視圖間的佈局狀況,使用約束最大的優點就是你不再需要把時間浪費在座標上了。數組
相反,你可以向本身主動佈局描寫敘述視圖怎樣和其它視圖相關聯,本身主動佈局將會爲你完畢所有困難的工做。這叫作依據目的設計(designing by intent)。app
當你依據目的設計時,你表達的是你想要實現什麼,而不需要關心它怎樣實現。在autosizing中咱們描寫敘述佈局時說」button的左上角座標爲(20,230)",現在你可以這麼說了:」button是垂直居中於它的父視圖。並且相對於父視圖的左邊緣有一個固定的距離」,使用這個描寫敘述。不管父視圖多大或多小,本身主動佈局都可以本身主動計算出你的button需要在哪兒出現。這使得你用戶界面的設置更具描寫敘述性.你僅僅需簡單的定義約束。系統會爲你本身主動計算frames。less
使用本身主動佈局還有一個重要的優勢就是本地化。iphone
比方德語中的文本。出了名的比老奶奶的裹腳布還要長。適配起來是一件很是麻煩的事。再次。本身主動佈局解救了猿,因爲它能依據label需要顯示的內容本身主動改變label的大小。函數
本身主動佈局不只對旋轉有做用。它還能輕易的縮放你UI的大小從而適應不一樣尺寸的屏幕。
好比iphone5比iphone4s的高度變高了,程序在iPhone5中顯示時是否會出現故障呢。不用操心。本身主動佈局能輕易的拉伸你程序的用戶界面。從而充滿iPhone5垂直方向上多出來的空間。
注意:標示本身主動佈局是否有效的T-bars是橘黃色時,意味着你的佈局沒有完畢,即本身主動佈局沒有足夠的約束條件計算出視圖的位置和大小。解決的方法即是添加不少其它約束,直到他們變藍。
三、擁抱約束
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就可以更新約束。
說明:在xib和storyboard中由於蘋果將約束可視化呈現給開發人員使得本身主動佈局很是easy使用;經過代碼構建界面時假設想使用本身主動佈局就必須經過蘋果提供的接口建立約束。由於本身主動佈局的接口不易使用。因此就產生了masonry,masonry將蘋果提供的本身主動佈局接口封裝的易於使用。
1、Auto Layout是什麼
Auto Layout是一個基於constraint(約束)的佈局系統。它依據UI元素之間約束關係來調整UI元素的位置和大小。
2、Auto Layout解決什麼問題
三、代碼中怎樣使用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
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),儘管語法比較簡潔,但是可讀性比較差和easy出錯。
五、爲何使用Masonry
Masonry是採用鏈式DSL(Domain-specific language)來封裝NSLayoutConstraint,經過這樣的方式編寫Auto Layout佈局代碼更加易讀和簡潔。
六、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查閱。
七、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);
八、此外本身主動佈局的第三方庫還有Classy。由於沒實用過,就再也不敘述了。有興趣的話可以參考http://blog.csdn.net/zhang_red/article/details/45503683,或者谷歌一下。
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都會致使還有一個佈局傳遞,那麼你將會陷入一個無限循環中。