系列:iOS開發(fā)-Masonry的使用
對(duì)于一個(gè)開發(fā)者來說,我們是肯定需要和布局打交道的,最早的我們可能會(huì)使用手寫frame或者bounce等,在之后我們可能會(huì)方便的使用xib或者storyboard來拉約束等.
我們會(huì)發(fā)現(xiàn)使用約束,autolayout是很方便的方法,但是僅限于在xib或者storyboard中拉出來的約束,如果我們是手寫的話,你會(huì)發(fā)現(xiàn)實(shí)在過于的繁瑣和啰嗦.
于是就出現(xiàn)了Masonry,這個(gè)高效的三方框架
Masonry是一個(gè)輕量級(jí)的布局框架 擁有自己的描述語法 采用更優(yōu)雅的鏈?zhǔn)秸Z法封裝自動(dòng)布局 簡潔明了 并具有高可讀性 而且同時(shí)支持 iOS 和 Max OS X权旷。
至于學(xué)習(xí)的話,最有效的方式就是下載官方的demo來學(xué)習(xí).這里我也就不寫自己的demo來班門弄斧了.
Masonry 源碼:https://github.com/Masonry/Masonry
下載下來
雙擊打開工程
選擇iOS Examples,至于設(shè)備的話,選擇自己習(xí)慣的就好
運(yùn)行之后我們能看到一個(gè)完整的列表
我們一個(gè)一個(gè)學(xué)習(xí),首先是Basic的內(nèi)容,我們觀察界面,同時(shí)進(jìn)入代碼中閱讀代碼
我們可以看到上面兩個(gè)view,一個(gè)綠色一個(gè)紅色,下面有一個(gè)藍(lán)色的view,很簡單的布局,我們?nèi)绻褂脁ib等拉的話很好拉,弄幾個(gè)帶有比例的約束就好了,
我們看看Masonry的實(shí)現(xiàn),首先它沒有xib,另外它使用的不是frame.手寫的代碼,我們知道他就是手寫的約束.我們看看內(nèi)容
int padding = 10;
//if you want to use Masonry without the mas_ prefix
//define MAS_SHORTHAND before importing Masonry.h see Masonry iOS Examples-Prefix.pch
[greenView makeConstraints:^(MASConstraintMaker *make) {
make.top.greaterThanOrEqualTo(superview.top).offset(padding);
make.left.equalTo(superview.left).offset(padding);
make.bottom.equalTo(blueView.top).offset(-padding);
make.right.equalTo(redView.left).offset(-padding);
make.width.equalTo(redView.width);
make.height.equalTo(redView.height);
make.height.equalTo(blueView.height);
}];
閱讀這一段,首先有一段注釋,如果想要使用不帶有mas_top的而是簡寫成top的,在pch文件中添加一段宏定義,這里我們看看就好了
下面是代碼,
約束代碼我們可以看到是封裝在一個(gè)block中,使用的是makeConstraints的方法.
在Masonry中有三種創(chuàng)建約束的方法
makeConstraints 這個(gè)方法只會(huì)添加新的約束
mas_updateConstraints 這個(gè)方法將會(huì)覆蓋以前的某些特定的約束
mas_remakeConstraints 這個(gè)方法會(huì)將以前的約束全部刪除蕉世,添加新的約束
/*
mas_makeConstraints 只負(fù)責(zé)新增約束 Autolayout不能同時(shí)存在兩條針對(duì)于同一對(duì)象的約束 否則會(huì)報(bào)錯(cuò)
mas_updateConstraints 針對(duì)上面的情況 會(huì)更新在block中出現(xiàn)的約束 不會(huì)導(dǎo)致出現(xiàn)兩個(gè)相同約束的情況
mas_remakeConstraints 則會(huì)清除之前的所有約束 僅保留最新的約束
三種函數(shù)善加利用 就可以應(yīng)對(duì)各種情況了
*/
內(nèi)容很簡單,跟我們xib拉約束的方式是基本完全一樣的
讀起來也很簡單,make.left.equalTo(superview.left).offset(padding);
讓左邊等于父視圖的左邊偏移10個(gè)像素單位
make.bottom.equalTo(blueView.top).offset(-padding);
讓底部等于藍(lán)色視圖的頂部偏移-10個(gè)像素
我們點(diǎn)擊top或者其他跳轉(zhuǎn)到Masonry中看看支持哪些屬性
@property (nonatomic, strong, readonly) MASConstraint *left;
@property (nonatomic, strong, readonly) MASConstraint *top;
@property (nonatomic, strong, readonly) MASConstraint *right;
@property (nonatomic, strong, readonly) MASConstraint *bottom;
@property (nonatomic, strong, readonly) MASConstraint *leading;
@property (nonatomic, strong, readonly) MASConstraint *trailing;
@property (nonatomic, strong, readonly) MASConstraint *width;
@property (nonatomic, strong, readonly) MASConstraint *height;
@property (nonatomic, strong, readonly) MASConstraint *centerX;
@property (nonatomic, strong, readonly) MASConstraint *centerY;
@property (nonatomic, strong, readonly) MASConstraint *baseline
跟我們的約束一樣,上下左右寬高,中心點(diǎn)等,
所以對(duì)于一般的約束,我們至此都已經(jīng)可以完全自定義了
當(dāng)然我們也可以注意一下這個(gè)
至于干什么也不用解釋,就是可以同時(shí)添加多個(gè)約束
接下來在看列表的下一項(xiàng)MASExampleUpdateView
+ (BOOL)requiresConstraintBasedLayout
{
return YES;
}
// this is Apple's recommended place for adding/updating constraints
- (void)updateConstraints {
[self.growingButton 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];
}
- (void)didTapGrowButton:(UIButton *)button {
self.buttonSize = CGSizeMake(self.buttonSize.width * 1.3, self.buttonSize.height * 1.3);
// tell constraints they need updating
[self setNeedsUpdateConstraints];
// update constraints now so we can animate the change
[self updateConstraintsIfNeeded];
[UIView animateWithDuration:0.4 animations:^{
[self layoutIfNeeded];
}];
}
首先是requiresConstraintBasedLayout
介紹是這樣的
constraint-based layout engages lazily when someone tries to use it (e.g., adds a constraint to a view). If you do all of your constraint set up in -updateConstraints, you might never even receive updateConstraints if no one makes a constraint. To fix this chicken and egg problem, override this method to return YES if your view needs the window to use constraint-based layout.
意思就是基于約束的布局是懶觸發(fā)的茴厉,只有在添加了約束的情況下品擎,系統(tǒng)才會(huì)自動(dòng)調(diào)用 -updateConstraints 方法,如果把所有的約束放在 updateConstraints中谅摄,那么系統(tǒng)將會(huì)不知道你的布局方式是基于約束的,所以 重寫+requiresConstraintBasedLayout 返回YES就是明確告訴系統(tǒng):雖然我之前沒有添加約束,但我確實(shí)是基于約束的布局沛厨!這樣可以保證系統(tǒng)一定會(huì)調(diào)用 -updateConstraints 方法 從而正確添加約束.
我們可以看到這一次Masonry的約束是寫在updateConstraints中的.
這只是一個(gè)小的知識(shí)點(diǎn).
接下來是更新的方式,只要的代碼是這幾個(gè)
// tell constraints they need updating
[self setNeedsUpdateConstraints];
// update constraints now so we can animate the change
[self updateConstraintsIfNeeded];
[UIView animateWithDuration:0.4 animations:^{
[self layoutIfNeeded];
}];
使用上面的方式來更新約束,這樣系統(tǒng)就會(huì)再次調(diào)用updateConstraints來實(shí)現(xiàn)約束的更新,從而實(shí)現(xiàn)動(dòng)畫,我們可以點(diǎn)擊按鈕來觀看效果,按鈕點(diǎn)擊一次變大一次,寬高是之前的1.3倍
[self.growingButton 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);
}];
另外這里涉及到優(yōu)先級(jí)的部分,首先是中心點(diǎn)位置跟父視圖的中心點(diǎn)一致,其次是寬高優(yōu)先的是小于<=父視圖的寬高,在滿足的情況下在是等于指定寬高
在這個(gè)demo中我們可以在學(xué)習(xí)一下優(yōu)先級(jí)的調(diào)用和更新約束的邏輯
接下來是MASExampleRemakeView
在這里我就不介紹了
使用的更新約束的方法為
[self.movingButton remakeConstraints:^(MASConstraintMaker *make) {
make.width.equalTo(@(100));
make.height.equalTo(@(100));
if (self.topLeft) {
make.left.equalTo(self.left).with.offset(10);
make.top.equalTo(self.top).with.offset(10);
}
else {
make.bottom.equalTo(self.bottom).with.offset(-10);
make.right.equalTo(self.right).with.offset(-10);
}
}];
大意是移除之前的約束,添加新的約束,也是在updateConstraints方法中實(shí)現(xiàn)
再繼續(xù) MASExampleSidesView
我們看到好多的view,眼睛都花掉了,這個(gè)肯定要寫很久吧
好吧,我錯(cuò)了實(shí)現(xiàn)的方法就幾行而已
UIView *lastView = self;
for (int i = 0; i < 10; i++) {
UIView *view = UIView.new;
view.backgroundColor = [self randomColor];
view.layer.borderColor = UIColor.blackColor.CGColor;
view.layer.borderWidth = 2;
[self addSubview:view];
[view mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(lastView).insets(UIEdgeInsetsMake(5, 10, 15, 20));
}];
lastView = view;
}
一個(gè)循環(huán)創(chuàng)建,在這里我們可以看到一個(gè)新的關(guān)鍵字edges
是的,這是一個(gè)新的關(guān)鍵字,表示了整個(gè)邊緣,不需要重復(fù)寫上下左右
我們進(jìn)入類中可以看到
- (MASConstraint *)edges {
return [self addConstraintWithAttributes:MASAttributeTop | MASAttributeLeft | MASAttributeRight | MASAttributeBottom];
}
- (MASConstraint *)size {
return [self addConstraintWithAttributes:MASAttributeWidth | MASAttributeHeight];
}
- (MASConstraint *)center {
return [self addConstraintWithAttributes:MASAttributeCenterX | MASAttributeCenterY];
}
他幫助你實(shí)現(xiàn)了邊緣 大小 中心 三種方法,所以之前的base的方式我們也可以嘗試著簡寫看看..
然后是insets
我們看到的是類似collectionView的組的間距,距離上下左右的像素差距,我們很容易理解
- (MASConstraint * (^)(MASEdgeInsets))insets {
return ^id(MASEdgeInsets insets){
self.insets = insets;
return self;
};
}
- (MASConstraint * (^)(CGFloat))inset {
return ^id(CGFloat inset){
self.inset = inset;
return self;
};
}
- (MASConstraint * (^)(CGSize))sizeOffset {
return ^id(CGSize offset) {
self.sizeOffset = offset;
return self;
};
}
- (MASConstraint * (^)(CGPoint))centerOffset {
return ^id(CGPoint offset) {
self.centerOffset = offset;
return self;
};
}
- (MASConstraint * (^)(CGFloat))offset {
return ^id(CGFloat offset){
self.offset = offset;
return self;
};
}
- (MASConstraint * (^)(NSValue *value))valueOffset {
return ^id(NSValue *offset) {
NSAssert([offset isKindOfClass:NSValue.class], @"expected an NSValue offset, got: %@", offset);
[self setLayoutConstantWithValue:offset];
return self;
};
}
除此以外,其實(shí)現(xiàn)了insets\inset\sizeOffset\centerOffset\offset.等方法我們可以注意到這些都是Modifies the NSLayoutConstraint constant,意思是修改約束,我們可以看到都是添加一些在固定了大小之后的微調(diào)的方式...我們合理運(yùn)用的時(shí)候會(huì)很方便
接下來就稍微有點(diǎn)復(fù)雜了,我們看看MASExampleAspectFitView
demo首先創(chuàng)建了4個(gè)視圖
// Create views
self.topView = [[UIView alloc] initWithFrame:CGRectZero];
self.topInnerView = [[UIView alloc] initWithFrame:CGRectZero];
self.bottomView = [[UIView alloc] initWithFrame:CGRectZero];
self.bottomInnerView = [[UIView alloc] initWithFrame:CGRectZero];
// Set background colors
UIColor *blueColor = [UIColor colorWithRed:0.663 green:0.796 blue:0.996 alpha:1];
[self.topView setBackgroundColor:blueColor];
UIColor *lightGreenColor = [UIColor colorWithRed:0.784 green:0.992 blue:0.851 alpha:1];
[self.topInnerView setBackgroundColor:lightGreenColor];
UIColor *pinkColor = [UIColor colorWithRed:0.992 green:0.804 blue:0.941 alpha:1];
[self.bottomView setBackgroundColor:pinkColor];
UIColor *darkGreenColor = [UIColor colorWithRed:0.443 green:0.780 blue:0.337 alpha:1];
[self.bottomInnerView setBackgroundColor:darkGreenColor];
這里不是我們需要關(guān)注的,我們看約束
先看上面兩個(gè)
// Layout top and bottom views to each take up half of the window
[self addSubview:self.topView];
[self.topView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.and.top.equalTo(self);
}];
[self addSubview:self.bottomView];
[self.bottomView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.and.bottom.equalTo(self);
make.top.equalTo(self.topView.mas_bottom);
make.height.equalTo(self.topView);
}];
這里我們看到了鏈?zhǔn)骄幊趟枷氲膶?shí)現(xiàn)將多個(gè)操作通過.連接在一起成為一句代碼make.left.right.and.top.equalTo(self)
;
讓,左右和上都與父視圖一致,至于鏈?zhǔn)骄幊趟枷?后續(xù)會(huì)介紹,這里就先不說了,大意是其不需要考慮調(diào)用順序,只需要知道考慮結(jié)果摔认,類似于蝴蝶效應(yīng)逆皮,產(chǎn)生一個(gè)事件,會(huì)影響很多東西参袱,這些事件像流一樣的傳播出去电谣,然后影響結(jié)果,借用面向?qū)ο蟮囊痪湓捘ㄊ矗f物皆是流剿牺。
上面的約束是固定了左右上 下面的約束是固定了左右下 ,然后讓下面的頂部跟上面的底部一樣 且兩個(gè)高度一樣,這樣我們就能看到了這樣的效果
繼續(xù)看下一個(gè)約束
[self.topView addSubview:self.topInnerView];
[self.topInnerView mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.equalTo(self.topInnerView.mas_height).multipliedBy(3);
make.width.and.height.lessThanOrEqualTo(self.topView);
make.width.and.height.equalTo(self.topView).with.priorityLow();
make.center.equalTo(self.topView);
}];
寬度是高度的3倍,這里我們又看到了新的約束方式,比例multipliedBy(3)
我們還看到了and
當(dāng)然,其沒有任何作用,僅僅是為了讀寫順暢
設(shè)置了寬度和高度的大小為小于或者等于父視圖的寬高,中心點(diǎn)的位置,確定了其是一個(gè)在父視圖中心的一個(gè)和父視圖等寬的高度是寬度1/3的一個(gè)位置和大小的視圖
至于后面的另外的一個(gè)約束就不做說明了
補(bǔ)充一句
multipler屬性表示約束值為約束對(duì)象的乘因數(shù), dividedBy屬性表示約束值為約束對(duì)象的除因數(shù)
例如我們可以寫成make.height.equalTo(self.topInnerView.mas_width).dividedBy(3);
也是一樣的效果
接下來就是基于動(dòng)畫的約束了MASExampleAnimatedView
首先是跟Base一樣的三個(gè)view的約束,只是換一個(gè)方式
UIView *superview = self;
int padding = self.padding = 10;
UIEdgeInsets paddingInsets = UIEdgeInsetsMake(self.padding, self.padding, self.padding, self.padding);
self.animatableConstraints = NSMutableArray.new;
[greenView mas_makeConstraints:^(MASConstraintMaker *make) {
[self.animatableConstraints addObjectsFromArray:@[
make.edges.equalTo(superview).insets(paddingInsets).priorityLow(),
make.bottom.equalTo(blueView.mas_top).offset(-padding),
]];
make.size.equalTo(redView);
make.height.equalTo(blueView.mas_height);
}];
[redView mas_makeConstraints:^(MASConstraintMaker *make) {
[self.animatableConstraints addObjectsFromArray:@[
make.edges.equalTo(superview).insets(paddingInsets).priorityLow(),
make.left.equalTo(greenView.mas_right).offset(padding),
make.bottom.equalTo(blueView.mas_top).offset(-padding),
]];
make.size.equalTo(greenView);
make.height.equalTo(blueView.mas_height);
}];
[blueView mas_makeConstraints:^(MASConstraintMaker *make) {
[self.animatableConstraints addObjectsFromArray:@[
make.edges.equalTo(superview).insets(paddingInsets).priorityLow(),
]];
make.height.equalTo(greenView.mas_height);
make.height.equalTo(redView.mas_height);
}];
優(yōu)先級(jí)底的我們最后看,首先是確定了各個(gè)view的位置關(guān)系,綠色下邊是藍(lán)色,紅色左邊是綠色,下邊是藍(lán)色,且三個(gè)等高,然后再分別看優(yōu)先級(jí)低的約束進(jìn)行補(bǔ)充,都是距離父視圖相同的間距,這樣我們就能夠想象出它的界面約束的關(guān)系形成的效果
很顯然,這種布局方式比base又高級(jí)了一點(diǎn).值得學(xué)習(xí)
然后我們看到,其會(huì)將某些約束關(guān)系添加到數(shù)組中保存起來,
然后
int padding = invertedInsets ? 100 : self.padding;
UIEdgeInsets paddingInsets = UIEdgeInsetsMake(padding, padding, padding, padding);
for (MASConstraint *constraint in self.animatableConstraints) {
constraint.insets = paddingInsets;
}
[UIView animateWithDuration:1 animations:^{
[self layoutIfNeeded];
} completion:^(BOOL finished) {
//repeat!
[self animateWithInvertedInsets:!invertedInsets];
}];
通過取里面的某一個(gè)constraint,來修改部分屬性達(dá)到更新約束形成動(dòng)畫的效果
這里設(shè)置的是insetsd的屬性,我們也可以試試其他的屬性
后面的MASExampleDebuggingView我就不再詳細(xì)說明了,是輔助查看約束的值的,方便檢查約束是否正確
我們可以在consle中看到自己自定的key和約束的值
接下來的是MASExampleLabelView
約束什么的就不說了
關(guān)注的是
- (void)layoutSubviews {
[super layoutSubviews];
// for multiline UILabel's you need set the preferredMaxLayoutWidth
// you need to do this after [super layoutSubviews] as the frames will have a value from Auto Layout at this point
// stay tuned for new easier way todo this coming soon to Masonry
CGFloat width = CGRectGetMinX(self.shortLabel.frame) - kPadding.left;
width -= CGRectGetMinX(self.longLabel.frame);
self.longLabel.preferredMaxLayoutWidth = width;
// need to layoutSubviews again as frames need to recalculated with preferredLayoutWidth
[super layoutSubviews];
}
這里有說明需要設(shè)置多行l(wèi)abel的時(shí)候你需要設(shè)置最大寬度,你需要在[super layoutSubviews]之后設(shè)置一個(gè)值,這樣其寬度就能夠設(shè)置成功,再之后所有的約束就都是正確的了,原因是在makeConstraints中我們看到只是設(shè)置了左上,確定了位置,但是沒有固定大小,所以需要設(shè)置寬度之后才能確定label的高度,才能繼續(xù)其他的依賴他的約束
我們?cè)俳又碝ASExampleScrollView
我們知道,scrollerView是一個(gè)可以拖動(dòng)的view,其是有一個(gè)contentSize屬性的,如果沒有設(shè)置好的話,其可能會(huì)導(dǎo)致無法下拉到底部等問題,xib我們就會(huì)遇到這樣的問題,有時(shí)候很難解決,我們看看Masonry是如何實(shí)現(xiàn)的
[contentView makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.scrollView);
make.width.equalTo(self.scrollView);
}];
[contentView makeConstraints:^(MASConstraintMaker *make) {
make.bottom.equalTo(lastView.bottom);
}];
上面先給scrollerView添加一個(gè)contentView,然后設(shè)置其位置和大小即可.是不是很方便,不需要再設(shè)置contentSize了,這里用到了bottom
這里需要補(bǔ)充自動(dòng)布局的情況下去定義contentSize是無效的。而mas_leading,mas_trailing是根據(jù)contentSize來確定的具體位置环壤,而不是根據(jù)scrollview的frame來確定晒来,因此布局得到的并不是想要得到的結(jié)果。(相互依賴)
如果設(shè)置了scrollView的contentSize镐捧,但是實(shí)際上在自動(dòng)布局的情況下潜索,contentSize的大小并不是原先設(shè)置的那樣,而是由內(nèi)容約束來定義的(leading,trailing)懂酱,后執(zhí)行的Masonry布局代碼重定義了contentSize竹习。
contentSize的height同理,由top列牺,bottom來決定
為什么上面的可以滑動(dòng)了呢?因?yàn)槲覀冊(cè)O(shè)置了contentView的上下左右,所以scrollerView自動(dòng)布局后的contentSize就是contentView的大小
接下來是MASExampleArrayView,
這里的思想是把幾個(gè)控件放在一起控制約束
- (void)setOffset:(CGFloat)offset {
_offset = offset;
[self setNeedsUpdateConstraints];
}
- (void)updateConstraints {
[self.buttonViews updateConstraints:^(MASConstraintMaker *make) {
make.baseline.equalTo(self.mas_centerY).with.offset(self.offset);
}];
//according to apple super should be called at end of method
[super updateConstraints];
}
這里的buttonViews是一個(gè)數(shù)組,數(shù)組里面是多個(gè)控件.至于約束的內(nèi)容還是之前學(xué)習(xí)的那些,不做描述
來到MASExampleAttributeChainingView
還是熟悉的界面,三個(gè)視圖
[greenView mas_makeConstraints:^(MASConstraintMaker *make) {
// chain attributes
make.top.and.left.equalTo(superview).insets(padding);
// which is the equivalent of
// make.top.greaterThanOrEqualTo(superview).insets(padding);
// make.left.greaterThanOrEqualTo(superview).insets(padding);
make.bottom.equalTo(blueView.mas_top).insets(padding);
make.right.equalTo(redView.mas_left).insets(padding);
make.width.equalTo(redView.mas_width);
make.height.equalTo(@[redView, blueView]);
}];
[redView mas_makeConstraints:^(MASConstraintMaker *make) {
// chain attributes
make.top.and.right.equalTo(superview).insets(padding);
make.left.equalTo(greenView.mas_right).insets(padding);
make.bottom.equalTo(blueView.mas_top).insets(padding);
make.width.equalTo(greenView.mas_width);
make.height.equalTo(@[greenView, blueView]);
}];
[blueView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(greenView.mas_bottom).insets(padding);
// chain attributes
make.left.right.and.bottom.equalTo(superview).insets(padding);
make.height.equalTo(@[greenView, redView]);
}];
至于約束還是之前的約束,要講的是什么呢? 其實(shí)在之前已經(jīng)說過了,上面采用了蓮式思想來進(jìn)行約束,采用.
將多個(gè)屬性進(jìn)行連接成一句話
make.left.right.and.bottom.equalTo(superview).insets(padding);
很簡單的意思,讓左右下都等于某一個(gè)約束值,這里順序沒有要求,and
可以理解成連接,沒有任何作用,僅作為讀寫順暢.
接下來MASExampleMarginView就不多說了,我們使用過xib就會(huì)知道,在iOS8.0之后使用約束的時(shí)候會(huì)有一個(gè)Margin屬性
至于意思就是會(huì)做一個(gè)保留值,而不是從0開始為從8開始的一個(gè)縮進(jìn),這個(gè)大家使用xib實(shí)驗(yàn)一下就知道了
至于MASExampleDistributeView,我們可以看到界面
幫我們實(shí)現(xiàn)了等距的效果,類似iOS9中的UIStackView.
總共有4種實(shí)現(xiàn),我們分別測試看看
[arr mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedSpacing:20 leadSpacing:5 tailSpacing:5];
[arr makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(@60);
make.height.equalTo(@60);
}];
指定頂部和高度同時(shí)指定左右間距和中間修正間距所以中間的間距是5+20+5=30個(gè)像素....
[arr mas_distributeViewsAlongAxis:MASAxisTypeVertical withFixedSpacing:20 leadSpacing:5 tailSpacing:5];
[arr makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(@0);
make.width.equalTo(@60);
}];
這是縱向的等距
說明一下
/**
- 確定間距等間距布局
- @param axisType 布局方向
- @param fixedSpacing 兩個(gè)item之間的間距(最左面的item和左邊, 最右邊item和右邊都不是這個(gè))
- @param leadSpacing 第一個(gè)item到父視圖邊距
- @param tailSpacing 最后一個(gè)item到父視圖邊距
*/
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedSpacing:(CGFloat)fixedSpacing leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing;
[arr mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedItemLength:30 leadSpacing:200 tailSpacing:30];
[arr makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(@60);
make.height.equalTo(@60);
}];
這里有點(diǎn)變化,使用的是另一個(gè)方法
/**
* distribute with fixed item size
*
* @param axisType 布局方向
* @param fixedItemLength 每個(gè)item的布局方向的長度
* @param leadSpacing 第一個(gè)item到父視圖邊距
* @param tailSpacing 最后一個(gè)item到父視圖邊距
*/
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedItemLength:(CGFloat)fixedItemLength leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing;
再看看最后一個(gè)
[arr mas_distributeViewsAlongAxis:MASAxisTypeVertical withFixedItemLength:30 leadSpacing:30 tailSpacing:200];
[arr makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(@0);
make.width.equalTo(@60);
}];
我們可以看到兩者的不同,前面一個(gè)fixedItemLength代表的是寬度,后者則是高度,前面一個(gè)leadSpacing\tailSpacing代表的是x后面的代表的則是y,
這里我們只要稍稍關(guān)注一下就可以明白
有了這樣的API,我們以后再寫等距的代碼就真的很簡單了.而不需要天天拉約束,拉等比,拉長度......
另外我們還可以看到,總共是兩種方法,有什么區(qū)別呢?
對(duì)比下來我們可以看到,第一種方法重視的是等距,而不關(guān)心沒有個(gè)控件本身的寬高,只要求等距,第二個(gè)方法則相反,重視的是等大小,只控制控件本身大小,至于間距則是等分...
如此一來,我們真的很方便,無論是要求固定大小還是要求固定間距我們都能很好的實(shí)現(xiàn).
最后關(guān)心一下MASExampleLayoutGuideViewController
[topView makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.mas_topLayoutGuide);
make.left.equalTo(self.view);
make.right.equalTo(self.view);
make.height.equalTo(@40);
}];
[topSubview makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.mas_topLayoutGuide);
make.centerX.equalTo(@0);
make.width.equalTo(@20);
make.height.equalTo(@20);
}];
[bottomView makeConstraints:^(MASConstraintMaker *make) {
make.bottom.equalTo(self.mas_bottomLayoutGuide);
make.left.equalTo(self.view);
make.right.equalTo(self.view);
make.height.equalTo(@40);
}];
我們可以看看效果
如果把make.top.equalTo(self.mas_topLayoutGuide);
改成我們平時(shí)想的make.top.equalTo(self.view);
會(huì)怎么樣呢?
OK 大致明白了,其實(shí)就是修正iOS中vc的view的frame帶來的位置偏移的因素,這里我就不說多少了.
至此,一個(gè)自動(dòng)布局框架Masonry的使用就學(xué)習(xí)完畢了,我們可以很輕松的使用期布局我們的界面,你會(huì)發(fā)現(xiàn)你再也不用糾結(jié)是使用xib還是storyboard還是使用手寫布局更方便了,你的代碼效率會(huì)大大提高的.
因?yàn)槠浯_實(shí)值得推薦...