Masonry是一个轻量级的布局框架,它拥有本身的描述语法(采用更优雅的链式语法封装)来自动布局,具备很好可读性且同时支持iOS和Max OS X等。
总之,对于侧重写代码的coder,请你慢慢忘记Frame,喜欢Masonry吧app
MASViewAttribute 以对应的系统类型框架
MASViewAttribute | NSLayoutAttribute |
---|---|
view.mas_left | NSLayoutAttributeLeft |
view.mas_right | NSLayoutAttributeRight |
view.mas_top | NSLayoutAttributeTop |
view.mas_bottom | NSLayoutAttributeBottom |
view.mas_leading | NSLayoutAttributeLeading |
view.mas_trailing | NSLayoutAttributeTrailing |
view.mas_width | NSLayoutAttributeWidth |
view.mas_height | NSLayoutAttributeHeight |
view.mas_centerX | NSLayoutAttributeCenterX |
view.mas_centerY | NSLayoutAttributeCenterY |
view.mas_baseline | NSLayoutAttributeBaseline |
UIViewless
先来一波最为经常使用的使用方法,你们能够看一下大体语法,下面会细讲使用dom
//分别设置各个相对边距(superview为view的父类视图,下同) make.left.mas_equalTo(superView.mas_left).mas_offset(10); make.right.mas_equalTo(superView.mas_right).mas_offset(-10); make.top.mas_equalTo(superView.mas_top).mas_offset(10); make.bottom.mas_equalTo(superView.mas_bottom).offset(-10); //直接链接使用left大于等于某个值 make.left.mas_greaterThanOrEqualTo(10); //设置宽和高 make.width.mas_equalTo(60); make.height.mas_equalTo(60); //.设置center和宽高比 make.center.mas_equalTo(superView); make.width.mas_equalTo(superView).multipliedBy(1.00/3); make.height.mas_equalTo(superView).multipliedBy(0.25); //.关于约束优先级,此处要注意约束冲突的问题,统一约束优先级大的生效 make.left.mas_equalTo(100); make.left.mas_equalTo(view.superview.mas_left).offset(10); make.left.mas_equalTo(20).priority(700); make.left.mas_equalTo(40).priorityHigh(); make.left.mas_equalTo(60).priorityMedium(); make.left.mas_equalTo(80).priorityLow(); //若是你想让view的(x坐标)左边大于等于label的左边,如下两个约束的写法效果同样 make.left.greaterThanOrEqualTo(label); make.left.greaterThanOrEqualTo(label.mas_left);
注:约束的链式写法中,不包含其余相对的view时,默认为其superview,即make.left.mas_equalTo(100);
等价于make.left.mas_equalTo(view.superview.mas_left).offset(10);
和make.left.mas_equalTo(view.superview).offset(10);
ide
更加便利的约束方法布局
Masonry提供了一些便利的方法供咱们同时建立多个不一样的约束,他们被称为MASCompositeConstraints,如:动画
edges:ui
// 使一个view的top, left, bottom, right 等于view2的 make.edges.equalTo(view2); //相对于superviewde上左下右边距分别为5,10,15,20 make.edges.equalTo(superview).insets(UIEdgeInsetsMake(5, 10, 15, 20))
size:this
// 使得宽度和高度大于等于 titleLabel make.size.greaterThanOrEqualTo(titleLabel) // 相对于superview宽度大100,高度小50 make.size.equalTo(superview).sizeOffset(CGSizeMake(100, -50))
center:
//中心与button1对齐 make.center.equalTo(button1) //水平方向中心相对向左偏移5,竖直方向中心向下偏移10 make.center.equalTo(superview).centerOffset(CGPointMake(-5, 10))
你能够在约束链里添加相应的view来增长代码的可读性:
// 除了top,全部的边界与superview对齐 make.left.right.and.bottom.equalTo(superview); make.top.equalTo(otherView);
NSNumber
自动布局容许使用常量去设置宽或高,若是你想经过一个数字设置一个view的最小和最大的width,能够用equality blocks,以下:
//width >= 200 && width <= 400 make.width.greaterThanOrEqualTo(@200); make.width.lessThanOrEqualTo(@400)
然而自动布局不容许对齐属性的约束(如:left,right,centerY等)设置为常量值,你可使用NSNumber来设置相对于父类view这些约束属性,如:
// creates view.left = view.superview.left + 10 make.left.lessThanOrEqualTo(@10)
若是你不想使用NSNumber来设置,也能够用以下结构来建立你的约束,如:
make.top.mas_equalTo(42); make.height.mas_equalTo(20); make.size.mas_equalTo(CGSizeMake(50, 100)); make.edges.mas_equalTo(UIEdgeInsetsMake(10, 0, 10, 0)); make.left.mas_equalTo(view).mas_offset(UIEdgeInsetsMake(10, 0, 10, 0));
NSArray
用数组添加集中不一样类的约束,如:
make.height.equalTo(@[view1.mas_height, view2.mas_height]); make.height.equalTo(@[view1, view2]); make.left.equalTo(@[view1, @100, view3.right]);
常见约束的各类类型
/** 1.尺寸:width、height、size 2.边界:left、leading、right、trailing、top、bottom 3.中心点:center、centerX、centerY 4.边界:edges 5.偏移量:offset、insets、sizeOffset、centerOffset 6.priority()约束优先级(0~1000),multipler乘因数, dividedBy除因数 */
mas_makeConstraints
方法的元素必须 事先 添加到父元素中,例如[self.view addSubView:view]
;mas_equalTo
和equalTo
的区别:
equalTo
:仅支持基本类型;mas_equalTo
:支持类型转换,支持复杂类型。是对equalTo的封装。支持CGSize CGPoint NSNumber UIEdgeinsets。如下实现的是相同的效果:make.width.equalTo(@100);
跟make.width.mas_equalTo(100);
mas_equalTo
是一个Macro,比较 值;equalTo
比较View。如下实现的是相同的效果make.bottom.mas_equalTo(ws.view.mas_bottom);
跟make.bottom.equalTo(ws.view);
mas_equalTo
比equalTo
多了类型转换操做,大多数时候两个方法是 通用的。可是
mas_equalTo
;equalTo
;(特别的多个属性时,必须使用equalTo
,例如make.left.and.right.equalTo(self.view)
)去掉mas_前缀,只用equalTo,只须要把下面代码添加到.prefix文件:
// 只要添加了这个宏,就不用带mas_前缀(`equalTo`就等价于`mas_equalTo`) #define MAS_SHORTHAND // 对于默认的约束参数自动装箱 #define MAS_SHORTHAND_GLOBALS // 这个头文件,必定要放在上面两个宏的后面 #import "Masonry.h"
注意点方法with
和and
,这两个方法其实没有作任何操做,方法只是返回对象自己,这个方法的做用,彻底是为了可读性。
make.left.and.right.equalTo(self.view);
和make.left.right.equalTo(self.view);
是彻底同样的,可是加了and
方法的语法,可读性更好。
multipliedBy
的使用只能是设置同一个控件的,好比这里的bottomInnerView
,make.height.mas_equalTo(bottomInnerView.mas_width).multipliedBy(3);
简化:
[iconView makeConstraints:^(MASConstraintMaker *make){ make.top.equalTo(self.view).with.offset(30); make.left.equalTo(self.view).with.offset(30); make.bottom.equalTo(self.view).with.offset(-30); make.right.equalTo(self.view).with.offset(-30); }]
能够简化为
make.top.left.bottom.and.right.equalTo(self.view).with.insets(UIEdgeInsetsMake(10,10,10,10));
或
make.edges.equalTo(self.view).insets(UIEdgeInsetsMake(30.30.30.30));
其中leading
与left trailing
与right
在正常状况下是等价的,可是在一些布局是从右至左时(好比阿拉伯文),则会对调,因此基本能够不理不用。用left
和right
就好。用leading
或trailing
后就不要用left
或right
,若是混用会出现崩溃。
对label
的约束比必须设置最大的约束宽度;
self.titleLabel.preferredMaxLayoutWidth = w - 100;
由于iOS中原点在左上角因此注意使用offset时注意right和bottom用负数
使用Masonry
不须要设置控件的translatesAutoresizingMaskIntoConstraints
属性为NO
,(错误观点:为防止block
中的循环引用,使用弱引用),由于在这里block
是局部的引用,block
内部引用self
不会形成循环应用的。(不必的写法:__weak typeof (self) weakSelf = self;
)
Masonry约束控件出现冲突的问题:当约束冲突发生的时候,咱们能够设置view
的key
来定位是哪一个view。
好比:
redView.mas_key = @"redView"; greenView.mas_key = @"greenView"; blueView.mas_key = @"blueView";
若是以为这样一个个设置比较繁琐,Masonry
提供了批量设置的宏:
// 一句代码便可所有设置 MASAttachKeysMASAttachKeys(redView,greenView,blueView);
.priority
容许你指定一个精确的优先级,数值越大优先级越高,最高1000
。
priorityHigh
等价于UILayoutPriorityDefaultHigh
,优先级值为750
。priorityMedium
介于高优先级和低优先级之间,优先级值在250~750
之间。priorityLow
等价于UILayoutPriorityDefaultLow
,优先级值为250
。优先级能够在约束的尾部添加:
make.left.greaterThanOrEqualTo(label.mas_left).with.priorityLow(); make.top.equalTo(label.mas_top).with.priority(600);
Greater/Less
通常与Priority
一块儿使用,为一个Constraint
设置了Greater/Less
后,调整Priority
。若是Constraint
的Priority
的值越大,程序优先设置它的Constraint
效果。
这个方法只会添加新的约束:
[view makeConstraints:^(MASConstraintMaker *make) { }];
这个方法将会覆盖之前的某些特定的约束
[view updateConstraints:^(MASConstraintMaker *make) { }];
这个方法会将之前的全部约束删掉,添加新的约束
``` [view remakeConstraints:^(MASConstraintMaker *make) { }]; ```
要记得将约束写在updateConstraints
里面:
- (void) updateConstraints{ }
而后调用下面这一串:
[self setNeedsUpdateConstraints]; [self updateConstraintsIfNeeded]; [self layoutIfNeeded];
不然不会更新。
setNeedsLayout:
setNeedsLayout:
告知页面须要刷新,可是不会马上开始更新。执行后会马上调用layoutSubviews
。layoutIfNeed:
告知页面布局马上更新。因此通常都会和setNeedsLayout
一块儿使用。若是但愿马上生成新的frame
须要调用此方法。利用这点:通常布局动画能够在更新布局后直接使用这个方法让动画生效。layoutSubViews:
系统重写布局setNeedsUpdateConstraints:
告诉须要更新约束,可是不会马上开始。updateConstraintsIfNeeded:
告知马上更新约束。updateConstraints:
系统更新约束。有时候,你为了实现动画或者移除替换一些约束时,你须要去修改一些已经存在的约束,Masonry提供了一些不一样的方法去更新约束,你也能够将多个约束存在数组里。
References
你能够持有某个特定的约束,让其成为成员变量或者属性
//设置为公共或私接口
@property (nonatomic, strong) MASConstraint *topConstraint; ... // 添加约束 [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); }]; ... // 而后能够调用 //该约束移除 [self.topConstraint uninstall]; //从新设置value,最经常使用 self.topConstraint.mas_equalTo(20); //该约束失效 [self.topConstraint deactivate]; //该约束生效 [self.topConstraint activate];
mas_updateConstraints
若是你只是想更新一下view对应的约束,可使用 mas_updateConstraints 方法代替 mas_makeConstraints方法
//这是苹果推荐的添加或者更新约束的地方
// 在响应setNeedsUpdateConstraints方法时,这个方法会被调用屡次
// 此方法会被UIKit内部调用,或者在你触发约束更新时调用
- (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); }]; //调用super [super updateConstraints]; }
mas_remakeConstraints
mas_updateConstraints只是去更新一些约束,然而有些时候修改一些约束值是没用的,这时候mas_remakeConstraints就能够派上用场了
mas_remakeConstraints某些程度类似于mas_updateConstraints,但不一样于mas_updateConstraints去更新约束值,他会移除以前的view的全部约束,而后再去添加约束
- (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); } }];
苹果官方建议:添加/更新约束在updateConstraints
这个方法内
// this is Apple's recommended place for adding/updating constraints - (void)updateConstraints { //更新约束 [self.btn 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]; }
贴一个官方说明的例子:
@implementation DIYCustomView - (id)init { self = [super init]; if (!self) return nil; // --- Create your views here --- self.button = [[UIButton alloc] init]; return self; } // tell UIKit that you are using AutoLayout + (BOOL)requiresConstraintBasedLayout { return YES; } // this is Apple's recommended place for adding/updating constraints - (void)updateConstraints { // --- remake/update constraints here [self.button remakeConstraints:^(MASConstraintMaker *make) { make.width.equalTo(@(self.buttonSize.width)); make.height.equalTo(@(self.buttonSize.height)); }]; //according to apple super should be called at end of method [super updateConstraints]; } - (void)didTapButton:(UIButton *)button { // --- Do your changes ie change variables that affect your layout etc --- self.buttonSize = CGSize(200, 200); // tell constraints they need updating [self setNeedsUpdateConstraints]; }
多个(2个以上)控件的等间隔排序显示
/** * axisType 轴线方向 * fixedSpacing 间隔大小 * fixedItemLength 每一个控件的固定长度/宽度 * leadSpacing 头部间隔 * tailSpacing 尾部间隔 * */ //1. 等间隔排列 - 多个控件间隔固定,控件长度/宽度变化 - (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedSpacing:(CGFloat)fixedSpacing leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing; //2. 等间隔排列 - 多个固定大小固定,间隔空隙变化 - (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedItemLength:(CGFloat)fixedItemLength leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing;
多行label的约束问题
//建立label self.label = [UILabel new]; self.label.numberOfLines = 0; self.label.lineBreakMode = NSLineBreakByTruncatingTail; self.label.text = @"有的人,没事时喜欢在朋友圈里处处点赞,东评论一句西评论一句,比谁都有存在感。等你有事找他了,他就马上变得很忙,让你再也找不着。真正的朋友,日常不多联系。可一旦你赶上了难处,他会马上回复你的消息,第一时间站出来帮你。所谓的存在感,不是你有没有出现,而是你的出现有没有价值。存在感,不是刷出来的,也不是说出来的。有存在感,未必是要个性锋芒毕露、甚至锋利扎人。翩翩君子,温润如玉,真正有存在感的人,反而不会刻意去强调他的存在感。他的出现,永远都恰到好处。我所欣赏的存在感,不是长袖善舞巧言令色,而是对他人的真心关照;不是锋芒毕露计较胜负,而是让人相处得舒服;不是时时刻刻聒噪不休,而是关键时刻能自告奋勇。别总急着出风头,但愿你能有恰到好处的存在感。"; [self addSubview: self.label]; [self.label makeConstraints:^(MASConstraintMaker *make) { make.left.top.equalTo(10); make.right.equalTo(-10); }]; //添加约束 - (void)layoutSubviews { //1. 执行 [super layoutSubviews]; [super layoutSubviews]; //2. 设置preferredMaxLayoutWidth: 多行label约束的完美解决 self.label.preferredMaxLayoutWidth = [UIScreen mainScreen].bounds.size.width - 20; //3. 设置preferredLayoutWidth后,须要再次执行 [super layoutSubviews]; //其实在实际中这步不写,也不会出错,官方解释是说设置preferredLayoutWidth后须要从新计算并布局界面,因此这步最好执行 [super layoutSubviews]; }
UIScrollView的问题
原理同自动布局同样 UIScrollView上添加UIView
UIView上添加须要显示的控件 UIScrollView滚动高度取决于显示控件的总高度
对子控件作好约束,可达到控制UIView的大小
//建立滚动视图 UIScrollView *scrollView = [UIScrollView new]; self.scrollView = scrollView; scrollView.backgroundColor = [UIColor grayColor]; [self addSubview:scrollView]; [self.scrollView makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(self); }]; [self setUpContentView]; //添加内容视图 - (void)setUpContentView { //约束UIScrollView上contentView UIView *contentView = [UIView new]; [self.scrollView addSubview:contentView]; [contentView makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(self.scrollView); make.width.equalTo(self.scrollView); //此处必填 - 关键点 }]; //添加控件到contentView,约束原理与自动布局相同 UIView *lastView; CGFloat height = 30; for (int i = 0; i <1 5; i ++) { UIView *view = UIView.new; view.backgroundColor = [UIColor colorWithRed:arc4random() % 255 / 256.0 green:arc4random() % 255 / 256.0 blue:arc4random() % 255 / 256.0 alpha:1.0]; [contentView addSubview:view]; [view makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(lastView ? lastView.bottom : @0); make.left.equalTo(0); make.width.equalTo(contentView.width); make.height.equalTo(height); }]; height += 30; lastView = view; } [contentView makeConstraints:^(MASConstraintMaker *make) { make.bottom.equalTo(lastView.bottom); }]; }