1. 概念
iOS通过纯代码进行UI开发的话,屏幕适配有时会比较麻烦,所以一般都会使用 自动化布局框架 进行屏幕适配工作,其中 Masonry 是一种非常流行的第三方布局框架。
Masonry 是一个轻量级的布局框架,拥有自己的描述语法,采用更优雅的 链式语法 封装自动布局,简洁明了,并具有高可读性,而且同时支持 iOS 和 Max 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的封装;
支持CGSize
、CGPoint
、NSNumber
、UlEdgeinsets
。
例如:
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 *)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
来确定scrollView
的contentSize
。
- (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);
}];
}