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

Posted Garenge

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UIView如何优雅的自适应布局(Masonry)相关的知识,希望对你有一定的参考价值。

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

cell的高度自适应

开发中常常需要UITableViewCell高度自适应, 最最常见的, 就是UILabel的多行文字显示, 我们都是让内部view自适应来撑起父视图. 一般的view自适应我们也是这么做的

子view撑起父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, 或者其他什么框架, 需要我们提供一个有已知frame的view, 这时候怎么办呢, 我们试试, 把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 layoutIfNeeded];

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

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

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

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

实战

headerView


这是我们常见的用户信息, 我现在要求你只传给我一个有已知size的headerView

那我们的方法就如下

- (UIView *)getHeaderView {
    UIView *headerView = [[UIView alloc] init];

    UIView *iconView = [[UIView alloc] init];
    [headerView addSubview:iconView];

    [iconView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.mas_equalTo(20);
        make.height.mas_equalTo(40);
        make.width.mas_equalTo(40);
        make.left.mas_equalTo(20);
    }];

    UIView *nameView = [[UIView alloc] init];
    [headerView addSubview:nameView];

    //  写法1
//    [nameView mas_makeConstraints:^(MASConstraintMaker *make) {
//        make.top.equalTo(iconView.mas_bottom).with.offset(20);
//        make.left.mas_equalTo(20);
//        make.right.mas_equalTo(-20);
//        make.height.mas_equalTo(40);
//        make.bottom.mas_equalTo(-40);
//    }];
//
//    [headerView mas_makeConstraints:^(MASConstraintMaker *make) {
//        make.width.mas_equalTo(320);
//    }];

    // 写法2
    [nameView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(iconView.mas_bottom).with.offset(20);
        make.left.mas_equalTo(20);
        make.right.mas_equalTo(-20);
        make.width.mas_equalTo(280);
        make.height.mas_equalTo(40);
        make.bottom.mas_equalTo(-40);
    }];

    [headerView mas_makeConstraints:^(MASConstraintMaker *make) {

    }];

    [headerView layoutIfNeeded];

    NSLog(@"%@", NSStringFromCGRect(headerView.frame)); // {{-160, -80}, {320, 160}}
    NSLog(@"%@", NSStringFromCGRect(iconView.frame)); // {{20, 20}, {40, 40}}
    NSLog(@"%@", NSStringFromCGRect(nameView.frame)); // {{20, 80}, {280, 40}}

    return headerView;
}

其中写法1就是headerView限制了宽度320, 里面的nameView左右距离20, 那么nameView就是280
写法2就是nameView限制了宽度280, 左右各20, 那headerView宽就是320

至此, 我们在还没有加到父视图上, 还没有对headerView有任何人为计算的情况下, 获得了一个已知size的headerView, 拿去干别的事岂不是很方便?

ios相关

以上是关于UIView如何优雅的自适应布局(Masonry)的主要内容,如果未能解决你的问题,请参考以下文章

ios怎么实现uiview的自适应高度

iOS自适应布局之Masonry

Masonry 布局 cell 高度适应的一种方案(实现类似朋友圈简单布局)

Masonry实现label宽高度自适应

iOS自适应布局之Masonry

SDAutoLayout:比masonry更简单易用的自动布局库