前言

我相信很多同学曾经都遇到过这样的问题:明明用masonry布好局了,怎么获取到的frame就是0呢?

解决问题不难,百度一下就能找到答案,但如果只是单纯的解决问题而不去想为什么或许会制约我们的成长。

问题重现

1. 先看下面这段代码:

[self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) {

make.edges.mas_equalTo(UIEdgeInsetsMake(0, 0, 0, 0));

}];

NSLog(@"%@", self.scrollView);

打印结果:

<UIScrollView: 0x7fc461014000; frame = (0 0; 0 0);

2. 再看一段代码:

[self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) {

make.edges.mas_equalTo(UIEdgeInsetsMake(0, 0, 0, 0));

}];

// 先调用superView的layoutIfNeeded方法再获取frame

[self.view layoutIfNeeded];

NSLog(@"%@", self.scrollView);

打印结果:

<UIScrollView: 0x7fc72d016000; frame = (0 0; 375 667);

3. 最后看一段代码:

[self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) {

make.edges.mas_equalTo(UIEdgeInsetsMake(0, 0, 0, 0));

}];

// 0.1秒后获取frame

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

NSLog(@"%@", self.scrollView);

});

打印结果:

<UIScrollView: 0x7fc8cc024400; frame = (0 0; 375 667);

问:为什么要调用superView的layoutIfNeeded或者延迟0.1秒后才能获取到frame?

iOS高端妓术裙的裙主:锤神 告诉我:

首先你要知道autolayout和frame的关系,autolayout最终也是转成frame,masonry是建立在autolayout之上的。你没获取到正确的值,那是因为约束还没布局完成。相当于就是我们给一定的约束,系统内部自己去根据约束条件转成对应的frame,而这需要一个过程。想要拿到正确的frame最好的就是让autolayout完成之后,什么时候完成呢?那就是在layoutsubviews for view or didlayoutsubviews for controller 里获取,当然在控制器的viewdidappear里也拿得到,但是正确做法和最佳做法还是在控制器里的viewdidlayout里获取最好~因为autolayout会根据约束,不停的去改变frame,这方法里最后拿到的frame就是最终姿势.

锤神最后补充了一句:

可能有些需要纠正,但这基本是我一个大概的理解~~

我来总结一下锤神的回答:

约束转frame需要时间。

探求原因

先不管锤神的回答,自己一步一步思考下。

猜想:Masonry的block的回调不是立即的

比如这段代码:

NSLog(@"a");

[self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) {

NSLog(@"b");

make.edges.mas_equalTo(UIEdgeInsetsMake(90, 90, 90, 90));

}];

NSLog(@"c");

按照猜想,打印结果应该是“acb”

但实际上却是“abc”。

为什么?

看看Masonry的源码就知道了:

- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {

self.translatesAutoresizingMaskIntoConstraints = NO;

MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];

block(constraintMaker);

return [constraintMaker install];

}

我们调用mas_makeConstraints这个方法的时候block就立即调用了。

那这就更奇怪了,既然获取frame的时候已经设置好约束了,怎么获取到的frame却是0呢?

因为你设置好了约束不代表它就转化成了frame,正如锤神所说,这需要一个过程

0.1秒后能获取到正确的frame是因为这个时候布局已经完成。

那么我们怎么知道布局完成的这个点?

锤神说的是didlayoutsubviews这个方法,虽然锤神很牛逼,但我们也应该保持怀疑态度,看下官方文档验证一下锤神的说法:

再来一段代码让大家加深一下印象:

- (void)viewDidLoad {

[super viewDidLoad];

// Do any additional setup after loading the view, typically from a nib.

self.scrollView = [[UIScrollView alloc] init];

[self.view addSubview:self.scrollView];

[self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) {

NSLog(@"建立约束");

make.edges.mas_equalTo(UIEdgeInsetsMake(90, 90, 90, 90));

}];

NSLog(@"直接获取frame:%@", self.scrollView);

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

NSLog(@"0.1秒后获取frame:%@", self.scrollView);

});

}

- (void)viewDidLayoutSubviews {

NSLog(@"viewDidLayoutSubviews时的frame:%@", self.scrollView);

}

控制台信息:

masonry block里的代码是最先执行的,接着是直接获取frame,然后是viewDidLayoutSubviews,最后才是0.1秒后block。

layoutIfNeeded做了什么事?

弄清了延迟0.1加载可以获取到正确frame的原因,再来研究下为什么调用了[xx.superView layoutIfNeeded]也能获取到正确frame的原因。

先看一下官方文档对这个方法的描述:

立即布局

我在上面的代码中多加一句[self.scrollView.superview layoutIfNeeded];:

- (void)viewDidLoad {

[super viewDidLoad];

// Do any additional setup after loading the view, typically from a nib.

self.scrollView = [[UIScrollView alloc] init];

[self.view addSubview:self.scrollView];

[self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) {

NSLog(@"建立约束");

make.edges.mas_equalTo(UIEdgeInsetsMake(90, 90, 90, 90));

}];

// 多加的这句

[self.scrollView.superview layoutIfNeeded];

NSLog(@"直接获取frame:%@", self.scrollView);

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

NSLog(@"0.1秒后获取frame:%@", self.scrollView);

});

}

- (void)viewDidLayoutSubviews {

NSLog(@"viewDidLayoutSubviews时的frame:%@", self.scrollView);

}

控制台信息:

可以发现,调用[self.scrollView.superview layoutIfNeeded];时回调了viewDidLayoutSubviews方法。这就是调用[xx.superView layoutIfNeeded]后能获取到正确frame的原因。

其他人的回答,大家可以参考下

1.在Stack Overflow的提问:https://stackoverflow.com/questions/46387298/why-i-cant-get-the-frame-immediately-when-i-use-masonry

2.在SegmentFault的提问:https://segmentfault.com/q/1010000011332667

引申:UIButton的titleLabel的宽度为0

之前在开发中遇到的问题,当时还不怎么理解,先看代码:

UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(90, 90, 200, 40)];

[self.view addSubview:button];

[button setTitle:@"呵呵哒" forState:UIControlStateNormal];

NSLog(@"%@", button);

NSLog(@"%@", button.titleLabel);

控制台信息:

titleLabel的size是0。

如果我加上这句:[button layoutIfNeeded];,titleLabel的size就是期望的了:

参考:https://stackoverflow.com/questions/25758599/uibutton-titlelabel-frame-size-returning-cgsize-with-zero-width-height

总结

遇到问题,如果找不到满意的答案,可以:

主动去问答网站提问

主动虚心向大神请教

上面两招同时使用效果更佳

当晚更新

后来有个同学告诉我不需要延迟0.1秒,延迟0秒就可以获取到frame了:

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

NSLog(@"0秒后获取frame:%@", self.scrollView);

});

是的,你没看错,延迟0秒也可以获取frame。

我很懵逼,延迟0秒跟没延迟有区别?

NSLog(@"直接获取frame:%@", self.scrollView);

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

NSLog(@"0秒后获取frame:%@", self.scrollView);

});

不明白为什么0秒后获取frame可以但是直接获取却不可以。

这位同学的说法是:

他分享了一篇关于runloop的文章:http://www.cocoachina.com/ios/20150601/11970.html

很显然以我现在的水平根本hold不住。

这个问题我会一直研究,这期间肯定会逐渐学习runloop的知识,希望能早点领悟这个问题吧。

非常欢迎大神对这个问题提出自己的看法

iOS-使用Masonry布局不能立即获取到frame相关推荐

  1. 使用Masonry布局后不能立即获取到frame

    经常有这样的需求,给view添加圆角,目前用过三种实现方式,第四种我也没用过.圆角总容易伴随着离屏渲染的问题 1 .设置layer.cornerRadius属性 view.layer.cornerRa ...

  2. iOS 添加在scrollview的子控件,用masonry布局的问题

    当在scrollview上添加子控件并用masonry布局时,发现运行后不是想要的布局,解决方法是需要添加一个view,上下左右,宽高.都要和scrollview一致.然后再在view上添加子控件即可 ...

  3. iOS Masonry 布局- UIScrollView/Masonry自动布局对UIScrollView的内容自适应

    2020年10月13日13:26:37「复习」 控制器中布局一般基础都是以UIscrollview为底部视图进行绘制的,方便我们进行屏幕适配. 在使用masonry布局的时候如何让UIscrollvi ...

  4. IOS之Masonry约束的使用

    IOS之Masonry约束的使用 Masonry是做约束的,类似苹果开发的约束,做屏幕的适配,有xib开发,纯代码开发,storyboard约束等其他第三方框架. pod 加入 pod 'Masonr ...

  5. iOS【终极方案】精准获取webView内容高度,自适应高度

    关于WebView内容高度的获取,相信很多人都踩过坑,无法获取到准确高度,导致页面布局出现差错,搜到的资料很多但都无法解决问题,以下是个人经验总结: 项目需求实现H5文章&原生评论效果,文章是 ...

  6. iOS之页面布局-踩坑的原由

    iOS之页面布局 原文请点击 在<iOS 7 UI Transition Guide>中有在<iOS 7 UI Transition Guide>的Bar and Bar Bu ...

  7. android界面布局错位,IOS 浏览器页面布局错位(如:点不到)的分析与解决

    IOS 浏览器页面布局错位(如:点不到)的分析与解决 IOS 浏览器软键盘的拉起与收缩.微信 IOS 浏览器底部导航条的显示与隐藏,很容易导致页面布局错位(相对窗体的绝对定位元素): 明明按钮在这里, ...

  8. iOS 【奇巧淫技】获取webView内容高度

    针对获取webView高度问题之前写过一个方案--通过监听WebView的scrollView的变化来实时更新高度 附上链接: iOS[终极方案]精准获取webView内容高度,自适应高度 本文是给出 ...

  9. 记录一个因为使用masonry 布局造成的宽度不准确的问题

    问题表现 如图,label 超出了cell的范围,明显的,计算出来的文字高度没有 label真实高度大 代码 添加约束使用masonry, 计算高度使用的是frame ///label 约束[self ...

最新文章

  1. 死磕Java并发:J.U.C之阻塞队列:LinkedBlockingDeque
  2. mxnet与pytorch比较 bn层
  3. (12)Verilog HDL可综合与不可综合区别(第3天)
  4. Qt4_使用项视图的简便类
  5. 删除MySql表中的大量记录后,文件不变小的解决方法
  6. Keyboard Control
  7. 前端开发中一些常用技巧总结
  8. Python让繁琐工作自动化——chapter17 操作图像
  9. php 虚拟主机和虚拟目录的配置
  10. Poi导入校验因单元格格式产生的空对象问题
  11. vue3源码effect
  12. 时间加减计算器_财政局刚刚回应丨禁止携带计算器,否则成绩无效!
  13. LevelDB 源码分析
  14. AutoCAD .NET: 遍历模型空间
  15. win7 隐藏受保护的操作系统文件 消失
  16. 哪些人适合读博士?哪些人不适合读博士?
  17. Win10远程桌面,用户账户无效的解决方法
  18. 苹果以旧换新活动_苹果“新iPhone SE”正式发布,这次京东补贴的有点狠!
  19. 从精英云到普惠云,青云QingCloud的“性格”变了
  20. [osg]OSG中的颜色数组,法向数组

热门文章

  1. 大数据集群服务器的规划,如何安排服务器
  2. 树莓派不插HDMI不能开机的解决方法
  3. 二元函数可微与偏导数_二元函数的连续、偏导数、可微之间的关系
  4. POSIX正则表达式
  5. Linux怎么查看软件安装路径 查看mysql安装在哪
  6. Proteus 8 Professional 仿真软件安装包和汉化
  7. mysql时间戳是什么意思_mysql中TIMESTAMP时间戳详解
  8. JavaWeb基础核心技术-佟刚-专题视频课程
  9. 考早了!华为认证推出“一试双证”,IE直接补贴3000元
  10. 【手机端测试的关注点】Android 和 IOS 两大主流系统测试点