视图框架在viewWillAppear:和viewDidAppear之间变化:


85

我在应用程序中发现了一个奇怪的行为,其中一个连接IBOutlet在其视图控制器中对viewWillAppear:和的调用之间具有其连接的视图框架viewDidAppear:。这是我的UIViewController子类中的相关代码:

-(void)viewWillAppear:(BOOL)animated {
    NSLog(@"%@", self.scrollView);
}

-(void)viewDidAppear:(BOOL)animated {
    NSLog(@"%@", self.scrollView);
}

以及结果日志输出:

MyApp[61880:c07] <UIScrollView: 0x1057eff0; frame = (0 0; 0 0); clipsToBounds = YES; autoresize = TM+BM; gestureRecognizers = <NSArray: 0x10580100>; layer = <CALayer: 0x1057f210>; contentOffset: {0, 0}>
MyApp[61880:c07] <UIScrollView: 0x1057eff0; frame = (0 44; 320 416); clipsToBounds = YES; autoresize = TM+BM; gestureRecognizers = <NSArray: 0x10580100>; layer = <CALayer: 0x1057f210>; contentOffset: {0, 0}>

这清楚地表明框架在两个调用之间正在改变。我想使用该viewDidLoad方法中的视图进行设置,但是如果无法在屏幕上显示内容之前更改内容,这似乎毫无用处。可能会发生什么?


2
您正在使用自动版式吗?您是在“界面”构建器中还是以编程方式添加此视图?
Andrea

启用了自动布局,并且此视图是从情节提要中在IB中创建的。
Jumhyn 2013年

1
我从未使用过情节提要,但最有可能的是它是正确的。当自动布局引擎开始计算时,将使用视图的自动布局框架进行设置。尝试在您的视图控制器的-(void)viewDidLayoutSubviews mpethod的超级之后问同样的事情。
Andrea

这在适当的时间成功触发了我的事件,但是每当我在视图上执行任何动画时,也会调用该方法。
Jumhyn

1
viewDidLayoutSubviews是正确的方法。我只需要将所有内容放在一个子视图中,这样一来,只要更改主视图的框架,就不会调用该方法。
Jumhyn

Answers:


111

Autolayout在我们设计和开发视图GUI的方式上发生了巨大的变化。主要区别之一是autolayout不会立即更改视图大小,而是仅在触发时更改,即在特定时间显示,但我们可以强制其立即重新计算约束或将其标记为布局的“需要”。它像-setNeedDisplay
对我来说,最大的挑战是要理解并接受这一点,我们不再需要使用自动调整大小的蒙版,并且框架已成为摆放视图的无用属性。我们不再需要考虑视图位置,而是应该考虑如何在彼此相关的空间中查看它们。
当我们想混合使用旧的自动调整大小蒙版和自动布局时,就会出现问题。我们应该很快考虑自动布局的实现,并尝试避免在基于自动布局的视图层次结构中混合使用旧方法。
拥有仅使用自动调整大小蒙版的容器视图(例如视图控制器的主视图)是很好的选择,但是如果我们不尝试混合使用,则更好。
我从未使用过情节提要,但最可能的是它是正确的。使用自动布局,当自动布局引擎开始计算时,将设置视图的框架。尝试在- (void)viewDidLayoutSubviews您的视图控制器的方法超级之后问同样的事情。
自动布局引擎完成计算视图框架时,将调用此方法。


5
-(void)viewDidLayoutSubviews是我的答案!非常感谢!
FrizzTheSnail

这个答案确实是不正确的。当然可以,显然,在(5?)年中,您必须使用自动布局。但是在很多情况下(使用autolayout),您都需要在屏幕上“在用户出现之前”添加一些内容。(如果在viewDidAppear中执行此操作,将会出现闪烁。如果在viewWillAppear中进行操作-位置将是错误的。)实际的答案确实是使用viewDidLayoutSubviews。
Fattie

add something on a screen, "just before it appears to the user". The actual answer is indeed to use viewDidLayoutSubviews....您将多次展示这种观点
Andrea

156

文档中:

viewWillAppear:

通知视图控制器其视图将被添加到视图层次结构中。

viewDidAppear:

通知视图控制器其视图已添加到视图层次结构中。

结果,子视图的框架尚未在 viewWillAppear

在视图显示到屏幕之前修改UI的适当方法是:

viewDidLayoutSubviews

通知视图控制器其视图刚刚布置了其子视图。


2
gh,真烦人。甚至文档的措辞也使viewWillApepar:和viewDidAppear:似乎应该紧接彼此发生。
Jumhyn

2个月的等待这个答案...谢谢我找到了!非常感谢你!
拉斐尔·鲁伊斯·穆尼奥斯

6
应该注意的是,viewDidLayoutSubviews它将被多次调用,而并不总是在同一帧中调用(我认为有时会在第一次调用时使用CGRectZero调用它)。每次添加的子视图和视图中的其他更改都会调用它。
bauerMusic 2015年

我整天都在忙这个问题(多次调用viewDidLayoutSubviews,包括关闭视图时),然后用ef表示,然后将逻辑放在if语句中,然后将布尔值设置为true。代码已运行一次。我认为必须有一种更清洁的方法,但是已经投入了太多时间。
电磁线圈

我们必须深入了解这一点!viewDidLayoutSubviews是可怕的,seNeedDisplay不起作用
薯芋

9

呼叫

self.scrollView.layoutIfNeeded()

用你的viewWillAppear方法。之后,您可以访问它的框架,并且它的值与打印时相同viewDidAppear


1
不,这是不正确的。示例:您在屏幕上有一个导航栏(来自导航控制器)。即使在layoutIfNeeded()之后,导航栏的高度也不包括在内,因此您的帧大小也会改变。
xaphod

强制重新计算scrollview框架尺寸的好方法,如果scrollView框架与其上级视图中的约束相关联,则它在整个视图布局调用层次结构中都是一致的。
smakus

4

就我而言,将所有与框架相关的方法移至

override func viewWillLayoutSubviews()

工作完美(我试图从情节提要中修改约束)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.