美文网首页iOS技术
iOS自动布局框架 - Masonry详解

iOS自动布局框架 - Masonry详解

作者: hazydream | 来源:发表于2020-11-22 08:55 被阅读0次

    1. 概念

    iOS通过纯代码进行UI开发的话,屏幕适配有时会比较麻烦,所以一般都会使用 自动化布局框架 进行屏幕适配工作,其中 Masonry 是一种非常流行的第三方布局框架。

    Masonry 是一个轻量级的布局框架,拥有自己的描述语法,采用更优雅的 链式语法 封装自动布局,简洁明了,并具有高可读性,而且同时支持 iOSMax OS X


    2. 基础知识

    (1) 设置约束方法

    // 添加约束
    - (NSArray *)mas_makeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;
    // 更新约束,更新修改的约束,其他约束不变
    - (NSArray *)mas_updateConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;
    // 移除约束,并重新添加约束 
    - (NSArray *)mas_remakeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;
    

    注意:

    • 使用 Masonry 添加约束之前,需要在 addSubview之后 才能生效。

    (2) 相关属性

    @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; // 文本基线
    
    @property (nonatomic, strong, readonly) MASConstraint *edges; // 内边距
    @property (nonatomic, strong, readonly) MASConstraint *size; // 尺寸
    @property (nonatomic, strong, readonly) MASConstraint *center; // 中点
    

    (3) 常用方法

    // 等于
    - (MASConstraint * (^)(id attr))equalTo;
    - (MASConstraint * (^)(id attr))mas_equalTo;
    // 大于等于
    - (MASConstraint * (^)(id attr))greaterThanOrEqualTo;
    - (MASConstraint * (^)(id attr))mas_greaterThanOrEqualTo;
    // 小于等于
    - (MASConstraint * (^)(id attr))lessThanOrEqualTo;
    - (MASConstraint * (^)(id attr))mas_lessThanOrEqualTo;
    // 偏移量
    - (MASConstraint * (^)(CGFloat offset))offset;
    - (MASConstraint * (^)(id offset))mas_offset;
    
    1> 方法区别
    • equalTo:仅支持基本类型;
    • mas_equalTo:支持类型转换,支持复杂类型,是对equalTo的封装;
      支持CGSizeCGPointNSNumberUlEdgeinsets

    例如:

    make.width.equalTo(@100); 等同于 
    make.width.mas_equalTo(100);
    
    make.bottom.equalTo(self.view); 等同于
    make.bottom.mas_equalTo(self.view.mas_bottom);
    
    2> 简化方法

    想要去掉mas_前缀,只用equalTo,将一下代码添加到.prefix文件中即可:

    // 添加这个宏,就不用带mas_前缀
    #define MAS SHORTHAND
    // 添加这个宏,equalTo就等价于mas_equalTo
    #define MAS SHORTHAND GLOBALS
    // 此头文件一定要放在上面两个宏的后面才可生效
    #import "Masonry.h"
    

    (4) 约束优先级

    // 设置优先级
    - (MASConstraint * (^)(MASLayoutPriority priority))priority;
    // 优先级低
    - (MASConstraint * (^)(void))priorityLow;
    // 优先级中
    - (MASConstraint * (^)(void))priorityMedium;
    // 优先级高
    - (MASConstraint * (^)(void))priorityHigh;
    

    (5) 约束比例

    // 约束值为约束对象的乘因数 即 倍数
    - (MASConstraint * (^)(CGFloat multiplier))multipliedBy;
    // 表示约束值为约束对象的除因数 即 比例
    - (MASConstraint * (^)(CGFloat divider))dividedBy;
    

    3. 使用技巧

    (1) 多行显示

    // 首先addSubview
    [self.view addSubview:self.textLabel];
    // 设置约束
    [self.textLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.center.equalTo(self.view);
        // 设置宽度 小于等于300
        make.width.mas_lessThanOrEqualTo(300);
        // 设置高度 大于等于20
        make.height.mas_greaterThanOrEqualTo(20);
    }];
    
    self.textLabel.text = @"蒹葭苍苍,白露为霜。所谓伊人,在水一方。溯洄从之,道阻且长。溯游从之,宛在水中央。蒹葭萋萋,白露未晞。所谓伊人,在水之湄。溯洄从之,道阻且跻。溯游从之,宛在水中坻。";
    
    // 1. 设置多行显示
    self.textLabel.numberOfLines = 0;
    // 2. 设置最大宽度
    self.textLabel.preferredMaxLayoutWidth = 300;
    // 3. 设置UILayout优先级及轴向
    [self.textLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
    

    (2) 设置内边距

    [self.view addSubview:self.firstView];
    [self.firstView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.left.equalTo(self.view).offset(10);
        // 注意根据UIView的坐标系,right和bottom进行取反。
        make.bottom.right.mas_equalTo(-10);
    }];
    
    [self.firstView mas_makeConstraints:^(MASConstraintMaker *make) {
        // insets方法自动做出取反操作
        make.edges.equalTo(self.view).insets(UIEdgeInsetsMake(10, 10, 10, 10));
    }];
    

    (3) 多个控件等间隔排序

    /**
     *  多个控件固定间隔的等间隔排列,变化的是控件的长度或者宽度值
     *
     *  @param axisType        轴线方向
     *  @param fixedSpacing    间隔大小
     *  @param leadSpacing     头部间隔
     *  @param tailSpacing     尾部间隔
     */
    - (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType
                        withFixedSpacing:(CGFloat)fixedSpacing
                             leadSpacing:(CGFloat)leadSpacing
                             tailSpacing:(CGFloat)tailSpacing;
    
    /**
     *  多个固定大小的控件的等间隔排列,变化的是间隔的空隙
     *
     *  @param axisType        轴线方向
     *  @param fixedItemLength 每个控件的固定长度或者宽度值
     *  @param leadSpacing     头部间隔
     *  @param tailSpacing     尾部间隔
     */
    - (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType
                     withFixedItemLength:(CGFloat)fixedItemLength
                             leadSpacing:(CGFloat)leadSpacing
                             tailSpacing:(CGFloat)tailSpacing;
    

    (4) 更新约束之后动画效果

    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
        
        [self.view addSubview:self.firstView];
        // 设置约束
        [self.firstView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.center.equalTo(self.view);
            make.width.height.mas_equalTo(100);
        }];
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        // 更新约束
        [self.firstView mas_remakeConstraints:^(MASConstraintMaker *make) {
            make.edges.equalTo(self.view).insets(UIEdgeInsetsMake(10, 10, 10, 10));
        }];
        
        // 告诉约束需要更新,但不会立即更新,
        //[self.firstView.superview setNeedsUpdateConstraints];
        [self.view setNeedsUpdateConstraints];
        // 检测当前视图及其子视图是否需要更新约束
        [self.view updateConstraintsIfNeeded];
        [UIView animateWithDuration:0.4 animations:^{
            // 立即更新约束
            [self.view layoutIfNeeded];
        }];
    }
    

    (5) For循环创建多个控件

    // 创建一个View作为容器
    UIView *lastView = [[UIView alloc] init];
    [self.view addSubview:lastView];
    [lastView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(self.view).insets(UIEdgeInsetsMake(15, 15, 15,15));
    }];
    
    for (int i = 0; i < 10; i++) {
        // 创建新的view
        UIView *view = [[UIView alloc] init];
        view.backgroundColor = [self randomColor];
        [lastView addSubview:view];
        [view mas_makeConstraints:^(MASConstraintMaker *make) {
            make.edges.equalTo(lastView).insets(UIEdgeInsetsMake(15, 15, 15,15));
        }];
        
        // 将view赋值给lastView
        lastView = view;
    }
    

    (6) UITableView动态Cell高度

    原理:

    1. 对tableView设置预估高度;
    2. 对自定义Cell里面的控件,要设置cell里最上方控件与cell.contentView上方的距离,最下方控件与cell.contentView下方的距离。
    // 1. 对tableView设置预估高度;
    - (UITableView *)tableView {
        if (!_tableView) {
            _tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
            _tableView.delegate = self;
            _tableView.dataSource = self;
            // 设置预估高度
            _tableView.estimatedRowHeight = 50;
        }
        return _tableView;
    }
    
    // 2. 对自定义Cell里面的控件,要设置cell里最上方控件与cell.contentView上方的距离,最下方控件与cell.contentView下方的距离。
    - (void)settingUI {
        [self.contentView addSubview:self.detailLabel];
        [self.detailLabel mas_makeConstraints:^(MASConstraintMaker *make) {
            make.edges.equalTo(self.contentView).insets(UIEdgeInsetsMake(15, 15, 15, 15));
        }];
    }
    

    (7) scrollView使用约束的问题

    原理:给scrollView添加唯一的子视图contentView,通过拉伸子视图的size来确定scrollViewcontentSize

    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
        
        // 创建scrollView
        UIScrollView *scrollView = [[UIScrollView alloc] init];;
        scrollView.backgroundColor = [UIColor redColor];
        [self.view addSubview:scrollView];
        [scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.edges.equalTo(self.view);
        }];
        
        // 创建contentView,添加到scrollView作为唯一子视图
        UIView *contentView = [[UIView alloc] init];
        contentView.backgroundColor = [UIColor whiteColor];
        [scrollView addSubview:contentView];
        [contentView mas_makeConstraints:^(MASConstraintMaker *make) {
            // 设置边距相对于scrollView的约束
            make.edges.equalTo(scrollView);
            // 设置宽度
            make.width.equalTo(scrollView);
        }];
        
        UIView *lastView;
        
        for (NSInteger i = 0; i < 10; i++) {
            UIView *view = [UIView new];
            view.backgroundColor = [self randomColor];
            [contentView addSubview:view];
            [view mas_makeConstraints:^(MASConstraintMaker *make) {
                if (i == 0) {
                    make.top.equalTo(contentView);
                } else {
                    make.top.equalTo(lastView.mas_bottom).offset(10);
                }
                make.left.right.equalTo(contentView);
                make.height.mas_equalTo(100);
            }];
    
            lastView = view;
        }
        
        [contentView mas_makeConstraints:^(MASConstraintMaker *make) {
            // 设置contentView的底部约束等于最后一个视图底部约束
            make.bottom.equalTo(lastView.mas_bottom);
        }];
    }
    

    相关文章

      网友评论

        本文标题:iOS自动布局框架 - Masonry详解

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