iOS技术

iOS自动布局框架 - Masonry详解

2020-11-22  本文已影响0人  hazydream

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;

注意:


(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> 方法区别

例如:

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);
    }];
}
上一篇下一篇

猜你喜欢

热点阅读