Masonry 是对 autolayout 的封装,优雅的链式语法和简洁易用的接口让我们在做UI开发时节省了不少时间。然而在初次使用它时,由于还对 autolayout 理解不够深,就遇到了一些问题,比如使用 Masonry 对控件添加约束后,并不会立即生效,frame 仍然是 0。如果此时我们需要这个 frame,应该怎么做呢?
1 | UIView *parent = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; |
使用 Masonry 添加约束后,如果有要设置 child 的形状为圆形,就得知道它的 frame,像下面这样写肯定不会设置成功的:
1 | [child mas_makeConstraints:^(MASConstraintMaker *make) { |
因为这时候的 frame 还是 0。曾天真的想,会不会是因为 block 中的处理是在异步线程进行的,不会等待 block 执行完就已经走到了下面使用frame的代码,(一阵狂喜,好聪明。。。),所以马上把代码改写:
1 | [child mas_makeConstraints:^(MASConstraintMaker *make) { |
然而并没有什么卵用。
想不通的时候,就只能请教 goole 了,然后找到了 Masonry 约束下获取 frame 的方法
masonry 本就是对 autolayout 的封装,使用 Masonry 就等于使用了苹果的 autolayout。使用 masonry 布局完之后,系统会在某个时间点调用各个 view 的 layoutSubViews
方法,从而更新各个控件的frame
。遗憾的是,frame 的更新并不会在刚执行完 Masonry 布局代码时立即进行,在布局代码的下一行,你所获取到的 frame 仍然是0。
想要在布局代码结束就立即获取当前某个控件正确的 frame,需要调用layoutIfNeeded函数立即刷新布局,各个控件才会按照约束条件,生成当前布局相应的frame和bounds。而调用layoutIfNeeded的目的是让系统调用layoutSubviews方法,我们也可以直接在这个方法里获取frame,因为这时候开始layout subviews,Masonry已经计算出了真实的frame。
下面附上关于autolayout更新几个方法的区别:
setNeedsLayout:标记页面需要更新,但是什么时候才会调用
layoutSubviews 去刷新布局,就不一定了。layoutIfNeeded:告知页面如果需要,就立刻更新布局。这里的“如果需要”什么意思呢?有什么条件吗?是的,只有满足如下某一个或几个条件,调用 layoutIfNeeded 才会立即刷新frame:
- 有 addSubview 操作
- 设置了view的 frame,当然前提是设置前后 frame 的值发生了变化
- 滚动一个UIScrollView
- 旋转 Screen
- 改变一个UIView大小的时候
如果不满足,就算是调用了 layoutIfNeeded 也不会立刻执行 layoutSubViews 进行 frame 更新。
如果我们想不管什么情况,都强制执行 layoutSubViews 进行 frame 更新怎么办呢?那就是同时调用下面这两个方法:1
2[self setNeedsLayout];
[self layoutIfNeeded];
这样必然会调用 layoutSubViews 。