获取UIViewController视图的正确边界

时间:2012-07-17 12:42:42

标签: ios xcode uiviewcontroller rotation bounds

我有一个iPad应用程序。在横向方向,UIViewController的视图实际宽度= 1024px,高度= 768 - 20(statusBar) - 44(navigationBar)= 704px

所以我希望得到这个[1024 x 704]大小,而我正在使用self.view.bounds。它返回[748 x 1024],这是错误的!但是当我将屏幕旋转两次(当前 - >纵向 - >当前)时,视图的边界是正确的 - [1024 x 704]

视图初始化如下:

- (void)loadView {
    self.view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
    self.view.backgroundColor = [UIColor lightGrayColor];
}

界限是这样的:

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"bounds = %@", NSStringFromCGRect(self.view.bounds));
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
    NSLog(@"bounds = %@", NSStringFromCGRect(self.view.bounds));
}

所以问题是......我怎样才能在一开始就得到正确的观点?

6 个答案:

答案 0 :(得分:31)

根据其他一些答案,您看到的问题是因为viewDidLoad在旋转发生之前被称为 。由于iPad始终以纵向模式初始化,因此如果您获得viewDidLoad中的尺寸值,它们将始终为纵向尺寸 - 这与您配置的任何方向无关。

要在方向/旋转发生后获得大小,请在viewDidAppear中获取尺寸值。


我并不特别理解为什么iOS不能更好地处理这个问题 - 特别是考虑到你在项目设置中定义了方向,并且在Xcode Interface Builder中。但是,我确信有充分的理由; - )。

答案 1 :(得分:24)

如何正确执行此操作

您的UIViewController子类应覆盖方法viewWillLayoutSubviewssee also here

调用此方法时,viewController的view大小正确,您可以在布局传递子视图之前对子视图进行必要的调整。

夫特

override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()
    NSLog("bounds = \(self.view.bounds)")
}

的OBJ-C

- (void)viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];
    NSLog(@"bounds = %@", NSStringFromCGRect(self.view.bounds));
}

文档

  

当视图边界发生变化时,视图会调整其子视图的位置。您的视图控制器可以覆盖此方法以在视图布局其子视图之前进行更改。此方法的默认实现不执行任何操作。

从强调部分看,每次视图控制器的视图改变大小时,以及视图首次出现时,此方法都称为。这使您可以正确响应旋转和其他边界更改事件

几种不起作用的方法

其他答案中提出的一些方法效果不佳,或者存在严重缺陷。不幸的是,这包括选定的答案。我敦促你避免这些方法。我将通过它们讨论你不应该使用它们的原因。请不要。

  • viewDidLoadviewWillAppear - 在这些调用view期间尚未达到最终大小。 你获得的大小只有通过纯粹的机会才能正确,以便误导你。
  • viewDidAppear - 这太迟了。 您的视图已在屏幕上并且对用户可见。在这里进行更改将导致明显的变化/突然的故障,并将看起来业余。再一次,请 - 为了你的缘故,为了我自己,为了每个人的缘故:不要这样做!你比那更好,你的用户也是如此。
  • UIScreen.mainScreen.bounds.size - 这是低级别。您正在实现UIViewController,其视图的大小取决于它嵌入的控制器(导航,选项卡,分页,任何自定义控制器等),设备如何旋转,以及可能的屏幕方式已被拆分为多任务处理。因此,虽然可能能够补偿所有这些并计算视图的最终大小,但如果Apple决定更改其中任何一个,您最终会遇到复杂而脆弱的代码。指标。如果您只是覆盖UIViewController
  • viewWillLayoutSubviews会为您完成所有这些操作

除了不提供正确的信息之外,这些有问题的方法无法帮助您进行自动旋转或导致视图控制器视图更改大小的其他事件,例如多任务处理手势。这是你真正想要顺利处理的事情。

所以请:成为冠军。以正确的方式做到这一点。使用viewWillLayoutSubviews。每次更改尺寸都会调用您的实施,您的用户,未来的自我,团队成员和我将为您庆祝。喝彩!

进一步提示

调用 viewWillLayoutSubviews时,层次结构中唯一的视图将调整为最终大小为viewController.view。为此提供的是该方法的名称。它告诉你view…(你的视图控制器的根view …WillLayout… (现在很快就会发生,但它还没有发生){ {1}}(根视图下层次结构中的其他所有内容)。

所以子视图布局还没有发生。 下的每个子项尚未具有有效的最终大小。您从子视图查询的任何大小信息都将最好完全错误。

更可能,且非常糟糕误导性正确

发生是您所期望和需要的,因为此大小和方向的默认设置,或由于您的故事板设置。但这只是偶然的,并不是你可以依赖于不同设备尺寸或方向的东西。

如果需要告知特定子视图何时更改大小并知道其确切的最终大小,通常应该覆盖该特定…Subviews子类的layoutSubviews方法。

答案 2 :(得分:13)

我一直用:

CGSize sizeOfScreen = [[UIScreen mainScreen] bounds].size;

获取屏幕大小,并且:

CGSize sizeOfView = self.view.bounds.size;

获取view的大小。我刚刚在viewDidLoad上测试了它并返回了:

2012-07-17 10:25:46.562 Project[3904:15203] bounds = {768, 1004}

这是正确的,因为CGSize被定义为{Width,Height}。

答案 3 :(得分:2)

这是我在上一个项目中发现的:self.view的框架将在viewDidLoad之后根据此屏幕是否有导航条等进行调整。

所以也许你想在viewDidLoad之后使用那个值(可能在viewWillAppear或viewDidAppear中),或者通过减去柱的高度来手动调整它。

答案 4 :(得分:2)

我遇到了这个问题,发现在viewDidAppear中获取界限符合我的需要。

答案 5 :(得分:0)

如果您想通过ViewDidLoad方法获得正确的视界,则可以使用延迟的Dispatch异步。请参见下面的示例:

DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                // Get your bounds here
            }