UIView如何优雅的自适应布局(Masonry)

怎么安装Masonry和怎么makeConstraints不多说, 我们假设你会...

cell的高度自适应 - 子view撑起父view

如果你经常使用这方法, 可以跳过这节.
开发中常常需要UITableViewCell高度自适应, 最最常见的, 就是UILabel的多行文字显示, 我们都是给内部view加上合适的约束, 比如上下左右, 宽高, 来把父视图给撑起来.
比如, 我们在界面中间需要放一个contentView, 居中, view的内部呢, 我们假设有一个子view-innerView, 上下左右距离父视图均为20, 我们一般配合Masonry, 可以这么写:

UIView *contentView = [[UIView alloc] init];
contentView.backgroundColor = [UIColor blueColor];
[self.view addSubview:contentView];

UIView *innerView = [[UIView alloc] init];
innerView.backgroundColor = [UIColor redColor];
[contentView addSubview:innerView];

[innerView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.edges.mas_equalTo(UIEdgeInsetsMake(20, 20, 20, 20));
    make.size.mas_equalTo(CGSizeMake(100, 100));
}];

[contentView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.centerX.centerY.mas_equalTo(0);
}];

通常写法


这里有一个注意点是我们把这个contentView加到self.view上去了, 自适应的好处是我们最终并不需要知道他的frame是多少, 界面显示的时候已经自己适配好了.

需要结果的自适应

开发中还有一种是我们需要知道frame的情况, 给tableView设置tableHeaderView, 或者其他什么框架, 需要我们提供一个有已知frameview, 这时候怎么办呢?
用layoutIfNeeded? 让UI更新, 拿到frame? 我们试试, 把contentView不设置父视图, 打印一下他的frame

UIView *contentView = [[UIView alloc] init];
contentView.backgroundColor = [UIColor blueColor];

UIView *innerView = [[UIView alloc] init];
innerView.backgroundColor = [UIColor redColor];
[contentView addSubview:innerView];

[innerView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.edges.mas_equalTo(UIEdgeInsetsMake(20, 20, 20, 20));
    make.size.mas_equalTo(CGSizeMake(100, 100));
}];

[contentView layoutIfNeeded];

NSLog(@"%@", NSStringFromCGRect(innerView.frame)); // {{20, 20}, {0, 0}}
NSLog(@"%@", NSStringFromCGRect(contentView.frame)); // {{0, 0}, {0, 0}}

都是0, 这时候我们设置view肯定不成功啊, 都是0, 咋整呢?

我们创建view, 内部那么多子view, 让我通过高低来计算出frame, 不是不可以, 很麻烦, 也很chǔn, 那我们能不能, 还是让他自适应, 我约束子视图, 最后在不添加到父视图的情况下, 直接获取到contentView的size呢?

优雅的自适应

很简单, 直接在[contentView layoutIfNeeded];前面加一句话

...
[contentView mas_makeConstraints:^(MASConstraintMaker *make) {
	// 这边的约束注意了, 我可以什么都不写, 完全让他自动适配
    // 又或者, 我限制宽高等属性
    // 但是千万注意, 此时contentView是没有父视图的, 它是不可以设置相对约束的
}];

[contentView layoutIfNeeded];

NSLog(@"%@", NSStringFromCGRect(innerView.frame)); // {{20, 20}, {100, 100}}
NSLog(@"%@", NSStringFromCGRect(contentView.frame)); // {{-70, -70}, {140, 140}}

contentView并不需要添加到父视图, 由于其内部视图已经做好了约束, 确实把它撑起来了.

最后我们得到innerView100*100, contentView140*140, 因为frame.origin我们没有设置, 显示的是-70, 这个不重要, 当他被添加到其他视图上时, origin会自动更改的.

到这一步, 我们得到一个view, 我只要把内部的view约束好, 就能拿到他的frame, 这样我设置header或者其他需要的地方的时候, 我可以直接给出正确的size, 就不需要自己去计算, 是不是很优雅.

posted @ 2021-09-16 23:49  CleverPeng  阅读(884)  评论(0)    收藏  举报