测试已测试 | ✗ |
语言语言 | Obj-CObjective C |
许可 | MIT |
发布上一个版本 | 2017年1月 |
由 ‘cs19851124‘ 维护。
依赖 | |
RHNJKWebViewProgress | >= 0 |
Masonry | >= 0 |
TCCategories | >= 0 |
Masonry 仍在积极维护中,我们致力于修复错误并合并社区的好 PR。但是如果您在项目中使用 Swift,我们建议使用 SnapKit,因为它提供了更好的类型安全和更简单的 API。
Masonry 是一个轻量级的布局框架,它通过更简洁的语法包装了 AutoLayout。Masonry 有自己的布局 DSL,它提供了描述 NSLayoutConstraints 的可链式方法,从而生成的布局代码更简洁、易于阅读。Masonry 支持 iOS 和 Mac OS X。
例如,您可以查看 Masonry 工作区中的 Masonry iOS 示例 项目。下载后,您需要运行 pod install
。
在底层,Auto Layout 是一种强大而灵活的方式,用于组织和布局您的视图。然而,从代码中创建约束既冗长又难以描述。想象一个简单的例子,您想要一个视图填充其父视图,但每边都要向内缩进 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 constraints
[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 个时,代码也会很快变得难以阅读。另一个选择是使用视觉格式语言(VFL),它比这一选择更简洁一些。然而,ASCII 类型的语法有其自身的缺点,它也很难动画,因为 NSLayoutConstraint constraintsWithVisualFormat:
返回一个数组。
这里展示的是使用 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 addConstraints:...
。然而,Masonry 会自动将约束添加到适当的视图。
Masonry 还会帮您调用 view1.translatesAutoresizingMaskIntoConstraints = NO;
。
.equalTo
等同于 NSLayoutRelationEqual
.lessThanOrEqualTo
等同于 NSLayoutRelationLessThanOrEqual
.greaterThanOrEqualTo
等同于 NSLayoutRelationGreaterThanOrEqual
这三个等式约束接受一个参数,可以是以下任何一种
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 |
如果你想要 view.left 大于或等于 label.left
//these two constraints are exactly the same
make.left.greaterThanOrEqualTo(label);
make.left.greaterThanOrEqualTo(label.mas_left);
Auto Layout 允许宽度和高度设置为常量值。如果你想要设置 view 具有最小和最大宽度,你可以向等式块传递一个数字
//width >= 200 && width <= 400
make.width.greaterThanOrEqualTo(@200);
make.width.lessThanOrEqualTo(@400)
但是 Auto Layout 不允许对齐属性(如 left、right、centerY 等)设置为常量值。因此,如果你为这些属性传递一个 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_SHORTHAND_GLOBALS
可获得无前缀的版本。
是一个包含前述任一类型混合的数组
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
// 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))
// 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))
// 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);
有时你需要修改现有的约束以进行动画或删除/替换约束。在 Masonry 中,有几种不同的方法来更新约束。
您可以通过将约束创建表达式的结果分配给局部变量或类属性来保持对特定约束的引用。您还可以将多个约束存储在数组中以便引用。
// 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];
或者,如果您只是更新约束的常量值,则可以使用便利方法 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];
}
mas_updateConstraints
用于更新约束集合非常有用,但是超出更新常量值之外的操作可能会变得令人疲惫。这就是 mas_remakeConstraints
介入的地方。
mas_remakeConstraints
与 mas_updateConstraints
类似,但它不是更新常量值,而是在重新安装之前会移除其所有的约束。这样您就可以提供不同的约束,而无需保留对要删除的约束的引用。
- (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);
}
}];
}
您可以在 Masonry iOS 示例 项目中找到这三个方法更详细的使用示例。
布局视图并不总是按计划进行。所以当事情真的失控时,您不希望看到这样的控制台输出
Unable to simultaneously satisfy constraints.....blah blah blah....
(
"<NSLayoutConstraint:0x7189ac0 V:[UILabel:0x7186980(>=5000)]>",
"<NSAutoresizingMaskLayoutConstraint:0x839ea20 h=--& v=--& V:[MASExampleDebuggingView:0x7186560(416)]>",
"<NSLayoutConstraint:0x7189c70 UILabel:0x7186980.bottom == MASExampleDebuggingView:0x7186560.bottom - 10>",
"<NSLayoutConstraint:0x7189560 V:|-(1)-[UILabel:0x7186980] (Names: '|':MASExampleDebuggingView:0x7186560 )>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x7189ac0 V:[UILabel:0x7186980(>=5000)]>
Masonry 为 NSLayoutConstraint 添加了一个分类,该分类覆盖了默认的 - (NSString *)description
实现方案。现在您可以为视图和约束提供有意义的名称,并且可以轻松地识别出由 Masonry 创建的约束。
这意味着您的控制台输出现在可以像这样显示
Unable to simultaneously satisfy constraints......blah blah blah....
(
"<NSAutoresizingMaskLayoutConstraint:0x8887740 MASExampleDebuggingView:superview.height == 416>",
"<MASLayoutConstraint:ConstantConstraint UILabel:messageLabel.height >= 5000>",
"<MASLayoutConstraint:BottomConstraint UILabel:messageLabel.bottom == MASExampleDebuggingView:superview.bottom - 10>",
"<MASLayoutConstraint:ConflictingConstraint[0] UILabel:messageLabel.top == MASExampleDebuggingView:superview.top + 1>"
)
Will attempt to recover by breaking constraint
<MASLayoutConstraint:ConstantConstraint UILabel:messageLabel.height >= 5000>
有关如何设置此功能的示例,请参阅 Masonry 工作区中的 Masonry iOS 示例 项目。
@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
您在 Podfile 中
pod 'Masonry'
如果您想在没有那些讨厌的 'mas_' 前缀的情况下使用 Masonry,在导入 Masonry 之前在 prefix.pch 中添加 #define MAS_SHORTHAND
#define MAS_SHORTHAND
开始 Masonry 工作吧
#import "Masonry.h"
将包含的代码片段复制到 ~/Library/Developer/Xcode/UserData/CodeSnippets
以快速编写 Masonry 块!
mas_make
-> [<view> mas_makeConstraints:^(MASConstraintMaker *make){<code>}];
mas_update
-> [<view> mas_updateConstraints:^(MASConstraintMaker *make){<code>}];
mas_remake
-> [<view> mas_remakeConstraints:^(MASConstraintMaker *make){<code>}];