Masonry的日常使用

一. 使用baseline

1
2
3
4
5
6
7
// 自定义view中:
#pragma mark - Override
// 返回自定义的baseline的view
- (UIView *)viewForBaselineLayout
{
return _baseView;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// item1&item2&item3 : 是上面自定义View的对象
[item1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo(self.view.mas_left).with.offset(8);
make.top.mas_equalTo(self.view.mas_top).with.offset(200);
}];

// 跟第一个item的baseline对齐
[item2 mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo(item1.mas_right).with.offset(10);
make.baseline.mas_equalTo(item1.mas_baseline);
}];

// 跟第一个item的baseline对齐
[item3 mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo(item2.mas_right).with.offset(10);
make.baseline.mas_equalTo(item1.mas_baseline);
}];

二. 在屏幕边缘控件布局技巧

1
2
// 注释掉这个宏定义,就直接使用length值做约束,否则的话,使用新的mas_topLayoutGuide和mas_bottomLayoutGuide
#define NEW_FEATURE
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- (void)updateViewConstraints 
{
#ifndef NEW_FEATURE
// 根据新的length值更新约束
[_topView mas_updateConstraints:^(MASConstraintMaker *make) {
// 直接利用其length属性,避免iOS、SDK版本升级后topLayoutGuide不再是UIView
make.top.equalTo(self.view.mas_top).with.offset(self.topLayoutGuide.length);
NSLog(@"[updateViewConstraints] top: %g", self.topLayoutGuide.length);
}];
// 根据新的length值更新约束
[_bottomView mas_updateConstraints:^(MASConstraintMaker *make) {
// 直接利用其length属性,避免iOS、SDK版本升级后topLayoutGuide不再是UIView
make.bottom.equalTo(self.view.mas_bottom).with.offset(-(self.bottomLayoutGuide.length));
NSLog(@"[updateViewConstraints] bottom: %g", self.bottomLayoutGuide.length);
}];
#endif
[super updateViewConstraints];
}

方式一. 直接使用UIViewController的topLayoutGuide.length

1
2
3
4
5
6
7
8
[_topView mas_makeConstraints:^(MASConstraintMaker *make) {
make.height.equalTo(@40);
make.left.and.right.equalTo(self.view);
}];
[_bottomView mas_makeConstraints:^(MASConstraintMaker *make) {
make.height.equalTo(@40);
make.left.and.right.equalTo(self.view);
}];

方式二. 使用mas_topLayoutGuide和mas_bottomLayoutGuide

1
2
3
4
5
6
7
8
9
10
[_topView mas_makeConstraints:^(MASConstraintMaker *make) {
make.height.equalTo(@40);
make.left.and.right.equalTo(self.view);
make.top.equalTo(self.mas_topLayoutGuide);
}];
[_bottomView mas_makeConstraints:^(MASConstraintMaker *make) {
make.height.equalTo(@40);
make.left.and.right.equalTo(self.view);
make.bottom.equalTo(self.mas_bottomLayoutGuide);
}];

三. UITabview cell height 计算方式

方式一. iOS 8 的Self-sizing特性

1
2
3
4
5
6
_tableView.estimatedRowHeight = 80.0f;

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewAutomaticDimension;
}

方式二. 调用systemLayoutSizeFittingSize:获取高度.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 
{
if (!_templateCell)
{
_templateCell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([Case4Cell class])];
}
// 获取对应的数据
Case4DataEntity *dataEntity = _data[(NSUInteger) indexPath.row];
// 判断高度是否已经计算过
if (dataEntity.cellHeight <= 0)
{
// 填充数据
[_templateCell setupData:dataEntity];
// 根据当前数据,计算Cell的高度,注意+1
dataEntity.cellHeight = [_templateCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height + 0.5f;
NSLog(@"Calculate: %ld, height: %g", (long) indexPath.row, dataEntity.cellHeight);
}
else
{
NSLog(@"Get cache: %ld, height: %g", (long) indexPath.row, dataEntity.cellHeight);
}
return dataEntity.cellHeight;
}

注意: 计算UILabel的preferredMaxLayoutWidth值,多行时必须设置这个值,否则系统无法决定Label的宽度

1
2
3
4
CGFloat preferredMaxWidth = [UIScreen mainScreen].bounds.size.width - 44 - 4 * 3;
_contentLabel.preferredMaxLayoutWidth = preferredMaxWidth;
// 这行是为了让Label中的内容全部展示
[_contentLabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];

四. 确定当前ViewController显示的范围

方式1. 直接使用length值做约束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
[_topView mas_makeConstraints:^(MASConstraintMaker *make) {
make.height.equalTo(@40);
make.left.and.right.equalTo(self.view);
}];

[_bottomView mas_makeConstraints:^(MASConstraintMaker *make) {
make.height.equalTo(@40);
make.left.and.right.equalTo(self.view);
}];

// handler called updateViewConstraints

- (void)updateViewConstraints
{
// 根据新的length值更新约束
[_topView mas_updateConstraints:^(MASConstraintMaker *make) {
// 直接利用其length属性,避免iOS、SDK版本升级后topLayoutGuide不再是UIView
make.top.equalTo(self.view.mas_top).with.offset(self.topLayoutGuide.length);
NSLog(@"[updateViewConstraints] top: %g", self.topLayoutGuide.length);
}];

// 根据新的length值更新约束
[_bottomView mas_updateConstraints:^(MASConstraintMaker *make) {
// 直接利用其length属性,避免iOS、SDK版本升级后topLayoutGuide不再是UIView
make.bottom.equalTo(self.view.mas_bottom).with.offset(-(self.bottomLayoutGuide.length));
NSLog(@"[updateViewConstraints] bottom: %g", self.bottomLayoutGuide.length);
}];
[super updateViewConstraints];
}

方式2. 使用新的mas_topLayoutGuide和mas_bottomLayoutGuide

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[_topView mas_makeConstraints:^(MASConstraintMaker *make) {
make.height.equalTo(@40);
make.left.and.right.equalTo(self.view);
make.top.equalTo(self.mas_topLayoutGuide);
}];

[_bottomView mas_makeConstraints:^(MASConstraintMaker *make) {
make.height.equalTo(@40);
make.left.and.right.equalTo(self.view);
make.bottom.equalTo(self.mas_bottomLayoutGuide);
}];

// Event called updateViewConstraints (可选)

- (void)updateViewConstraints
{
[super updateViewConstraints];
}

五. 引用MASConstraint

1
2
3
4
5
6
7
8
9
10
// one : 创建一个引用
@property (strong, nonatomic) MASConstraint *constraint;
// two : 赋值
[view mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.and.right.equalTo(self.view);
make.top.equalTo(self.mas_topLayoutGuideBottom);
_constraint = make.height.equalTo(@(value ...));
}];
// three : 更新约束的值
_constraint.equalTo(@(ohter value...));

此外还可以这么操作:

1
2
3
4
// 卸载这根儿约束
[_constraint uninstall];
// 在把这根儿约束填上
[_constraint install];

更新UITabelViewCell的高度的两种方式:

  1. 刷新方法1
1
2
3
// 只会重新计算高度,不会reload cell,所以只是把原来的cell撑大了而已,还是同一个cell实例
[_tableView beginUpdates];
[_tableView endUpdates];
  1. 刷新方法2
1
2
// 先重新计算高度,然后reload,不是原来的cell实例
[_tableView reloadRowsAtIndexPaths:@[index] withRowAnimation:UITableViewRowAnimationFade];

最后还可以通过下面的方式来展示更新的效果

1
[_tableView scrollToRowAtIndexPath:index atScrollPosition:UITableViewScrollPositionMiddle animated:YES];

六. 巧用优先级保证内容可见

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// _containerView 中包含了一个 支持滑动的_tipLabel
// step one :
[_tipLabel mas_makeConstraints:^(MASConstraintMaker *make) {
// 设置边界条件约束,保证内容可见,优先级1000
make.left.greaterThanOrEqualTo(_containerView.mas_left);
make.right.lessThanOrEqualTo(_containerView.mas_right);
make.top.greaterThanOrEqualTo(_containerView.mas_top);
make.bottom.lessThanOrEqualTo(_containerView.mas_bottom);
// 优先级要比边界条件低750
_leftConstraint = make.centerX.equalTo(_containerView.mas_left).with.offset(50).priorityHigh();
// 优先级要比边界条件低750
_topConstraint = make.centerY.equalTo(_containerView.mas_top).with.offset(50).priorityHigh();
make.width.mas_equalTo(CGRectGetWidth(_tipLabel.frame) + 8);
make.height.mas_equalTo(CGRectGetHeight(_tipLabel.frame) + 4);
}];

// setp two
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panWithGesture:)];
[_containerView addGestureRecognizer:pan];

// setp three
- (void)panWithGesture:(UIPanGestureRecognizer *)pan
{
CGPoint touchPoint = [pan locationInView:_containerView];
_logLabel.text = NSStringFromCGPoint(touchPoint);
_leftConstraint.offset = touchPoint.x;
_topConstraint.offset = touchPoint.y;
}

七. 约束生效时机(以动画为例)

创建一个承载改变约束的视图View

1
@property (nonatomic, strong) UILabel *animationLabel;

设置最初的位置

1
2
3
4
5
6
7
8
9
// 有个引用约束的前提
@property (nonatomic, strong) MASConstraint *centerXConstraint;
// 设置最初的位置
[_animationLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.equalTo(@(200));
make.height.equalTo(@(40));
_centerXConstraint = make.centerX.equalTo(self.view.mas_centerX);
make.centerY.equalTo(self.view.mas_centerY);
}];

核心, 查看约束生效时机带来的动画效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (void)action:(id)sender 
{
// 设置初始状态
_centerXConstraint.equalTo(@(-CGRectGetWidth(self.view.frame)));
// 立即让约束生效
[self.view layoutIfNeeded];

// 设置动画约束
_centerXConstraint.equalTo(@0);
// 动画生效
[UIView animateWithDuration:0.3f animations:^{
[self.view layoutIfNeeded];
}];
}