Masonry的一些用法
在使用Masonry时,如果不想使用mas_
前缀,可在.pch
文件中,在导入Masonry.h
之前,定义MAS_SHORTHAND
//define this constant if you want to use Masonry without the 'mas_' prefix
//如果使用Masonry时,不带有`mas_`前缀,定义该常量
#define MAS_SHORTHAND
//define this constant if you want to enable auto-boxing for default syntax
//如果你想启用auto-boxing,则定义该常量
#define MAS_SHORTHAND_GLOBALS
#import "Masonry.h"
By default, macros which support
autoboxing
are prefixed withmas_
. Unprefixed versions are available by definingMAS_SHORTHAND_GLOBALS
before importing Masonry.
默认情况下,mas_
开头的是自动支持autoboxing
,对于非mas_
开头的,在导入Masonry之前,要定义MAS_SHORTHAND_GLOBALS
在源码中mas_equalTo
定义如下,MASBoxValue
相当与是自动装箱,所以支持原始值
#define mas_equalTo(...) equalTo(MASBoxValue((__VA_ARGS__)))
添加基本约束
UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(superview.mas_top).with.offset(padding.top); //with is an optional semantic filler
make.left.equalTo(superview.mas_left).with.offset(padding.left);
make.bottom.equalTo(superview.mas_bottom).with.offset(-padding.bottom);
make.right.equalTo(superview.mas_right).with.offset(-padding.right);
}];
上面的代码还可以继续简化
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(superview).with.insets(padding);
}];
Masonry会自动调用view1.translatesAutoresizingMaskIntoConstraints = NO;
除了使用.equalTo
,还可以使用.lessThanOrEqualTo
和.greaterThanOrEqualTo
如果想要 view.left
大于或者等于 label.left
//下面2个约束是一样的
make.left.greaterThanOrEqualTo(label);
make.left.greaterThanOrEqualTo(label.mas_left);
Auto Layout允许width和height设置为常量值。如果你想设置view有一个最小宽度和最大宽度:
//width >= 200 && width <= 400
make.width.greaterThanOrEqualTo(@200);
make.width.lessThanOrEqualTo(@400)
然而Auto Layout并不允许对齐属性attributes,例如left、right、centerY等设置为常量。如果个这些值传递常量,Masonry会自动转为相对于superview的约束:
//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]);
.priority
allows you to specify an exact priority 指定一个确定的优先级
.priorityHigh
equivalent to UILayoutPriorityDefaultHigh
.priorityMedium
is half way between high and low
.priorityLow
equivalent to UILayoutPriorityDefaultLow
优先级可放在约束链的尾部
make.left.greaterThanOrEqualTo(label.mas_left).with.priorityLow();
make.top.equalTo(label.mas_top).with.priority(600);
有一些便利方法可在同一时间创建多个约束,称为MASCompositeConstraints
edges
// make top, left, bottom, right equal view2
make.edges.equalTo(view2);
// make top = superview.top + 5, left = superview.left + 10,
// bottom = superview.bottom - 15, right = superview.right - 20
make.edges.equalTo(superview).insets(UIEdgeInsetsMake(5, 10, 15, 20))
size
// make width and height greater than or equal to titleLabel
make.size.greaterThanOrEqualTo(titleLabel)
// make width = superview.width + 100, height = superview.height - 50
make.size.equalTo(superview).sizeOffset(CGSizeMake(100, -50))
center
// make centerX and centerY = button1
make.center.equalTo(button1)
// make centerX = superview.centerX - 5, centerY = superview.centerY + 10
make.center.equalTo(superview).centerOffset(CGPointMake(-5, 10))
可以将属性链起来增加可读性:
// All edges but the top should equal those of the superview
make.left.right.and.bottom.equalTo(superview);
make.top.equalTo(otherView);
1.持有引用
持有一个特定约束的引用,作为一个本地的变量或者类的属性
// 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
如果你只是更新约束的常量值,可以使用mas_updateConstraints
代替mas_makeConstraints
// 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_updateConstraints
用来更新一系列约束时非常有用,
mas_remakeConstraints
与mas_updateConstraints
类似,它在install约束之前,会移除所有的约束
- (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);
}
}];
}
在哪儿创建约束呢?
@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];
}
@end
//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);
}];
//with is semantic and option
[redView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(superview.mas_top).with.offset(padding); //with with
make.left.equalTo(greenView.mas_right).offset(padding); //without with
make.bottom.equalTo(blueView.mas_top).offset(-padding);
make.right.equalTo(superview.mas_right).offset(-padding);
make.width.equalTo(greenView.mas_width);
make.height.equalTo(@[greenView, blueView]); //can pass array of views
}];
[blueView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(greenView.mas_bottom).offset(padding);
make.left.equalTo(superview.mas_left).offset(padding);
make.bottom.equalTo(superview.mas_bottom).offset(-padding);
make.right.equalTo(superview.mas_right).offset(-padding);
make.height.equalTo(@[greenView.mas_height, redView.mas_height]); //can pass array of attributes
}];
[purpleView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(@20);
make.left.equalTo(@20);
make.bottom.equalTo(@-20);
make.right.equalTo(@-20);
}];
// auto-boxing macros allow you to simply use scalars and structs, they will be wrapped automatically
[orangeView mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(CGPointMake(0, 50)); //中心Y偏移Y50
make.size.equalTo(CGSizeMake(200, 100)); //设置size
}];
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;
}
类似于图片的Aspect Fit模式
// Inner views are configured for aspect fit with ratio of 3:1
[self.topView addSubview:self.topInnerView];
[self.topInnerView mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.equalTo(self.topInnerView.mas_height).multipliedBy(3); //宽度为高度的3倍
make.width.and.height.lessThanOrEqualTo(self.topView);//width height小于或等于父view
make.width.and.height.equalTo(self.topView).with.priorityLow();//width height等于父view 低优先级
make.center.equalTo(self.topView); //居中
}];
[self.bottomView addSubview:self.bottomInnerView];
[self.bottomInnerView mas_makeConstraints:^(MASConstraintMaker *make) {
make.height.equalTo(self.bottomInnerView.mas_width).multipliedBy(3); //高度为宽度的3倍
make.width.and.height.lessThanOrEqualTo(self.bottomView);
make.width.and.height.equalTo(self.bottomView).with.priorityLow();
make.center.equalTo(self.bottomView);
}];
如果是多行label,需要设置lablel的preferredMaxLayoutWidth
例子中是在view的layoutSubviews方法中,设置这个值,但是在模拟器上运行时,有bug,官方demo如下:
- (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];
}
对于一行有多个label时,可能还需要设置ContentHuggingPriority
和ContentCompressionResistance
的优先级
[label1 setContentHuggingPriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisHorizontal];
可参考:
先给scrollView添加约束,在scrollView上添加一个contentView,在contentView上添加子view,相当于将scrollview撑起来
- (id)init {
self = [super init];
if (!self) return nil;
UIScrollView *scrollView = UIScrollView.new;
self.scrollView = scrollView;
scrollView.backgroundColor = [UIColor grayColor];
[self addSubview:scrollView];
[self.scrollView makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self);
}];
[self generateContent];
return self;
}
- (void)generateContent {
UIView* contentView = UIView.new;
[self.scrollView addSubview:contentView];
[contentView makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.scrollView);
make.width.equalTo(self.scrollView);
}];
UIView *lastView;
CGFloat height = 25;
for (int i = 0; i < 10; i++) {
UIView *view = UIView.new;
view.backgroundColor = [self randomColor];
[contentView addSubview:view];
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTap:)];
[view addGestureRecognizer:singleTap];
[view mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(lastView ? lastView.bottom : @0);
make.left.equalTo(@0);
make.width.equalTo(contentView.width);
make.height.equalTo(@(height));
}];
height += 25;
lastView = view;
}
[contentView makeConstraints:^(MASConstraintMaker *make) {
make.bottom.equalTo(lastView.bottom);
}];
}
可以同时对多个view的数组,同时进行更新约束
- (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];
}
[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]);
}];
即可以均分多个view
- (id)init {
self = [super init];
if (!self) return nil;
NSMutableArray *arr = @[].mutableCopy;
for (int i = 0; i < 4; i++) {
UIView *view = UIView.new;
view.backgroundColor = [self randomColor];
view.layer.borderColor = UIColor.blackColor.CGColor;
view.layer.borderWidth = 2;
[self addSubview:view];
[arr addObject:view];
}
unsigned int type = arc4random()%4;
//type = 3;
switch (type) {
case 0:
[arr mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedSpacing:20 leadSpacing:5 tailSpacing:5];
[arr makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(@60);
make.height.equalTo(@60);
}];
break;
case 1:
[arr mas_distributeViewsAlongAxis:MASAxisTypeVertical withFixedSpacing:20 leadSpacing:5 tailSpacing:5];
[arr makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(@0);
make.width.equalTo(@60);
}];
break;
case 2:
[arr mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedItemLength:30 leadSpacing:200 tailSpacing:30];
[arr makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(@60);
make.height.equalTo(@60);
}];
break;
case 3:
[arr mas_distributeViewsAlongAxis:MASAxisTypeVertical withFixedItemLength:30 leadSpacing:30 tailSpacing:200];
[arr makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(@0);
make.width.equalTo(@60);
}];
break;
default:
break;
}
return self;
}
有关于修改约束,Apple文档介绍如下:Changing Constraints
Update Pass
是:
The system traverses the view hierarchy and calls the
updateViewConstraints
method on all view controllers, and theupdateConstraints
method on all views. You can override these methods to optimize changes to your constraints系统遍历view层级,在控制器中调用
updateViewConstraints
方法,在view上调用updateConstraints
方法
所以在Masonry
中,也是重写了view的updateConstraints
方法(注意在方法最后要调用父类的方法),调用setNeedsUpdateConstraints
方法
要注意,不要在updateConstraints
方法中调用setNeedsUpdateConstraints
方法
其demo如下(有修改):
// 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) priorityHigh];
[make.height.lessThanOrEqualTo(self) priorityHigh];
}];
//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];
}];
}
还有就是,如果更新约束时要做动画,建议如下:
[containerView layoutIfNeeded];
[UIView animateWithDuration:1.0 animations:^{
// Make all constraint changes here
[containerView layoutIfNeeded];
}];
效果如下:
使用remakeConstraints
方法,如下:
// this is Apple's recommended place for adding/updating constraints
- (void)updateConstraints {
[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);
}
}];
//according to apple super should be called at end of method
[super updateConstraints];
}
- (void)toggleButtonPosition {
self.topLeft = !self.topLeft;
// 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];
}];
}
效果如下:
在使用过程中,如果设置约束不正确,调试起来会很麻烦
在控制台通过查看提示的信息,来对照view,但如果view复杂,就不知道那个view对应的是哪个view
Masonry也提供了调试功能,通过添加debug key来调试,如:
//you can attach debug keys to views like so:
greenView.mas_key = @"greenView";
redView.mas_key = @"redView";
blueView.mas_key = @"blueView";
superview.mas_key = @"superview";
或者如下的形式:
//OR you can attach keys automagically like so:
MASAttachKeys(greenView, redView, blueView, superview);
加入key后,警告将会变成如下的形式:
[blueView mas_makeConstraints:^(MASConstraintMaker *make) {
//you can also attach debug keys to constaints
make.edges.equalTo(@1).key(@"ConflictingConstraint"); //composite constraint keys will be indexed
make.height.greaterThanOrEqualTo(@5000).key(@"ConstantConstraint");
make.top.equalTo(greenView.mas_bottom).offset(padding);
make.left.equalTo(superview.mas_left).offset(padding);
make.bottom.equalTo(superview.mas_bottom).offset(-padding).key(@"BottomConstraint");
make.right.equalTo(superview.mas_right).offset(-padding);
make.height.equalTo(greenView.mas_height);
make.height.equalTo(redView.mas_height).key(@340954); //anything can be a key
}];
可参考: