Masonry是一个轻量级的布局框架,它拥有自己的描述语法(采用更优雅的链式语法封装)来自动布局,具有很好可读性且同时支持iOS和Max OS X等。
总之,对于侧重写代码的coder,请你慢慢忘记Frame,喜欢Masonry吧
[TOC]
常用的属性与常量
-
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
-
UIView
先来一波最为常用的使用方法,大家可以看一下大致语法,下面会细讲使用
//分别设置各个相对边距(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);
-
更加便利的约束方法
Masonry提供了一些便利的方法供我们同时创建多个不同的约束,他们被称为MASCompositeConstraints,如:
edges:
// 使一个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:
// 使得宽度和高度大于等于 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除因数 */
Masonry使用注意
使用
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
效果。
Masonry添加约束的方法
-
这个方法只会添加新的约束:
[view makeConstraints:^(MASConstraintMaker *make) { }];
-
这个方法将会覆盖以前的某些特定的约束
[view updateConstraints:^(MASConstraintMaker *make) { }];
-
这个方法会将以前的所有约束删掉,添加新的约束
``` [view remakeConstraints:^(MASConstraintMaker *make) { }]; ```
- 里面觉得最好用的是masonry_remakeConstraints,保证不会错,
-
要记得将约束写在
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); }]; }