AutoLayout的一些基本概念
- 利用约束来控制视图的大小和位置,系统会在运行时通过设置的约束计算得到frame再绘制屏幕
- 两个属性Content Compression Resistance(排挤,值越高越固定)和Content Hugging(拥抱),Masonry代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13
|
[view setContentHuggingPriority:UILayoutPriorityRequired
forAxis:UILayoutConstraintAxisHorizontal];
[view setContentCompressionResistancePriority:UILayoutPriorityDefaultLow
forAxis:UILayoutConstraintAxisHorizontal];
|
- multipler属性表示约束值为约束对象的百分比,在Masonry里有对应的multipliedBy函数
1 2 3 4 5
|
make.width.equalTo(superView.mas_width).multipliedBy(0.2);
|
- AutoLayout下UILabel设置多行计算需要设置preferredMaxLayoutWidth
1 2 3
| label.preferredMaxWidth = [UIScreen mainScreen].bounds.size.width - margin - padding;
|
- preferredMaxLayoutWidth用来制定最大的宽,一般用在多行的UILabel中
- systemLayoutSizeFittingSize方法能够获得view的高度
- iOS7有两个很有用的属性,topLayoutGuide和bottomLayoutGuide,这个两个主要是方便获取UINavigationController和UITabBarController的头部视图区域和底部视图区域。
1 2 3 4 5
|
make.top.equalTo(self.mas_topLayoutGuide);
|
AutoLayout关于更新的几个方法的区别
- setNeedsLayout:告知页面需要更新,但是不会立刻开始更新。执行后会立刻调用layoutSubviews。
- layoutIfNeeded:告知页面布局立刻更新。所以一般都会和setNeedsLayout一起使用。如果希望立刻生成新的frame需要调用此方法,利用这点一般布局动画可以在更新布局后直接使用这个方法让动画生效。
- layoutSubviews:系统重写布局
- setNeedsUpdateConstraints:告知需要更新约束,但是不会立刻开始
- updateConstraintsIfNeeded:告知立刻更新约束
- updateConstraints:系统更新约束
Masonry使用注意事项
- 用mas_makeConstraints的那个view需要在addSubview之后才能用这个方法
- mas_equalTo适用数值元素,equalTo适合多属性的比如make.left.and.right.equalTo(self.view)
- 方法and和with只是为了可读性,返回自身,比如make.left.and.right.equalTo(self.view)和make.left.right.equalTo(self.view)是一样的。
- 因为iOS中原点在左上角所以注意使用offset时注意right和bottom用负数。
Masonry适配iOS6和iOS7时需要注意的问题
开发项目时是先在iOS8上调试完成的,测试时发现低版本的系统会发生奔溃的现象,修复后总结问题主要是在equalTo的对象指到了父视图的父视图或者父视图同级的子视图上造成的,所以做约束时如果出现了奔溃问题百分之九十都是因为这个。
Masonry使用范例
基本写法
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
|
UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
[self.avatarView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(superview.mas_top).with.offset(padding.top);
make.left.equalTo(superview.mas_left).with.offset(padding.left);
make.bottom.equalTo(superview.mas_bottom).with.offset(-padding.bottom);
make.right.equalTo(superview.mas_right).with.offset(-padding.right);
}];
[self.avatarView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(superview).with.insets(padding);
}];
[self.avatarView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.greaterThanOrEqualTo(self.view);
make.left.greaterThanOrEqualTo(self.view.mas_left);
}];
[self.avatarView mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.greaterThanOrEqualTo(@200);
make.width.lessThanOrEqualTo(@400);
make.left.lessThanOrEqualTo(@10);
}];
[self.avatarView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(42);
make.height.mas_equalTo(20);
make.size.mas_equalTo(CGSizeMake(50, 100));
make.edges.mas_equalTo(UIEdgeInsetsMake(10, 0, 10, 0));
make.left.mas_equalTo(self.view).mas_offset(UIEdgeInsetsMake(10, 0, 10, 0));
}];
[self.avatarView mas_makeConstraints:^(MASConstraintMaker *make) {
make.height.equalTo(@[self.view.mas_height, superview.mas_height]);
make.height.equalTo(@[self.view, superview]);
make.left.equalTo(@[self.view, @100, superview.mas_right]);
}];
[self.avatarView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.greaterThanOrEqualTo(self.view.mas_left).with.priorityLow();
make.top.equalTo(self.view.mas_top).with.priority(600);
}];
[self.avatarView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view);
make.edges.equalTo(self.view).insets(UIEdgeInsetsMake(5, 10, 15, 20));
make.size.greaterThanOrEqualTo(self.view);
make.size.equalTo(superview).sizeOffset(CGSizeMake(100, -50));
make.center.equalTo(self.view);
make.center.equalTo(self.view).centerOffset(CGPointMake(-5, 10));
make.left.right.and.bottom.equalTo(self.view);
make.top.equalTo(self.view);
}];
|
AutoLayout情况如何计算UITableView的变高高度
主要是UILabel的高度会有变化,所以这里主要是说说label变化时如何处理,设置UILabel的时候注意要设置preferredMaxLayoutWidth这个宽度,还有ContentHuggingPriority为UILayoutPriorityRequried
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
| CGFloat maxWidth = [UIScreen mainScreen].bounds.size.width - 10 * 2;
textLabel = [UILabel new];
textLabel.numberOfLines = 0;
textLabel.preferredMaxLayoutWidth = maxWidth;
[self.contentView addSubview:textLabel];
[textLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(statusView.mas_bottom).with.offset(10);
make.left.equalTo(self.contentView).with.offset(10);
make.right.equalTo(self.contentView).with.offset(-10);
make.bottom.equalTo(self.contentView).with.offset(-10);
}];
[_contentLabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
|
如果版本支持最低版本为iOS 8以上的话可以直接利用UITableViewAutomaticDimension在tableview的heightForRowAtIndexPath直接返回即可。
1 2 3 4 5 6 7 8 9 10 11 12 13
| tableView.rowHeight = UITableViewAutomaticDimension;
tableView.estimatedRowHeight = 80;
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewAutomaticDimension;
}
|
但如果需要兼容iOS 8之前版本的话,就要回到老路子上了,主要是用systemLayoutSizeFittingSize来取高。步骤是先在数据model中添加一个height的属性用来缓存高,然后在table view的heightForRowAtIndexPath代理里static一个只初始化一次的Cell实例,然后根据model内容填充数据,最后根据cell的contentView的systemLayoutSizeFittingSize的方法获取到cell的高。具体代码如下
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 30 31 32 33 34 35 36 37 38 39 40
|
@interface DataModel : NSObject
@property (copy, nonatomic) NSString *text;
@property (assign, nonatomic) CGFloat cellHeight;
@end
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
static CustomCell *cell;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([CustomCell class])];
});
DataModel *model = self.dataArray[(NSUInteger) indexPath.row];
[cell makeupData:model];
if (model.cellHeight <= 0) {
model.cellHeight = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height + 1;
}
return model.cellHeight;
}
|
动画
因为布局约束就是要脱离frame这种表达方式的,可是动画是需要根据这个来执行,这里面就会有些矛盾,不过根据前面说到的布局约束的原理,在某个时刻约束也是会被还原成frame使视图显示,这个时刻可以通过layoutIfNeeded这个方法来进行控制。具体代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| [aniView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.bottom.left.right.equalTo(self.view).offset(10)
}]
[aniView mas_updateConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view).offset(30)
}]
[UIView animateWithDuration:3 animations:^{
[self.view layoutIfNeeded]
}]
|
参考文章