系列:iOS开发-Masonry的使用
对于一个开发者来说,我们是肯定需要和布局打交道的,最早的我们可能会使用手写frame或者bounce等,在之后我们可能会方便的使用xib或者storyboard来拉约束等.
我们会发现使用约束,autolayout是很方便的方法,但是仅限于在xib或者storyboard中拉出来的约束,如果我们是手写的话,你会发现实在过于的繁琐和啰嗦.
于是就出现了Masonry,这个高效的三方框架
Masonry是一个轻量级的布局框架 拥有自己的描述语法 采用更优雅的链式语法封装自动布局 简洁明了 并具有高可读性 而且同时支持 iOS 和 Max OS X。
至于学习的话,最有效的方式就是下载官方的demo来学习.这里我也就不写自己的demo来班门弄斧了.
Masonry 源码:https://github.com/Masonry/Masonry
下载下来
双击打开工程
选择iOS Examples,至于设备的话,选择自己习惯的就好
运行之后我们能看到一个完整的列表
我们一个一个学习,首先是Basic的内容,我们观察界面,同时进入代码中阅读代码
我们可以看到上面两个view,一个绿色一个红色,下面有一个蓝色的view,很简单的布局,我们如果使用xib等拉的话很好拉,弄几个带有比例的约束就好了,
我们看看Masonry的实现,首先它没有xib,另外它使用的不是frame.手写的代码,我们知道他就是手写的约束.我们看看内容
int padding = 10;
//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);
}];
阅读这一段,首先有一段注释,如果想要使用不带有mas_top的而是简写成top的,在pch文件中添加一段宏定义,这里我们看看就好了
下面是代码,
约束代码我们可以看到是封装在一个block中,使用的是makeConstraints的方法.
在Masonry中有三种创建约束的方法
makeConstraints 这个方法只会添加新的约束
mas_updateConstraints 这个方法将会覆盖以前的某些特定的约束
mas_remakeConstraints 这个方法会将以前的约束全部删除,添加新的约束
/*
mas_makeConstraints 只负责新增约束 Autolayout不能同时存在两条针对于同一对象的约束 否则会报错
mas_updateConstraints 针对上面的情况 会更新在block中出现的约束 不会导致出现两个相同约束的情况
mas_remakeConstraints 则会清除之前的所有约束 仅保留最新的约束
三种函数善加利用 就可以应对各种情况了
*/
内容很简单,跟我们xib拉约束的方式是基本完全一样的
读起来也很简单,make.left.equalTo(superview.left).offset(padding);
让左边等于父视图的左边偏移10个像素单位
make.bottom.equalTo(blueView.top).offset(-padding);
让底部等于蓝色视图的顶部偏移-10个像素
我们点击top或者其他跳转到Masonry中看看支持哪些属性
@property (nonatomic, strong, readonly) MASConstraint *left;
@property (nonatomic, strong, readonly) MASConstraint *top;
@property (nonatomic, strong, readonly) MASConstraint *right;
@property (nonatomic, strong, readonly) MASConstraint *bottom;
@property (nonatomic, strong, readonly) MASConstraint *leading;
@property (nonatomic, strong, readonly) MASConstraint *trailing;
@property (nonatomic, strong, readonly) MASConstraint *width;
@property (nonatomic, strong, readonly) MASConstraint *height;
@property (nonatomic, strong, readonly) MASConstraint *centerX;
@property (nonatomic, strong, readonly) MASConstraint *centerY;
@property (nonatomic, strong, readonly) MASConstraint *baseline
跟我们的约束一样,上下左右宽高,中心点等,
所以对于一般的约束,我们至此都已经可以完全自定义了
当然我们也可以注意一下这个
至于干什么也不用解释,就是可以同时添加多个约束
接下来在看列表的下一项MASExampleUpdateView
+ (BOOL)requiresConstraintBasedLayout
{
return YES;
}
// 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);
make.height.lessThanOrEqualTo(self);
}];
//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];
}];
}
首先是requiresConstraintBasedLayout
介绍是这样的
constraint-based layout engages lazily when someone tries to use it (e.g., adds a constraint to a view). If you do all of your constraint set up in -updateConstraints, you might never even receive updateConstraints if no one makes a constraint. To fix this chicken and egg problem, override this method to return YES if your view needs the window to use constraint-based layout.
意思就是基于约束的布局是懒触发的,只有在添加了约束的情况下,系统才会自动调用 -updateConstraints 方法,如果把所有的约束放在 updateConstraints中,那么系统将会不知道你的布局方式是基于约束的,所以 重写+requiresConstraintBasedLayout 返回YES就是明确告诉系统:虽然我之前没有添加约束,但我确实是基于约束的布局!这样可以保证系统一定会调用 -updateConstraints 方法 从而正确添加约束.
我们可以看到这一次Masonry的约束是写在updateConstraints中的.
这只是一个小的知识点.
接下来是更新的方式,只要的代码是这几个
// 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];
}];
使用上面的方式来更新约束,这样系统就会再次调用updateConstraints来实现约束的更新,从而实现动画,我们可以点击按钮来观看效果,按钮点击一次变大一次,宽高是之前的1.3倍
[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);
make.height.lessThanOrEqualTo(self);
}];
另外这里涉及到优先级的部分,首先是中心点位置跟父视图的中心点一致,其次是宽高优先的是小于<=父视图的宽高,在满足的情况下在是等于指定宽高
在这个demo中我们可以在学习一下优先级的调用和更新约束的逻辑
接下来是MASExampleRemakeView
在这里我就不介绍了
使用的更新约束的方法为
[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);
}
}];
大意是移除之前的约束,添加新的约束,也是在updateConstraints方法中实现
再继续 MASExampleSidesView
我们看到好多的view,眼睛都花掉了,这个肯定要写很久吧
好吧,我错了实现的方法就几行而已
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;
}
一个循环创建,在这里我们可以看到一个新的关键字edges
是的,这是一个新的关键字,表示了整个边缘,不需要重复写上下左右
我们进入类中可以看到
- (MASConstraint *)edges {
return [self addConstraintWithAttributes:MASAttributeTop | MASAttributeLeft | MASAttributeRight | MASAttributeBottom];
}
- (MASConstraint *)size {
return [self addConstraintWithAttributes:MASAttributeWidth | MASAttributeHeight];
}
- (MASConstraint *)center {
return [self addConstraintWithAttributes:MASAttributeCenterX | MASAttributeCenterY];
}
他帮助你实现了边缘 大小 中心 三种方法,所以之前的base的方式我们也可以尝试着简写看看..
然后是insets
我们看到的是类似collectionView的组的间距,距离上下左右的像素差距,我们很容易理解
- (MASConstraint * (^)(MASEdgeInsets))insets {
return ^id(MASEdgeInsets insets){
self.insets = insets;
return self;
};
}
- (MASConstraint * (^)(CGFloat))inset {
return ^id(CGFloat inset){
self.inset = inset;
return self;
};
}
- (MASConstraint * (^)(CGSize))sizeOffset {
return ^id(CGSize offset) {
self.sizeOffset = offset;
return self;
};
}
- (MASConstraint * (^)(CGPoint))centerOffset {
return ^id(CGPoint offset) {
self.centerOffset = offset;
return self;
};
}
- (MASConstraint * (^)(CGFloat))offset {
return ^id(CGFloat offset){
self.offset = offset;
return self;
};
}
- (MASConstraint * (^)(NSValue *value))valueOffset {
return ^id(NSValue *offset) {
NSAssert([offset isKindOfClass:NSValue.class], @"expected an NSValue offset, got: %@", offset);
[self setLayoutConstantWithValue:offset];
return self;
};
}
除此以外,其实现了insets\inset\sizeOffset\centerOffset\offset.等方法我们可以注意到这些都是Modifies the NSLayoutConstraint constant,意思是修改约束,我们可以看到都是添加一些在固定了大小之后的微调的方式...我们合理运用的时候会很方便
接下来就稍微有点复杂了,我们看看MASExampleAspectFitView
demo首先创建了4个视图
// Create views
self.topView = [[UIView alloc] initWithFrame:CGRectZero];
self.topInnerView = [[UIView alloc] initWithFrame:CGRectZero];
self.bottomView = [[UIView alloc] initWithFrame:CGRectZero];
self.bottomInnerView = [[UIView alloc] initWithFrame:CGRectZero];
// Set background colors
UIColor *blueColor = [UIColor colorWithRed:0.663 green:0.796 blue:0.996 alpha:1];
[self.topView setBackgroundColor:blueColor];
UIColor *lightGreenColor = [UIColor colorWithRed:0.784 green:0.992 blue:0.851 alpha:1];
[self.topInnerView setBackgroundColor:lightGreenColor];
UIColor *pinkColor = [UIColor colorWithRed:0.992 green:0.804 blue:0.941 alpha:1];
[self.bottomView setBackgroundColor:pinkColor];
UIColor *darkGreenColor = [UIColor colorWithRed:0.443 green:0.780 blue:0.337 alpha:1];
[self.bottomInnerView setBackgroundColor:darkGreenColor];
这里不是我们需要关注的,我们看约束
先看上面两个
// Layout top and bottom views to each take up half of the window
[self addSubview:self.topView];
[self.topView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.and.top.equalTo(self);
}];
[self addSubview:self.bottomView];
[self.bottomView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.and.bottom.equalTo(self);
make.top.equalTo(self.topView.mas_bottom);
make.height.equalTo(self.topView);
}];
这里我们看到了链式编程思想的实现将多个操作通过.连接在一起成为一句代码make.left.right.and.top.equalTo(self)
;
让,左右和上都与父视图一致,至于链式编程思想,后续会介绍,这里就先不说了,大意是其不需要考虑调用顺序,只需要知道考虑结果,类似于蝴蝶效应,产生一个事件,会影响很多东西,这些事件像流一样的传播出去,然后影响结果,借用面向对象的一句话,万物皆是流。
上面的约束是固定了左右上 下面的约束是固定了左右下 ,然后让下面的顶部跟上面的底部一样 且两个高度一样,这样我们就能看到了这样的效果
继续看下一个约束
[self.topView addSubview:self.topInnerView];
[self.topInnerView mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.equalTo(self.topInnerView.mas_height).multipliedBy(3);
make.width.and.height.lessThanOrEqualTo(self.topView);
make.width.and.height.equalTo(self.topView).with.priorityLow();
make.center.equalTo(self.topView);
}];
宽度是高度的3倍,这里我们又看到了新的约束方式,比例multipliedBy(3)
我们还看到了and
当然,其没有任何作用,仅仅是为了读写顺畅
设置了宽度和高度的大小为小于或者等于父视图的宽高,中心点的位置,确定了其是一个在父视图中心的一个和父视图等宽的高度是宽度1/3的一个位置和大小的视图
至于后面的另外的一个约束就不做说明了
补充一句
multipler属性表示约束值为约束对象的乘因数, dividedBy属性表示约束值为约束对象的除因数
例如我们可以写成make.height.equalTo(self.topInnerView.mas_width).dividedBy(3);
也是一样的效果
接下来就是基于动画的约束了MASExampleAnimatedView
首先是跟Base一样的三个view的约束,只是换一个方式
UIView *superview = self;
int padding = self.padding = 10;
UIEdgeInsets paddingInsets = UIEdgeInsetsMake(self.padding, self.padding, self.padding, self.padding);
self.animatableConstraints = NSMutableArray.new;
[greenView mas_makeConstraints:^(MASConstraintMaker *make) {
[self.animatableConstraints addObjectsFromArray:@[
make.edges.equalTo(superview).insets(paddingInsets).priorityLow(),
make.bottom.equalTo(blueView.mas_top).offset(-padding),
]];
make.size.equalTo(redView);
make.height.equalTo(blueView.mas_height);
}];
[redView mas_makeConstraints:^(MASConstraintMaker *make) {
[self.animatableConstraints addObjectsFromArray:@[
make.edges.equalTo(superview).insets(paddingInsets).priorityLow(),
make.left.equalTo(greenView.mas_right).offset(padding),
make.bottom.equalTo(blueView.mas_top).offset(-padding),
]];
make.size.equalTo(greenView);
make.height.equalTo(blueView.mas_height);
}];
[blueView mas_makeConstraints:^(MASConstraintMaker *make) {
[self.animatableConstraints addObjectsFromArray:@[
make.edges.equalTo(superview).insets(paddingInsets).priorityLow(),
]];
make.height.equalTo(greenView.mas_height);
make.height.equalTo(redView.mas_height);
}];
优先级底的我们最后看,首先是确定了各个view的位置关系,绿色下边是蓝色,红色左边是绿色,下边是蓝色,且三个等高,然后再分别看优先级低的约束进行补充,都是距离父视图相同的间距,这样我们就能够想象出它的界面约束的关系形成的效果
很显然,这种布局方式比base又高级了一点.值得学习
然后我们看到,其会将某些约束关系添加到数组中保存起来,
然后
int padding = invertedInsets ? 100 : self.padding;
UIEdgeInsets paddingInsets = UIEdgeInsetsMake(padding, padding, padding, padding);
for (MASConstraint *constraint in self.animatableConstraints) {
constraint.insets = paddingInsets;
}
[UIView animateWithDuration:1 animations:^{
[self layoutIfNeeded];
} completion:^(BOOL finished) {
//repeat!
[self animateWithInvertedInsets:!invertedInsets];
}];
通过取里面的某一个constraint,来修改部分属性达到更新约束形成动画的效果
这里设置的是insetsd的属性,我们也可以试试其他的属性
后面的MASExampleDebuggingView我就不再详细说明了,是辅助查看约束的值的,方便检查约束是否正确
我们可以在consle中看到自己自定的key和约束的值
接下来的是MASExampleLabelView
约束什么的就不说了
关注的是
- (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的时候你需要设置最大宽度,你需要在[super layoutSubviews]之后设置一个值,这样其宽度就能够设置成功,再之后所有的约束就都是正确的了,原因是在makeConstraints中我们看到只是设置了左上,确定了位置,但是没有固定大小,所以需要设置宽度之后才能确定label的高度,才能继续其他的依赖他的约束
我们再接着看MASExampleScrollView
我们知道,scrollerView是一个可以拖动的view,其是有一个contentSize属性的,如果没有设置好的话,其可能会导致无法下拉到底部等问题,xib我们就会遇到这样的问题,有时候很难解决,我们看看Masonry是如何实现的
[contentView makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.scrollView);
make.width.equalTo(self.scrollView);
}];
[contentView makeConstraints:^(MASConstraintMaker *make) {
make.bottom.equalTo(lastView.bottom);
}];
上面先给scrollerView添加一个contentView,然后设置其位置和大小即可.是不是很方便,不需要再设置contentSize了,这里用到了bottom
这里需要补充自动布局的情况下去定义contentSize是无效的。而mas_leading,mas_trailing是根据contentSize来确定的具体位置,而不是根据scrollview的frame来确定,因此布局得到的并不是想要得到的结果。(相互依赖)
如果设置了scrollView的contentSize,但是实际上在自动布局的情况下,contentSize的大小并不是原先设置的那样,而是由内容约束来定义的(leading,trailing),后执行的Masonry布局代码重定义了contentSize。
contentSize的height同理,由top,bottom来决定
为什么上面的可以滑动了呢?因为我们设置了contentView的上下左右,所以scrollerView自动布局后的contentSize就是contentView的大小
接下来是MASExampleArrayView,
这里的思想是把几个控件放在一起控制约束
- (void)setOffset:(CGFloat)offset {
_offset = offset;
[self setNeedsUpdateConstraints];
}
- (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];
}
这里的buttonViews是一个数组,数组里面是多个控件.至于约束的内容还是之前学习的那些,不做描述
来到MASExampleAttributeChainingView
还是熟悉的界面,三个视图
[greenView mas_makeConstraints:^(MASConstraintMaker *make) {
// chain attributes
make.top.and.left.equalTo(superview).insets(padding);
// which is the equivalent of
// make.top.greaterThanOrEqualTo(superview).insets(padding);
// make.left.greaterThanOrEqualTo(superview).insets(padding);
make.bottom.equalTo(blueView.mas_top).insets(padding);
make.right.equalTo(redView.mas_left).insets(padding);
make.width.equalTo(redView.mas_width);
make.height.equalTo(@[redView, blueView]);
}];
[redView mas_makeConstraints:^(MASConstraintMaker *make) {
// chain attributes
make.top.and.right.equalTo(superview).insets(padding);
make.left.equalTo(greenView.mas_right).insets(padding);
make.bottom.equalTo(blueView.mas_top).insets(padding);
make.width.equalTo(greenView.mas_width);
make.height.equalTo(@[greenView, blueView]);
}];
[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]);
}];
至于约束还是之前的约束,要讲的是什么呢? 其实在之前已经说过了,上面采用了莲式思想来进行约束,采用.
将多个属性进行连接成一句话
make.left.right.and.bottom.equalTo(superview).insets(padding);
很简单的意思,让左右下都等于某一个约束值,这里顺序没有要求,and
可以理解成连接,没有任何作用,仅作为读写顺畅.
接下来MASExampleMarginView就不多说了,我们使用过xib就会知道,在iOS8.0之后使用约束的时候会有一个Margin属性
至于意思就是会做一个保留值,而不是从0开始为从8开始的一个缩进,这个大家使用xib实验一下就知道了
至于MASExampleDistributeView,我们可以看到界面
帮我们实现了等距的效果,类似iOS9中的UIStackView.
总共有4种实现,我们分别测试看看
[arr mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedSpacing:20 leadSpacing:5 tailSpacing:5];
[arr makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(@60);
make.height.equalTo(@60);
}];
指定顶部和高度同时指定左右间距和中间修正间距所以中间的间距是5+20+5=30个像素....
[arr mas_distributeViewsAlongAxis:MASAxisTypeVertical withFixedSpacing:20 leadSpacing:5 tailSpacing:5];
[arr makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(@0);
make.width.equalTo(@60);
}];
这是纵向的等距
说明一下
/**
- 确定间距等间距布局
- @param axisType 布局方向
- @param fixedSpacing 两个item之间的间距(最左面的item和左边, 最右边item和右边都不是这个)
- @param leadSpacing 第一个item到父视图边距
- @param tailSpacing 最后一个item到父视图边距
*/
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedSpacing:(CGFloat)fixedSpacing leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing;
[arr mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedItemLength:30 leadSpacing:200 tailSpacing:30];
[arr makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(@60);
make.height.equalTo(@60);
}];
这里有点变化,使用的是另一个方法
/**
* distribute with fixed item size
*
* @param axisType 布局方向
* @param fixedItemLength 每个item的布局方向的长度
* @param leadSpacing 第一个item到父视图边距
* @param tailSpacing 最后一个item到父视图边距
*/
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedItemLength:(CGFloat)fixedItemLength leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing;
再看看最后一个
[arr mas_distributeViewsAlongAxis:MASAxisTypeVertical withFixedItemLength:30 leadSpacing:30 tailSpacing:200];
[arr makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(@0);
make.width.equalTo(@60);
}];
我们可以看到两者的不同,前面一个fixedItemLength代表的是宽度,后者则是高度,前面一个leadSpacing\tailSpacing代表的是x后面的代表的则是y,
这里我们只要稍稍关注一下就可以明白
有了这样的API,我们以后再写等距的代码就真的很简单了.而不需要天天拉约束,拉等比,拉长度......
另外我们还可以看到,总共是两种方法,有什么区别呢?
对比下来我们可以看到,第一种方法重视的是等距,而不关心没有个控件本身的宽高,只要求等距,第二个方法则相反,重视的是等大小,只控制控件本身大小,至于间距则是等分...
如此一来,我们真的很方便,无论是要求固定大小还是要求固定间距我们都能很好的实现.
最后关心一下MASExampleLayoutGuideViewController
[topView makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.mas_topLayoutGuide);
make.left.equalTo(self.view);
make.right.equalTo(self.view);
make.height.equalTo(@40);
}];
[topSubview makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.mas_topLayoutGuide);
make.centerX.equalTo(@0);
make.width.equalTo(@20);
make.height.equalTo(@20);
}];
[bottomView makeConstraints:^(MASConstraintMaker *make) {
make.bottom.equalTo(self.mas_bottomLayoutGuide);
make.left.equalTo(self.view);
make.right.equalTo(self.view);
make.height.equalTo(@40);
}];
我们可以看看效果
如果把make.top.equalTo(self.mas_topLayoutGuide);
改成我们平时想的make.top.equalTo(self.view);
会怎么样呢?
OK 大致明白了,其实就是修正iOS中vc的view的frame带来的位置偏移的因素,这里我就不说多少了.
至此,一个自动布局框架Masonry的使用就学习完毕了,我们可以很轻松的使用期布局我们的界面,你会发现你再也不用纠结是使用xib还是storyboard还是使用手写布局更方便了,你的代码效率会大大提高的.
因为其确实值得推荐...