Masonry是一个强大、简洁的基于NSLayoutContraints的自动布局框架,使用可链接的表达语法。支持iOS和OS X自动布局。
Masonry
Masonry一直在积极维护,我们致力于修改bug并且从更广泛的社区合并更好的品质。然而,如果你的项目使用swift语言,我们建议你使用SnapKit,因为它使用更简单的API更安全的提供更好的类。
Masonry是一个轻量级的布局框架,它使用更好的语法包装自动布局。Masonry拥有它自己的布局DSL,它提供了可链接方式描述你的NSLayoutConstraints,使用了它的布局代码更简洁跟具有可读性。Masonry支持iOS和MAC OS X。
查看Masonry工作空间的MasonryiOS Examples项目的例子。在下载之后你需要运行pod install。
NSLayoutConstraints有什么弊端
在引擎盖下的AutoLayout是一个强大且灵活的组织并布局你的视图的方式。然而,使用代码创建约束是啰嗦的并且不好描述。想象一个简单的例子,你想要拥有一个视图它比他的父视图的每个边都缩进10像素。
UIView *superview = self;
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 constraints
[NSLayoutConstraints constraintWithItem:view1
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:padding.top],
[NSLayoutConstraints constraintWithItem:view1
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:padding.left]
[NSLayoutConstraints constraintWithItem:view1
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:padding.bottom]
[NSLayoutConstraints constraintWithItem:view1
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeRight
multiplier:1.0
constant:padding.right]
]];
即使如此简单的一个例子,需要的代码也确实非常啰嗦而且当你需要超过2个或3个视图时,代码迅速变的不可读了。另一个选择是使用Visual格式语言(VFL),它确实少了许多代码。然而,ASCII类型语法有它自己的缺陷并且对于动画有点困难,由于NSLayoutConstraint constrainsWithVisuaFormat返回一个数组。
准备满足你的设备!
这是用MASConstraintMaker创造的相同的约束。
UIEdgeInsets padding = UIEdgeInsetsMake(10,10,10,10);
[view1mas_makeConstraints:^(MASConstraintMaker *make){
make.top.equalTo(superview.mas_top).with.offset(padding.top);
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);
}];
或甚至更短
[view1mas_makeConstraints:^(MASConstraintMaker *maker){
make.edges.equalTo(superview).with.insets(padding);
}];
还注意到,在第一个例子中,我们必须添加约束给superview[superview addConstraints:],Masonry将会自动为适当的视图添加约束。
Masonry也会为你调用view1.translatesAutoresingMaskIntoConstraints = NO.
不是所有的约束条件都是equal
equalTo相当于NSLayoutRelationEqual。
lessThanOrEqualTo相当于NSLayoutRelationLessThanOrEqual。
greaterThanOrEqual相当于NSLayoutRelationGreaterThanOrEqual。
1. MASViewAttribute
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 |
2. UIView/NSView
如果你想要view.left大于等于label.left
//下面的两个约束相同
make.left.greaterThanEqualTo(label);
make.left.greaterThanOrEqualTo(label.left)
3. NSNumber
自动布局允许宽和高被设置为常量。如果你想要设置视图拥有最小和最大宽度,你可以通过设置一个数字等于block块。
//width >= 200 && width <= 400
make.width.greaterThanOrEqualTo(@200);
make.width.lessThanOtEqualTo(@400);
但是自动布局不允许对齐属性就像left,right,centerX,等等被设置为常量。因此如果你为这些属性传递一个NSNumber,Masonry就会把这些约束关联到视图的父视图上:
//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));
默认情况下,支持自动装箱的宏的前缀是mas_.无前缀的版本可以通过在导入 Masonry前定义MAS_SHOPTHAND_GLOBALS实现.
4. 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
//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 equalto 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 ofthe superview
make.left.right.and.bottom.equalTo(superview);
make.top.equalTo(otherView);
为了美好的生活坚持
有时候你需要为了动画效果修改,移除或者替代已经存在的约束。在Masonry有一些不同的方法来更新约束。
1. References
你可以通过指定约束的结果来坚持参考一个特定约束表达到一个局部变量或者类属性。你也可以通过存储多个约束到一个数组来参考多个约束。
//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 foradding/updating constraints
//this method can get called multiple times inresponse to setNeedsUpdateConstraints
//whice can be called by UIKit internally orin your code if you need to trigger an update
-(void)updateConstraints{
[self.growingButtonmas_updateConstraints:^(MASConstraintMaker *make){
make.center.equalTo(self);
make.width.equalTo(@(self.buttonSize.width)).priorityLow();
make.height.equalTo(@(self.buttonSize.height)).priotiyyLow();
make.width.lessThanOrEqualTo(self);
make.height.lessThanOrEqualTo(self);
}];
//according to apple super should be called atend of method
[super updateConstriants];
}
3. mas_ remakeConstraints
对于更行一个约束集来说mas_updateConstraints是有用的,但是做一些超出更新常量的事情时会很辛苦。到了使用mas_remakeConstraints的时候了。
mas_remakeConstraints和mas_updateConstraints很相似,但是替代了更新常量功能,它会在再次初始化之前移除的所有约束。这个使你提供不同于必须保存周围的参考的给那些你想要移除的约束。
-(void)changeButtonPosition{
[self.buttonmas_remakeConstraints:^(MASConstraintMaker *make){
make.size.equalTo(self.buttonSize);
if(topLeft){
make.top.and.left.offset(10);
}else{
make.bottom.and.right.offset(-10);
}
}];
}
你在Masonry iOSExample项目中可以查找更多关于这三个方法的详细例子。
当爱好者遇到^&*!@时
布局你的视图时不是总合乎你的计划。所以当事情呈现梨形时,你不想看到控制台输出这样的内容:
Unableto simultaneously satisfy constraints.....blah blah blah....
(
"<NSLayoutConstraint:0x7189ac0V:[UILabel:0x7186980(>=5000)]>",
"<NSAutoresizingMaskLayoutConstraint:0x839ea20 h=--&v=--& V:[MASExampleDebuggingView:0x7186560(416)]>",
"<NSLayoutConstraint:0x7189c70UILabel:0x7186980.bottom == MASExampleDebuggingView:0x7186560.bottom -10>",
"<NSLayoutConstraint:0x7189560V:|-(1)-[UILabel:0x7186980] (Names:'|':MASExampleDebuggingView:0x7186560 )>"
)
Willattempt to recover by breaking constraint
<NSLayoutConstraint:0x7189ac0 V:[UILabel:0x7186980(>=5000)]>
Masonry为NSLayoutConstraint添加了一个类别,它覆盖了-(NSString *)description默认的实现。现在,你可以给予视图和约束更有意义的名字,并且可以简单选出由Masonry创建的约束。
这意味着你的控制台输出现在看起来像这样:
Unableto simultaneously satisfy constraints......blah blah blah....
(
"<NSAutoresizingMaskLayoutConstraint:0x8887740MASExampleDebuggingView:superview.height == 416>",
"<MASLayoutConstraint:ConstantConstraintUILabel:messageLabel.height >= 5000>",
"<MASLayoutConstraint:BottomConstraintUILabel:messageLabel.bottom == MASExampleDebuggingView:superview.bottom -10>",
"<MASLayoutConstraint:ConflictingConstraint[0]UILabel:messageLabel.top == MASExampleDebuggingView:superview.top + 1>"
)
Willattempt to recover by breaking constraint
<MASLayoutConstraint:ConstantConstraint UILabel:messageLabel.height>= 5000>
有关如何这样设置,请看Masonry工作空间的MasonryiOS Examples项目。
安装
使用orsomeCocoaPods.
在你的项目
pod ‘Masonry’
如果你想使用没有“mas_”前缀的Masonry。在你导入Masonry之前在你的prefix.ch文件中添加#define MAS_SHOPTHAND.
#define MAS_SHORTHAND
获得忙碌的Masonry
#import "Masonry.h"
代码片段
拷贝包含的代码片段到~/Library/Developer/Xcode/UserData/CodeSnippets来以闪电般的速度写你的masonryblocks。
mas_make -> [<view>mas_makeConstraints:^(MASConstraintMaker *make){<code>}];
mas_update -> [<view>mas_updateConstraints:^(MASConstraintMaker *make){<code>}];
mas_remake -> [<view>mas_remakeConstraints:^(MASConstraintMaker *make){<code>}];
特征
不限于AutoLayout的子集。NSLayoutConstraints可以做的,Masonry也可以。
很好的debug支持,给予你的视图和约束更有意义的名字。
约束读起来像语句。
没有疯狂魔幻的宏。Masonry不会污染宏的全局命名空间。
不基于字符串或字典,因此你可以获得检查的编译时间。
去做
秀色可餐
Mac的示例项目
更多的测试和实例