Masonry
是一个基于AutoLayout封装的轻量级的框架,它提供简洁易读的链式自动布局语法.并且支持macOS和iOS两个平台.
安装
使用CocoaPods安装.
在podfile中导入Masonry
pod 'Masonry'
如果你不想使用mas_
前缀
把#define MAS_SHORTHAND
添加到你的prefix.pch
文件中
#define MAS_SHORTHAND
在需要用到Masonry的文件前导入
#import "Masonry.h"
NSLayoutConstraints使用中出现的问题
NSLayoutConstraints是一个非常强大且灵活的组织布局的框架.但是它的布局代码过于冗长且不易读.举个例子,使用NSLayoutConstraints语法创建一个view,并且在该view和它的父view周边有10个点的距离.
UIView *superview = self.view;
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的约束
[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],
]];
即使有这样一个简单的例子,所需的代码也是非常冗长的,并且当你有超过2或3个视图时,很快就变得不可读.另一个选择是使用Visual Format Language(VFL),代码会相对少一点。然而,ASCII类型的语法有其自己的陷阱.
使用Masonry
上述代码使用Masonry
的MASConstraintMaker
来创建会是这样的
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);
}];
注意,在添加约束前要保证子视图已经添加到了父视图中了,也就是[superView addSubview:subView]
,并且父视图已经添加了约束.
Masonry
会自动添加view1.translatesAutoresizingMaskIntoConstraints = NO;
语句.
约束语法
.equalTo
相当于NSLayoutRelationEqual
.lessThanOrEqualTo
相当于NSLayoutRelationLessThanOrEqual
.greaterThanOrEqualTo
相当于NSLayoutRelationGreaterThanOrEqual
这三个约束接受一个变量作为参数,这个变量可以是以下类型:
第一种:MASViewAttribute(MASview的属性)
make.centerX.lessThanOrEqualTo(view2.mas_left);
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 | 文本基线 |
其中leading与left ,trailing与right 在正常情况下是等价的,但是当一些布局是从右至左时(比如阿拉伯文) 则会对调,换句话说就是基本可以不理不用,用left和right就好了.
第二种: UIView/NSView
如果你想view.left大于或等于label.left:
//这两个约束是等价的
make.left.greaterThanOrEqualTo(label);
make.left.greaterThanOrEqualTo(label.mas_left);
第三种:NSNumber
自动布局允许宽度和高度设置为常量值。如果你想设置视图具有最小和最大宽度,你可以传递一个NSNumber:
//width >= 200 && width <= 400
make.width.greaterThanOrEqualTo(@200);
make.width.lessThanOrEqualTo(@400)
但是,不允许如left,right,centerY等对齐属性设置为常量值。所以,如果你通过一个NSNumber来设置这些属性,Masonry将把它们变成相对于视图的超视图的约束,即:
//创建 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));
默认情况下,支持自动装箱的宏前缀为mas_
.在prefix.pch
定义了宏之后可以不使用前缀.
第四种:NSArray
make.height.equalTo(@[view1.mas_height, view2.mas_height]);
make.height.equalTo(@[view1, view2]);
make.left.equalTo(@[view1, @100, view3.right]);
优先级
.priority
允许你指定一个确切的优先级
.priorityHigh
相当于UILayoutPriorityDefaultHigh
,高优先级
.priorityMedium
中等优先级
.priorityLow
相当于UILayoutPriorityDefaultLow
,低优先级
优先级可以像下面这样绑定到约束链的末尾:
make.left.greaterThanOrEqualTo(label.mas_left).with.priorityLow();
make.top.equalTo(label.mas_top).with.priority(600);
创建多个约束
Masonry也提供了同时创建多个约束的便利方法,这些被称为MASCompositeConstraints.
edges
// 上下左右和view2相等
make.edges.equalTo(view2);
// 让 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
// 让宽和高大于或等于titleLabel
make.size.greaterThanOrEqualTo(titleLabel)
// 让 width = superview.width + 100, height = superview.height - 50
make.size.equalTo(superview).sizeOffset(CGSizeMake(100, -50))
center
// 与button1的中心点对齐
make.center.equalTo(button1)
// 让 centerX = superview.centerX - 5, centerY = superview.centerY + 10
make.center.equalTo(superview).centerOffset(CGPointMake(-5, 10))
可以使用链式语法提供可读性:
make.left.right.and.bottom.equalTo(superview);
make.top.equalTo(otherView);
布局动态更新
如果需要修改现有约束,用删除/替换约束,可以这样操作:
1.使用属性
您可以通过将约束make表达式的结果分配给局部变量或类属性来保留特定约束的引用。您也可以通过将它们存储在一个数组中来引用多个约束。
// 在interface中定义
@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];
2.使用mas_updateConstraints
如果你只是更新约束的常量值,可以使用mas_updataConstraints
代替mas_makeConstraints
:
/*
* updateConstraints是苹果官方推荐的更新布局的方法
* 当响应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];
}
3.使用mas_remakeConstraints
mas_updateConstraints
是用来更新就布局的常量的,但如果要加入新的布局约束,则无能为力.
mas_remakeConstraints
不是更新常量值,而是在再次安装它们之前删除所有的约束条件.这可以让你提供不同的约束:
- (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;
// --- 在这里创建view---
self.button = [[UIButton alloc] init];
return self;
}
// 通知UIKit你在使用AutoLayout
+ (BOOL)requiresConstraintBasedLayout {
return YES;
}
// 这是苹果推荐的添加或更新视图的方法
- (void)updateConstraints {
// --- 在这里重置或更新你的视图
[self.button remakeConstraints:^(MASConstraintMaker *make) {
make.width.equalTo(@(self.buttonSize.width));
make.height.equalTo(@(self.buttonSize.height));
}];
// 调用super方法
[super updateConstraints];
}
- (void)didTapButton:(UIButton *)button {
// --- 更改布局参数 ---
self.buttonSize = CGSize(200, 200);
// 通知系统需要变化布局
[self setNeedsUpdateConstraints];
}
@end