iOS自动布局框架 - Masonry详解

本文章转自https://www.jianshu.com/p/428783e1e47e,学习使用

1. 概念

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

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;

综述:
make用第一次添加的, update用最新的, remake不但用最新的,同时还会把原有的统统删除,不管是不是同类约束

注意:

  • 使用 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高度

原理:

  • 对tableView设置预估高度;
  • 对自定义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);
    }];
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容