GitHub的地址:https://github.com/SnapKit/Masonry,里面有官方Demo
本来想把自己写的demo放上来,无奈公司电脑是加密的,无法上传如何文件.0.0
1.去掉mas_前缀方法
equalTo:仅支持基本类型
mas_equalTo:支持类型转换,支持复杂类型。是对equalTo的封装。支持CGSize CGPoint NSNumber UIEdgeinsets
以下实现的是相同的效果
make.width.equalTo(@100);
make.width.mas_equalTo(100);
mas_equalTo是一个Macro,比较值,equalTo比较View。
以下实现的是相同的效果
make.bottom.mas_equalTo(ws.view.mas_bottom);
make.bottom.equalTo(ws.view);
具体方法,就是在pch中引用masonry前添加两个宏:
// 添加这个宏,就不用带mas_前缀
#define MAS_SHORTHAND
// 添加这个宏,equalTo就等价于mas_equalTo
#define MAS_SHORTHAND_GLOBALS
// 这个头文件一定要放在上面两个宏的后面
#import "Masonry.h"
2. mas_makeConstraints,mas_updateConstraints和remakeConstraints
添加约束前,必须要先把先addSubView,添加到父view上;
mas_makeConstraints:添加约束
添加的约束是会累加的,我们每添加一条(例:make.top.equalTo(greenView.mas_bottom).offset(padding);)
就会使得记录条数的数组增加一个元素( NSArray *installedConstraints = [MASViewConstraint installedConstraintsForView:self.view];
);重复累加而不会清除之前的约束,后果可想而知;
mas_updateConstraints:更新约束
- 只会更新mas_makeConstraints创建的约束;
- 是对比该对象之前的约束数组,添加过的约束就直接修改它的值,但是参照物不可改变;没有添加过的约束就添加上;
- 如
make.bottom.equalTo(view2.mas_bottom).offset(-2);
参照的是view2,后面更新bottom的约束,也只能是参照着view2进行更改,否则无效;
mas_remakeConstraints: 清除原来的约束 重新设置约束
3.scrollView下的Masonry约束
scrollView下的Masonry约束是不一样的,因为scrollView的可滑动性,导致有所区别;
在利用自动布局来布局UIScrollView时,一般都会在上面添加一个UIView的子控件;
具体看如下Demo:
例1:
@interface Scroll_ViewController ()
@property (strong, nonatomic) UIScrollView* scrollView;
@property (nonatomic ,strong) UIView *contentView;
@end
@implementation Scroll_ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
UIScrollView *scrollView = UIScrollView.new;
self.scrollView = scrollView;
scrollView.backgroundColor = [UIColor grayColor];
[self.view addSubview:scrollView];
UIView* contentView = UIView.new;
contentView.backgroundColor = [UIColor brownColor];
self.contentView = contentView;
[self.scrollView addSubview:contentView];
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);// 确定contentView的高度
}];
}
-(void)updateViewConstraints {
[self.scrollView makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view);
}];
[self.contentView makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.scrollView);//上下左右为0
make.width.equalTo(self.scrollView);// 确定contentView的宽度
}];
[super updateViewConstraints];// 必须要调用,且建议写在最后
}
- (UIColor *)randomColor {
CGFloat hue = ( arc4random() % 256 / 256.0 ); // 0.0 to 1.0
CGFloat saturation = ( arc4random() % 128 / 256.0 ) + 0.5; // 0.5 to 1.0, away from white
CGFloat brightness = ( arc4random() % 128 / 256.0 ) + 0.5; // 0.5 to 1.0, away from black
return [UIColor colorWithHue:hue saturation:saturation brightness:brightness alpha:1];
}
- (void)singleTap:(UITapGestureRecognizer*)sender {
[self dismissViewControllerAnimated:YES completion:nil];
};
例2:模拟网络请求下的UI布局改变
@interface ScrollTwo_ViewController ()
@property (strong, nonatomic) UIScrollView* scrollView;
@property (nonatomic ,strong) UIView *containerView;
@property (nonatomic ,strong) UILabel *oneLabel;
@property (nonatomic ,strong) UILabel *twoLabel;
@property (nonatomic ,strong) UILabel *threeLabel;
@property (nonatomic ,strong) UILabel *fourLabel;
@property (nonatomic, assign) CGFloat oneHeight;
@property (nonatomic, assign) CGFloat twoHeight;
@property (nonatomic, assign) CGFloat threeHeight;
@property (nonatomic, assign) CGFloat fourHeight;
@end
@implementation ScrollTwo_ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.oneHeight = 0;
self.twoHeight = 0;
self.threeHeight = 0;
self.fourHeight = 0;
[self.view addSubview:self.scrollView];
[self.scrollView addSubview:self.containerView];
[self.containerView addSubview:self.oneLabel];
[self.containerView addSubview:self.twoLabel];
[self.containerView addSubview:self.threeLabel];
[self.containerView addSubview:self.fourLabel];
[self.view setNeedsUpdateConstraints];
// 模拟3s后网络请求数据回来了,oneLabel根据数据获得了高度。
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.oneHeight = 80;
[self.view setNeedsUpdateConstraints];
});
// 模拟6s后网络请求数据回来了,fourLabel根据数据获得了高度。
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.fourHeight = 100;
[self.view setNeedsUpdateConstraints];
});
// 模拟9s后网络请求数据回来了,twoLabel根据数据获得了高度。
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(9 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.twoHeight = 150;
[self.view setNeedsUpdateConstraints];
});
// 模拟12s后网络请求数据回来了,threeLabel根据数据获得了高度。
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(12 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.threeHeight = 60;
[self.view setNeedsUpdateConstraints];
});
}
- (void)updateViewConstraints {
[self.scrollView mas_updateConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view);
}];
[self.containerView mas_updateConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.scrollView);
make.width.equalTo(self.scrollView);
}];
[self.oneLabel mas_updateConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.containerView).offset(50);
make.left.right.equalTo(self.containerView);
make.height.equalTo(@(self.oneHeight));
}];
[self.twoLabel mas_updateConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.oneLabel.mas_bottom);
make.left.right.equalTo(self.containerView);
make.height.equalTo(@(self.twoHeight));
}];
[self.threeLabel mas_updateConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.twoLabel.mas_bottom);
make.left.right.equalTo(self.containerView);
make.height.equalTo(@(self.threeHeight));
}];
[self.fourLabel mas_updateConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.threeLabel.mas_bottom);
make.left.right.equalTo(self.containerView);
make.height.equalTo(@(self.fourHeight));
make.bottom.equalTo(self.containerView);
}];
[super updateViewConstraints];
}
-(UIScrollView *)scrollView {
if (!_scrollView) {
_scrollView = [[UIScrollView alloc] init];
_scrollView.backgroundColor = [UIColor grayColor];
}
return _scrollView;
}
-(UIView *)containerView{
if (!_containerView) {
_containerView = [[UIView alloc] init];
_containerView.backgroundColor = [UIColor purpleColor];
}
return _containerView;
}
🔥部分懒加载被省略了🔥
4.更新布局时调用的四个方法
-
-(void)updateConstraintsIfNeeded
调用此方法,如果有标记为需要重新布局的约束,则立即进行重新布局,内部会调用updateConstraints方法 -
-(void)updateConstraints
重写此方法,内部实现自定义布局过程 -
-(BOOL)needsUpdateConstraints
当前是否需要重新布局,内部会判断当前有没有被标记的约束 -
-(void)setNeedsUpdateConstraints
标记需要进行重新布局 -
layoutIfNeeded
:告知页面布局立刻更新。所以一般都会和setNeedsLayout一起使用。如果希望立刻生成新的frame需要调用此方法,利用这点一般布局动画可以在更新布局后直接使用这个方法让动画生效。 -
-(void)updateViewConstraints
ViewController的View在更新视图布局时,会先调用ViewController的updateViewConstraints 方法。我们可以通过重写这个方法去更新当前View的内部布局,而不用再继承这个View去重写-updateConstraints方法。我们在重写这个方法时,务必要调用 super 或者 调用当前View的 -updateConstraints 方法。
5.对齐界面的上边距,或者对齐下边距:,可以看官网demo:MASExampleLayoutGuideViewController
如我们要对齐导航的下边距,tabbar的上边距等,或者是没有tabbar,直接对齐底部,可直接使用topLayoutGuide
和bottomLayoutGuide
属性实现,例:
🔥让view1顶部对齐导航下边距或者没有导航情况下的顶部对齐,可以加上如下约束:🔥
make.top.equalTo(self.view.mas_topLayoutGuide);
🔥让view2顶部对齐界面底部或者是tabbar的tap,可以加上如下约束:🔥
make.bottom.equalTo(self.view.mas_bottomLayoutGuide);
6. 给一个view设置了margin后,子视图可以直接对齐margin
view.layoutMargins = UIEdgeInsetsMake(5, 10, 15, 20);
make.top.equalTo(view.topMargin);
make.left.equalTo(view.leftMargin);
7.通过数组可以对一系列对象做相同的处理:
self.offset = 10.0f;
[self.buttonViewsArray updateConstraints:^(MASConstraintMaker *make) {
make.baseline.equalTo(self.view.mas_centerY).with.offset(self.offset);
}];
8. 约束的优先级
.priority允许你指定一个精确的优先级,数值越大优先级越高.最高1000.
.priorityHigh等价于 UILayoutPriorityDefaultHigh .优先级值为 750.
.priorityMedium介于高优先级和低优先级之间,优先级值在 250~750之间.
.priorityLow等价于 UILayoutPriorityDefaultLow , 优先级值为 250.
优先级可以在约束的尾部添加:
make.left.greaterThanOrEqualTo(label.mas_left).with.priorityLow();
make.top.equalTo(label.mas_top).with.priority(600);
9.其他:
1.设置大小: make.size.equalTo(CGSizeMake(300, 300));
- 设置中心点,同时把中心点下移了50: make.center.equalTo(self.view).centerOffset(CGPointMake(0, 50));
- 连写 left top 约束: make.left.top.equalTo(20);
- 设置 size 和top 与 view2 相同: make.size.top.equalTo(self.view2);
- left 和 center 一起使用是可以确定出view的长度:width
[self.textField mas_makeConstraints:^(MASConstraintMaker *make) {
//left,right,centerx,y 不能共存只能有其二
make.left.mas_equalTo(20);
make.centerX.equalTo(self.view); //left 和 center 一起使用是可以确定出view的长度: width
make.bottom.mas_equalTo(-100);
make.height.mas_equalTo(40);
}];
- mas_distributeViewsAlongAxis 可以等分视图,自动计算,具体看官方demo例子;
-(void)distributeViewsOne{
//控件等间距
[self.view1 addSubview:self.view4];
[self.view1 addSubview:self.view5];
[self.view1 addSubview:self.view6];
[self.view1 addSubview:self.view7];
NSArray *viewArray = @[self.view4,self.view5,self.view6,self.view7];
//数组里面的元素不能小于2个,要不会报错 views to distribute need to bigger than one
if (viewArray.count>1) {
/**
* 多个控件固定间隔的等间隔排列,变化的是控件的长度或者宽度值
*
* @param axisType 轴线方向
* @param fixedSpacing 间隔大小
* @param leadSpacing 头部间隔
* @param tailSpacing 尾部间隔
*/
[viewArray mas_distributeViewsAlongAxis:MASAxisTypeHorizontal
withFixedSpacing:10
leadSpacing:5
tailSpacing:5];
//以left=0为开始,width是通过间隔和父view计算得出,只需要给出height和上下的限制就可以了
[viewArray mas_makeConstraints:^(MASConstraintMaker *make) {
make.bottom.equalTo(self.view1.mas_bottom).offset(-10);
make.height.equalTo(50);
}];
}else{
NSLog(@"⚠️⚠️⚠️⚠️⚠️数组中的元素少于2个⚠️⚠️⚠️⚠️⚠️");
}
}
- 更新约束后增加动画,避免生硬的改变
// 修改为以前的约束(距下边距0)
[_textField mas_updateConstraints:^(MASConstraintMaker *make) {
make.bottom.mas_equalTo(-100);
}];
// 更新约束动画
[UIView animateWithDuration:keyboardDuration animations:^{
[self.view layoutIfNeeded];
}];
- 一个键盘弹起,处理界面变化的伪代码:
@property (nonatomic ,strong) UITextField *textField;
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:self.textField];
[self.textField mas_makeConstraints:^(MASConstraintMaker *make) {
//left,right,centerx,y 不能共存只能有其二
make.left.mas_equalTo(20);
make.centerX.equalTo(self.view); //left 和 center 一起使用是可以确定出view的长度: width
make.bottom.mas_equalTo(-100);
make.height.mas_equalTo(40);
}];
//处理键盘弹起:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrameNotification:) name:UIKeyboardWillChangeFrameNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHideNotification:) name:UIKeyboardWillHideNotification object:nil];
}
-(UITextField *)textField {
if (!_textField) {
_textField = [UITextField new];
_textField.layer.borderWidth = 1.0f;
_textField.layer.borderColor = [UIColor lightGrayColor].CGColor;
_textField.placeholder = @"请输入...";
// _textField.backgroundColor = [UIColor purpleColor];
}
return _textField;
}
#pragma mark ------ 键盘事件 ------
- (void)keyboardWillChangeFrameNotification:(NSNotification *)notification {
// 获取键盘基本信息(动画时长与键盘高度)
NSDictionary *userInfo = [notification userInfo];
//键盘的宽高
CGRect rect = [userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue];
CGFloat keyboardHeight = CGRectGetHeight(rect);
//键盘弹出时间
CGFloat keyboardDuration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
// 修改下边距约束
[_textField mas_updateConstraints:^(MASConstraintMaker *make) {
make.bottom.mas_equalTo(-keyboardHeight);
}];
// 给 更新约束,添加一个动画
[UIView animateWithDuration:keyboardDuration animations:^{
[self.view layoutIfNeeded];
}];
}
- (void)keyboardWillHideNotification:(NSNotification *)notification {
// 获得键盘动画时长
NSDictionary *userInfo = [notification userInfo];
CGFloat keyboardDuration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
// 修改为以前的约束(距下边距0)
[_textField mas_updateConstraints:^(MASConstraintMaker *make) {
make.bottom.mas_equalTo(-100);
}];
// 更新约束
[UIView animateWithDuration:keyboardDuration animations:^{
[self.view layoutIfNeeded];
}];
}
网友评论