美文网首页ios专注iOS开发IOS开发资料库
代码适配Masonry使用的详细介绍

代码适配Masonry使用的详细介绍

作者: 不冷的南风 | 来源:发表于2016-03-17 01:01 被阅读17413次

Masonry简介

Masonry是一个轻量级的布局框架,它拥有自己的描述语法(采用更优雅的链式语法封装)来自动布局,具有很好可读性且同时支持iOS和Max OS X等。
总之,对于侧重写代码的coder,请你慢慢忘记Frame,喜欢Masonry

使用前的准备

若是你对于自动布局很熟练的话,再接触这个第三方Masonry很容易上手的,对UI界面显示的控件的约束本质都是相同的,现在呢,我一般都是喜欢在控制器里导入 #import "Masonry.h"之前再添加两个宏,来提高App的开发效率。

//1. 对于约束参数可以省去"mas_"
#define MAS_SHORTHAND
//2. 对于默认的约束参数自动装箱
#define MAS_SHORTHAND_GLOBALS

即:需要我们导入的框架与宏如下

//define this constant if you want to use Masonry without the 'mas_' prefix
#define MAS_SHORTHAND
//define this constant if you want to enable auto-boxing for default syntax
#define MAS_SHORTHAND_GLOBALS

#import "Masonry.h" //宏必须添加在头文件前面

添加约束前提:被约束的必须有父控件,其中约束项都必须是UIView或子类的实例

约束的属性

在此我就列举几个可能不太熟悉的吧

@property (nonatomic, strong, readonly) MASConstraint *leading;  //首部
@property (nonatomic, strong, readonly) MASConstraint *trailing; //尾部
@property (nonatomic, strong, readonly) MASConstraint *baseline; //文本基线

约束的三种方法

/**
 //这个方法只会添加新的约束
 [blueView mas_makeConstraints:^(MASConstraintMaker *make)  {
 
 }];
 
 // 这个方法会将以前的所有约束删掉,添加新的约束
 [blueView mas_remakeConstraints:^(MASConstraintMaker *make) {
 
 }];
 
 // 这个方法将会覆盖以前的某些特定的约束
 [blueView mas_updateConstraints:^(MASConstraintMaker *make) {
 
 }];
 */

常见约束的各种类型

/**
 1.尺寸:width、height、size
 2.边界:left、leading、right、trailing、top、bottom
 3.中心点:center、centerX、centerY
 4.边界:edges
 5.偏移量:offset、insets、sizeOffset、centerOffset
 6.priority()约束优先级(0~1000),multipler乘因数, dividedBy除因数
 */

Masonry约束易忽略的技术点

使用Masonry不需要设置控件的translatesAutoresizingMaskIntoConstraints属性为NO;
防止block中的循环引用,使用弱引用(这是错误观点),在这里block是局部的引用,block内部引用self不会造成循环引用的
__weak typeof (self) weakSelf = self;(没必要的写法)

Masonry约束控件出现冲突的问题

当约束冲突发生的时候,我们可以设置view的key来定位是哪个view
redView.mas_key = @"redView";
greenView.mas_key = @"greenView";
blueView.mas_key = @"blueView";
若是觉得这样一个个设置比较繁琐,怎么办呢,Masonry则提供了批量设置的宏MASAttachKeys
MASAttachKeys(redView,greenView,blueView); //一句代码即可全部设置

约束iconView距离各个边距为30

    [iconView makeConstraints:^(MASConstraintMaker *make) {
      make.edges.equalTo(self.view).insets(UIEdgeInsetsMake(30, 30, 30, 30));
    }];

equalTo 和 mas_equalTo的区别

#define mas_equalTo(...) equalTo(MASBoxValue((__VA_ARGS__)))
#define mas_greaterThanOrEqualTo(...) greaterThanOrEqualTo(MASBoxValue((__VA_ARGS__)))
#define mas_lessThanOrEqualTo(...) lessThanOrEqualTo(MASBoxValue((__VA_ARGS__)))
#define mas_offset(...) valueOffset(MASBoxValue((__VA_ARGS__)))

得出结论:mas_equalTo只是对其参数进行了一个BOX(装箱) 操作,目前支持的类型:数值类型(NSNumber)、 点(CGPoint)、大小(CGSize)、边距(UIEdgeInsets),而equalTo:这个方法不会对参数进行包装。

//常见约束的写法 这里太简单了 ,就不备注了
[iconView makeConstraints:^(MASConstraintMaker *make) {
        make.right.equalTo(self.view).offset(-30);
        make.top.equalTo(self.view).offset(30);
        make.height.width.equalTo(100); //== make.size.equalTo(100);

        //make.size.mas_equalTo(self.view).multipliedBy(0.5);
        //make.top.greaterThanOrEqualTo(self.view).offset(padding);
    }];
[redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(superview.mas_top).with.offset(10);  //with 增强可读性
        make.left.equalTo(greenView.mas_right).and.offset(10); //and 增强可读性
        make.bottom.equalTo(blueView.mas_top).offset(-10);
        make.right.equalTo(superview.mas_right).offset(-10);
        make.width.equalTo(greenView.mas_width);
        
        make.height.equalTo(@[greenView, blueView]); //约束参数相同可以通过数组
    }];

更新约束的问题

例如:控制器有个按钮,若是点击按钮,则按钮本身的大小、位置会随机改变

  • 监听按钮点击
[self.btn addTarget:self action:@selector(didClickBtn:) forControlEvents:UIControlEventTouchUpInside];
  • 处理事件
 (void) didClickBtn:(UIButton *)button {
    self.btnSize = CGSizeMake(self.btnSize.width * 1.3, self.btnSize.height * 1.3); //设置一个属性(btnSize)保存其大小的变化

    //1.告知需要更新约束,但不会立刻开始,系统然后调用updateConstraints
    [self setNeedsUpdateConstraints];

    //2.告知立刻更新约束,系统立即调用updateConstraints
    [self updateConstraintsIfNeeded];

    //3.这里动画当然可以取消,具体看项目的需求
    //系统block内引用不会导致循环引用,block结束就会释放引用对象
    [UIView animateWithDuration:0.4 animations:^{
        [self layoutIfNeeded]; //告知页面立刻刷新,系统立即调用updateConstraints
    }];
}
  • 苹果官方建议:添加/更新约束在这个方法(updateConstraints)
// this is Apple's recommended place for adding/updating constraints
- (void)updateConstraints {
   //更新约束
    [self.btn 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];
}
  • 设置requiresConstraintBasedLayout为YES
+ (BOOL)requiresConstraintBasedLayout{
    return YES ; //重写这个方法 若视图基于自动布局的
}

重置约束的问题

对于控件的重新约束,则之前的约束都是无效的,步骤都更新约束一样的,只是在updateConstraints方法内的约束方法改为了remakeConstraints,直接贴代码吧(仍以按钮为例,其他原理都是相同的)

//首先监听按钮
[self.btn addTarget:self action:@selector(didClickBtn:) forControlEvents:UIControlEventTouchUpInside];

//处理事件
- (void) didClickBtn :(UIButton *)button{
   (...) //触发条件
    [self setNeedsUpdateConstraints]; 

    [self updateConstraintsIfNeeded];
    
   /**
     *   动画展示变化 - 这句代码可有可无,参考项目具体的需求
     *   [UIView animateWithDuration:0.4 animations:^{
     *         [self layoutIfNeeded];
     *   }];
     */
}

//重置约束
- (void)updateConstraints {
    [self.btn remakeConstraints:^(MASConstraintMaker *make) {
      .....
    }];
    [super updateConstraints]; 
}

+ (BOOL)requiresConstraintBasedLayout{
    return YES ; //重写这个方法 若视图基于自动布局的
}

多个(2个以上)控件的等间隔排序显示

首先介绍2个函数
/**
    *  axisType         轴线方向
    *  fixedSpacing     间隔大小
    *  fixedItemLength  每个控件的固定长度/宽度
    *  leadSpacing      头部间隔
    *  tailSpacing      尾部间隔
    *
    */
//1. 等间隔排列 - 多个控件间隔固定,控件长度/宽度变化
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType
withFixedSpacing:(CGFloat)fixedSpacing leadSpacing:(CGFloat)leadSpacing
tailSpacing:(CGFloat)tailSpacing;

//2. 等间隔排列 - 多个固定大小固定,间隔空隙变化
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType
withFixedItemLength:(CGFloat)fixedItemLength
leadSpacing:(CGFloat)leadSpacing
tailSpacing:(CGFloat)tailSpacing;
//首先添加5个视图
 NSMutableArray *array = [NSMutableArray new];
    for (int i = 0; i < 5; i ++) {
        UIView *view = [UIView new];
        view.backgroundColor = [UIColor greenColor];
        [self addSubview:view];
        [array addObject:view]; //保存添加的控件
    }

//水平方向控件间隔固定等间隔
[array mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedSpacing:15 leadSpacing:10 tailSpacing:10];
            [array makeConstraints:^(MASConstraintMaker *make) {
                make.top.equalTo(50);
                make.height.equalTo(70);
            }];

//水平方向宽度固定等间隔
[array mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedItemLength:70 leadSpacing:10 tailSpacing:10];
            [array makeConstraints:^(MASConstraintMaker *make) { //数组额你不必须都是view 
                make.top.equalTo(50);
                make.height.equalTo(70);
            }];
水平方向等间隔.png 水平方向控件宽度固定等间隔.png

多行label的约束问题

对于UILabel文字内容多的问题,个人觉得Masonry约束设置的非常简单优雅,在此非常感谢Masonry的作者@Robert Payne

    //创建label
    self.label = [UILabel new];
    self.label.numberOfLines = 0;
    self.label.lineBreakMode = NSLineBreakByTruncatingTail;
    self.label.text = @"有的人,没事时喜欢在朋友圈里到处点赞,东评论一句西评论一句,比谁都有存在感。等你有事找他了,他就立刻变得很忙,让你再也找不着。真正的朋友,平常很少联系。可一旦你遇上了难处,他会立刻回复你的消息,第一时间站出来帮你。所谓的存在感,不是你有没有出现,而是你的出现有没有价值。存在感,不是刷出来的,也不是说出来的。有存在感,未必是要个性锋芒毕露、甚至锋利扎人。翩翩君子,温润如玉,真正有存在感的人,反而不会刻意去强调他的存在感。他的出现,永远都恰到好处。我所欣赏的存在感,不是长袖善舞巧言令色,而是对他人的真心关照;不是锋芒毕露计较胜负,而是让人相处得舒服;不是时时刻刻聒噪不休,而是关键时刻能挺身而出。别总急着出风头,希望你能有恰到好处的存在感。";
    [self addSubview: self.label];

    [self.label makeConstraints:^(MASConstraintMaker *make) {
        make.left.top.equalTo(10);
        make.right.equalTo(-10);
    }];

//添加约束
- (void)layoutSubviews {
    //1. 执行 [super layoutSubviews];
    [super layoutSubviews];

    //2. 设置preferredMaxLayoutWidth: 多行label约束的完美解决
   self.label.preferredMaxLayoutWidth = [UIScreen mainScreen].bounds.size.width - 20;

    //3. 设置preferredLayoutWidth后,需要再次执行 [super layoutSubviews]; 
    //其实在实际中这步不写,也不会出错,官方解释是说设置preferredLayoutWidth后需要重新计算并布局界面,所以这步最好执行
    [super layoutSubviews];
}
多行label约束.png

UIScrollView的问题

原理同自动布局一样 UIScrollView上添加UIView
UIView上添加需要显示的控件 UIScrollView滚动高度取决于显示控件的总高度
对子控件做好约束,可达到控制UIView的大小

    //创建滚动视图
    UIScrollView *scrollView = [UIScrollView new];
    self.scrollView = scrollView;
    
    scrollView.backgroundColor = [UIColor grayColor];
    [self addSubview:scrollView];
    
    [self.scrollView makeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(self);
    }];
    
    [self setUpContentView]; //添加内容视图

- (void)setUpContentView {
    //约束UIScrollView上contentView
    UIView *contentView = [UIView new];
    [self.scrollView addSubview:contentView];

    [contentView makeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(self.scrollView);
        make.width.equalTo(self.scrollView); //此处必填 - 关键点
    }];
    
    //添加控件到contentView,约束原理与自动布局相同
    UIView *lastView;
    CGFloat height = 30;
    for (int i = 0; i <1 5; i ++) {
        UIView *view = UIView.new;
        view.backgroundColor = [UIColor colorWithRed:arc4random() % 255 / 256.0  green:arc4random() % 255 / 256.0 blue:arc4random() % 255 / 256.0 alpha:1.0];
        [contentView addSubview:view];

        [view makeConstraints:^(MASConstraintMaker *make) {
            make.top.equalTo(lastView ? lastView.bottom : @0);
            make.left.equalTo(0);
            make.width.equalTo(contentView.width);
            make.height.equalTo(height);
        }];
        
        height += 30;
        lastView = view;
    }
    
    [contentView makeConstraints:^(MASConstraintMaker *make) {
        make.bottom.equalTo(lastView.bottom);
    }];
}
scrollView的约束设置.png

Masonry源码GitHub地址下载:Masonry
终于写完了,有什么理解不对的直接说...今晚还有巴萨、尤文的欧冠,真心伤不起。。。

相关文章

  • sky-约束相关

    https://www.jianshu.com/p/e3162f3c61fa 代码适配Masonry使用的详细介绍...

  • 代码适配Masonry使用的详细介绍

    Masonry简介 Masonry是一个轻量级的布局框架,它拥有自己的描述语法(采用更优雅的链式语法封装)来自动布...

  • 代码适配Masonry使用的详细介绍

    Masonry简介 Masonry是一个轻量级的布局框架,它拥有自己的描述语法(采用更优雅的链式语法封装)来自动布...

  • Autolayout、VFL、Masonry

    适配 VFL语言 Masonry 代码实现Autolayout VFL代码 Masonry使用 总结 使用代码实现...

  • 使用Masonry代码进行屏幕适配的详细介绍

    Masonry自动布局使用 Masonry是一个轻量级的布局框架,采用更好的语法封装自动布局,它有自己的布局DSL...

  • IOS适配

    分为两个部分:VFL代码适配和Masonry框架适配 1.VFL代码适配 一、VFL介绍 1、VFL全称是Visu...

  • Masonry学习理解

    iOS中进行屏幕适配,使用的是AutoLayout。在纯代码中可以使用第三方Masonry添加约束进行适配,而使用...

  • 版本适配

    屏幕适配 代码适配 Masonry UIView+AutoLayout 可视化适配 autoLayout 系统适配...

  • iOS开发文档

    1.**关于适配** 1.代码适配:引用第三方库“SDAutoLayout”以及“Masonry”进行代码适配. ...

  • UIScrollView 3页滑屏

    需要使用UIScrollView实现3页滑屏得纯代码实现 使用了Masonry适配 需要注意的是scrollVie...

网友评论

  • 舒马赫:Masory写的很棒,但是不喜欢纯代码写界面,太慢了。推荐使用xml的布局库FlexLib,采用前端布局标准flexbox(不使用autolayout),支持热刷新,自动计算高度等。可以到这里了解详细信息:

    https://github.com/zhenglibao/FlexLib
  • 童话镇里蜿蜒的河:mas_distributeViewsAlongAxis 一定是均分的? 我的控件大小不一。我要怎么去理由数组布局?
  • iDog:提问 mas_left和mas_leftMargin有啥区别?如果是right的话
  • 午马丶:你好为什么 我的让label内容适应他的大小还是不好使啊,求回复谢谢啊
  • 867e6808da22://常见约束的写法 这里太简单了 ,就不备注了
    [iconView makeConstraints:^(MASConstraintMaker *make) {
    make.right.equalTo(self.view).offset(-30);
    make.top.equalTo(self.view).offset(30);
    make.height.width.equalTo(100); //== make.size.equalTo(100);

    //make.size.mas_equalTo(self.view).multipliedBy(0.5);
    //make.top.greaterThanOrEqualTo(self.view).offset(padding);
    }];

    这一类的最好也做下说明。我刚刚接触masonry,看这个还是云里雾里的。
  • b0cf03724cef:作者你好,你这个scrollview 内部的view循环约束是垂直的,如果是横向的话,masony怎么约束,希望指教一二。
    4dbde41e70a3:make.width.equalTo(self.scrollView); //此处必填 - 关键点 此句宽度约束改成高度与scrollView等同
  • hhgvg:请问控件大小怎么适配呀
  • kkkore:在一个VC内,创建一个自定义的VIew,使用masonry约束,在该自定义的view中(下称为A),有其它的view,也是使用masonry约束。 这个A内部view的约束,是要在A,添加约束完成之后再进行的吗?还是可以在其初始化方法中进行?
    望回复。谢谢,楼主大大 :beers:
  • 若雨千寻:两个控件之间怎么加比例约束啊?比如一个控件宽是另一个的两倍
  • 31202988e8c1:您好,我按照您的方式设置的label,但是label依然无法自适应到两行,为什么会出现这种问题?
    iOS开发周立贺:label的行数设置为0 就自适应了
  • 策马鞭程:楼主,请问一下,有三个控件上到下排列,如何保持一个控件一直保持在两个控件中间同等距离?该怎么约束?
    策马鞭程:@策马鞭程 第一和第三个控件位置是固定的.第一个控件是UILabel,内容可能是1行也会是2行. 要保证第二个控件一直会处于两个控件中间.
  • 四月天__:楼主你好 请教下 如果需要更新约束的话 是不是view必须新建一个继承自UIView的子类,在里面才能使用 + (BOOL)requiresConstraintBasedLayout这个方法 呢 在视图控制器里面 怎么用,
  • SunStart:你好 我问下 你插入等分间隔 View 的时候 他们之间怎么插入一个 分隔View 类似于微博那种 评论 点赞 之类的
  • yue博客:masonry,的block没有进行copy,不会形成循环引用的
    不冷的南风:@月哥的技术博客 嗯,谢谢你的指正,已修改, :blush:
  • Farmers:写的不错,楼主辛苦了,给个赞
    不冷的南风:@code程序人生 一起进步咯。。。

本文标题:代码适配Masonry使用的详细介绍

本文链接:https://www.haomeiwen.com/subject/ybhqlttx.html