为什么我需要在viewDidLoad中手动设置视图的框架?

32

我有一个相当基本的设置,其中包含一个UINavigationController和一个UITabBarController。我想以编程方式布局该navcontroller的rootViewController视图,但是当我在viewDidLoad中查看self.view.frame时,例如在横向模式下,我得到了如下结果:

1. view frame: {{20, 0}, {748, 1024}} // looks like an odd portrait mode

然后我自动旋转到竖屏模式,结果出现了这个:

2. view frame: {{0, 0}, {768, 911}}

然后当我回到横屏状态时,该框架现在是这样的:

3. view frame: {{0, 0}, {1024, 655}}

此外,自动旋转事件将在第2和第3帧值之间来回切换。

为了解决#1的问题,我目前在viewDidLoad中执行以下操作:

if (UIInterfaceOrientationIsPortrait(self.interfaceOrientation)) {
    self.view.frame = CGRectMake(0, 0, 768, 911);
} else {
    self.view.frame = CGRectMake(0, 0, 1024, 655);
}

我觉得我显然漏掉了什么。为什么视图的默认框架与其自动旋转回相同方向时的框架不匹配?视图框架没有设置为初始方向吗?非常困惑......

我应该提到,上述内容中包括我笨拙的hack,都在视觉上没有改变任何东西。我有这个hack的原因是,当我将子视图布局到此视图中时,它们将基于我的期望位置,即导航栏下方的左上角。

我做错了什么?

更新:关闭视图上的所有自适应大小功能会更改结果#1:

view frame: {{0, 0}, {748, 1024}}

看起来更接近一些了,但仍然无法匹配#3。

2个回答

102

在viewDidLoad中,视图的frame不一定与最终显示时相同。UIKit根据将要出现的上下文调整您的视图控制器的视图的大小和位置。该大小基于界面方向以及任何可见的导航栏、标签栏、工具栏或状态栏的尺寸来确定(状态栏本身的高度可能会发生变化,例如在通话时)。

了解视图控制器的视图何时加载和显示有助于理解发生的事情:

  1. 第一次访问您的视图控制器的view属性。这可能发生在您自己的代码中,也可能是响应用户操作(例如选择选项卡)时UIKit中的操作。

  2. 如果定义了loadView,则UIKit通过调用它懒加载您的视图控制器的视图,否则从指定在initWithNibName:bundle:中的NIB文件加载视图。如果两者都不存在,UIKit只会加载一个空视图。

  3. 当视图及其子视图完全加载后,UIKit调用viewDidLoad一次。此时,视图的帧将是在NIB或loadView中设置的任何内容。

  4. 某些东西请求UIKit显示您的视图控制器的视图。同样,这可能是用户操作(例如点击选项卡)或您的代码中的显式方法调用,例如pushViewController:animated:presentModalViewController:animated:

  5. UIKit根据上述上下文调整视图大小。

  6. UIKit调用viewWillAppear:。此时,frame应该是将要显示的大小。 (?) EDIT: 这可能不再正确。请参见下面的注释。

  7. UIKit显示视图,带或不带动画。

  8. UIKit调用viewDidAppear:

如您所见,如果需要在视图呈现之前知道其框架的大小,则 viewWillAppear: 是您唯一的机会。请记住,由于旋转事件或状态栏高度变化等各种原因,此大小可能在视图出现后发生更改。因此,重要的是为每个子视图提供适当的自动调整掩码,以确保布局可以根据边界的任何变化进行适当的调整。

如果希望手动构建视图层次结构,则建议在 loadView 方法中进行。由于您在该方法中自己构造视图,因此可以将其框架初始化为任何您想要的大小。您选择的大小并不重要,因为 UIKit 可能会对其进行更改。只需确保设置了适当的自动调整掩码即可。


我对你的第三个答案有一个问题。我已经尝试在nib文件中设置视图,但在viewDidLoad中,我的视图框架不是我在Nib中设置的。我在这里发布了我的问题http://stackoverflow.com/q/7266177/147564 - sarunw
14
奇怪,直到第8步的viewDidAppear:,我的视图才有了正确的框架。 - Mr Rogers
14
iOS 5.0及以上版本可以在viewDidLayoutSubviews中获取视图的frame信息。 - Jon
@MrRogers 我也是。视图大小/位置在viewDidAppear中不能保证。 - DanM
3
使用自动布局时,应该使用viewDidLayoutSubviews而不是viewWillAppear。此外,这篇文章可能会有所帮助:http://kevindew.me/post/18579273258/where-to-progmatically-lay-out-views-in-ios-5-and - Leszek Szary
我也遇到了同样的问题:在viewWillAppear中无法使用框架,但在viewDidAppear中可以。但是这对于初始布局来说太晚了。那么,viewDidLayoutSubviews是应用最终框架和基于内容微调的规范最佳位置吗? - Benjohn

-7
你的类中将会有一个方法。
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    // Return YES for supported orientations    return YES; }

将你的框架放入这个方法中...然后它就会正常工作。
例如:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation 
{
        // Return YES for supported orientations    
        if(interfaceOrientation == UIDeviceOrientationPortrait || UIDeviceOrientationPortraitUpsideDown)
                self.view.frame = CGRectMake(0, 0, FRAME.WIDTHinPOTRAIT, FRAME.HEIGHTinPOTRAIT);
        else if(interfaceOrientation == UIDeviceOrientationLandscapeRight || UIDeviceOrientationLandscapeLeft)
                self.view.frame = CGRectMake(0, 0, FRAME.WIDTHinLANDSCAPE, FRAME.HEIGHTinLANDSCAPE);

        return YES; 
}

FRAME.WIDTH > 所需宽度 FRAME.HEIGHT > 所需高度


2
shouldAutorotate 中调整 self.view 的大小正是 UIKit 文档所不建议的。 - cbowns

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接