【问题标题】:View frame changes between viewWillAppear: and viewDidAppear:viewWillAppear: 和 viewDidAppear: 之间的视图框架变化
【发布时间】:2013-07-12 07:56:15
【问题描述】:

我在我的应用程序中发现了一个奇怪的行为,其中连接的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 方法中的视图进行设置,但是如果内容在屏幕上之前我无法更改,那似乎毫无用处。会发生什么?

【问题讨论】:

  • 你在使用自动布局吗?您是在界面生成器中添加此视图还是以编程方式添加此视图?
  • 自动布局已启用,此视图是在 IB 中从故事板创建的。
  • 我从未使用过故事板,但很可能它是正确的。使用自动布局引擎在自动布局引擎开始计算时设置视图的自动布局框架。尝试在视图控制器的 - (void)viewDidLayoutSubviews 方法的 super 之后立即询问相同的问题。
  • 这会在正确的时间成功触发我的事件,但是每当我在视图上执行任何动画时也会调用该方法。
  • viewDidLayoutSubviews 是正确的方法。我只需将所有内容放在子视图中,这样每当我更改主视图的框架时就不会重新调用该方法。

标签: iphone ios objective-c uiscrollview


【解决方案1】:

来自documentation:

viewWillAppear:

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

viewDidAppear:

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

因此,子视图的框架尚未在viewWillAppear 中设置:

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

viewDidLayoutSubviews

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

【讨论】:

  • 呃,这很烦人。甚至文档的措辞也让人觉得 viewWillApepar: 和 viewDidAppear: 应该一个接一个地发生。
  • 2 个月等待这个答案......谢谢我找到它!非常感谢!
  • 应该注意viewDidLayoutSubviews 会被多次调用,并不总是在同一个帧中,(我认为有时在第一次调用时会使用 CGRectZero 调用它)。每个添加的子视图和视图中的其他更改都会调用它。
  • 我整天都在插话(viewDidLayoutSubviews被多次调用,包括关闭视图时),只是用它说了ef,并将逻辑放在if语句中并做了一个代码运行一次后设置为 true 的布尔值。我认为必须有一种更清洁的方法,但已经花费了太多时间。
  • 我们必须查明真相! viewDidLayoutSubviews 很糟糕,seNeedDisplay 不起作用
【解决方案2】:

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

【讨论】:

  • - (void)viewDidLayoutSubviews 是我的答案!非常感谢!
  • 这个答案真的不正确。是的,当然,很明显,(5?)年来,您必须使用自动布局。但是在许多情况下(使用自动布局),您需要在屏幕上添加一些东西,“就在它出现在用户面前之前”。 (如果您在 viewDidAppear 中执行此操作,则会出现闪烁。如果您在 viewWillAppear 中执行此操作 - 位置将是错误的。)实际答案确实是使用 viewDidLayoutSubviews。
  • add something on a screen, "just before it appears to the user". The actual answer is indeed to use viewDidLayoutSubviews.... 您将多次展示该视图
【解决方案3】:

打电话

self.scrollView.layoutIfNeeded()

在您的 viewWillAppear 方法中。之后您可以访问它的框架,它的值与您在viewDidAppear中打印的值相同

【讨论】:

  • 不,这是不正确的。示例:您的屏幕上有一个导航栏(来自您的导航控制器)。即使在 layoutIfNeeded() 之后,导航栏的高度也不包括在内,所以你的框架大小会改变。
  • IS 是强制重新计算滚动视图框架尺寸的好方法,以便在滚动视图框架绑定到约束时在整个视图布局调用层次结构中保持一致在它的superview内。
【解决方案4】:

在我的情况下,将所有与框架相关的方法移动到

override func viewWillLayoutSubviews()

完美运行(我试图从情节提要修改约束)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-11
    • 1970-01-01
    • 2013-11-28
    • 1970-01-01
    • 2012-08-26
    • 1970-01-01
    相关资源
    最近更新 更多