ios 获取 UIViewController 视图的正确边界

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11522672/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 19:16:57  来源:igfitidea点击:

Getting the correct bounds of UIViewController's view

iosxcodeuiviewcontrollerrotationbounds

提问by demon9733

I have an iPad-application. In landscape orientation the UIViewController's view actual width = 1024pxand height = 768 - 20 (statusBar) - 44 (navigationBar) = 704px.

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

So I wanna get this [1024 x 704]size and I'm using self.view.boundsfor it. It returns [748 x 1024], which is wrong! But when I rotate the screen twice (current -> portrait -> current), the view's bounds are correct - [1024 x 704].

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

The view was initialized like this:

视图是这样初始化的:

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

And bounds were get like this:

边界是这样的:

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

So the question is.. How can I get the correct view's bound in the very beginning?

所以问题是.. 我怎样才能在一开始就获得正确的视图边界?

采纳答案by Benjohn

How to do this correctly

如何正确执行此操作

Your UIViewControllersubclass should override the method viewWillLayoutSubviews, see also here.

您的UIViewController子类应该覆盖该方法viewWillLayoutSubviews另请参见此处

When this method is called, the viewController's viewhas its correct size and you can make any necessary adjustments to subviews prior to the layout pass over the subviews.

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

Swift

迅速

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

Obj-C

对象-C

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

Documentation

文档

When a view's bounds change, the view adjusts the position of its subviews. Your view controller can override this method to make changes before the view lays out its subviews. The default implementation of this method does nothing.

当一个视图边界改变,视图调整其子视图的位置。您的视图控制器可以重写此方法来更改视图勾画出它的子视图之前。此方法的默认实现不执行任何操作。

As you see from the emphasised part, this method is called every timethe view controller's view changes size, as well as when the view first appears. This lets you respond correctly to rotation and other bounds change events

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

Several ways that don't work

几种行不通的方法

A few approaches suggested in other answers don't work well, or have serious drawbacks. Unfortunately this includes the selected answer. I urge you to avoid these approaches. I will go through them and discuss the reasons that you should not use them. Please don't.

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

  • viewDidLoadand viewWillAppear– during these calls viewdoes not yet have its final size. The size you get will only ever be correct by pure chance, so as to mislead you.
  • viewDidAppear– this is too late. Your view is already on screen and visible to the user. Making changes here will cause visible changes / abrupt glitches and will look amateurish. Once again, please – for your sake, for my sake, for everyone'ssake: don't do it! You're better than that and so are your users.
  • UIScreen.mainScreen.bounds.size– this is extremelylow level. You're implementing a UIViewControllerand the size of its view depends on the controllers it is nested in (navigation, tab, paging, any custom controllers, etc), how the device is rotated, and potentially, how the screen has been split up for multitasking. So, while you mightbe able to compensate for all these and calculate the final size of your view, you'll end up with complex and brittle code that can easily break if Apple decide to change any of these metrics. UIViewControllerwill do all this for you if you just override viewWillLayoutSubviews.
  • viewDidLoad并且viewWillAppear——在这些调用view中还没有最终的大小。您获得的尺寸永远是正确的,纯属偶然,从而误导您。
  • viewDidAppear——这已经太晚了。您的视图已经在屏幕上并且对用户可见。在此处进行更改将导致可见的更改/突然的故障,并且看起来很业余。再一次,请——为了你,为了我,为了大家:不要这样做!你比这更好,你的用户也是如此。
  • UIScreen.mainScreen.bounds.size-这是非常低的水平。您正在实现 aUIViewController并且其视图的大小取决于它嵌套的控制器(导航、选项卡、分页、任何自定义控制器等)、设备的旋转方式以及可能的屏幕拆分方式多任务处理。因此,虽然您可能能够补偿所有这些并计算视图的最终大小,但如果 Apple 决定更改这些指标中的任何一个,您最终会得到复杂而脆弱的代码,这些代码很容易被破坏。UIViewController如果您只是覆盖viewWillLayoutSubviews.

Other than not providing correct information, these problematic approaches will not help you with auto-rotation or other events that cause the view controller's view to change size, such as multitasking gestures. This is something you really want to handle smoothly.

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

So please: be a champ. Do it the right way. Use viewWillLayoutSubviews. Your implementation will be called for every size change, and your users, future self, team members and I will celebrate you for it. Bravo!

所以请:成为冠军。以正确的方式去做。使用viewWillLayoutSubviews. 每次规模变化都会调用您的实现,您的用户、未来的自己、团队成员和我将为此庆祝。太棒了!

Further tips

更多提示

WhenviewWillLayoutSubviewsis called, the only viewin your hierarchy that will be resized to its final size is viewController.view. The give away for this is in the name of the method. It's telling you view…(your view controller's root view) …WillLayout…(really soon now, but it's not happened yet) …Subviews(everything else in its hierarchy under the root view).

viewWillLayoutSubviews被调用时,只查看您的层次结构,将被调整到它的最终尺寸viewController.view。这样做的好处在于方法的名称。它告诉你view…(你的视图控制器的根view…WillLayout…(真的快了,但它没有发生尚未…Subviews(一切在层次结构中的根视图下)。

So subview layout has not happened yet. Everychild under the root does notyet have a valid finalsize. Any size information you query from the child views will be at bestcompletely wrong.

所以子视图布局还没有发生。根下的每个孩子还没有有效的最终大小。您从子视图查询的任何尺寸信息充其量都是完全错误的。

More likely, and enormously worse, it will be misleadingly correct.

更有可能,更糟糕的是,它会误导性地正确

It happensto be what you expect and need due to a default at this size and orientation, or due to your storyboard settings. But this is only by chance and isn't something you can rely on with different device sizes or orientations.

由于此大小和方向的默认设置,或者由于您的故事板设置,它恰好是您期望和需要的。但这只是偶然的,您不能依赖于不同的设备尺寸或方向。

If you need to be told when a particular subview changes size, and know its exact final size, you should generally override the layoutSubviewsmethod of that particular UIViewsubclass.

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

回答by RichS

As per some of the other answers, the issue you are seeing is because viewDidLoadis called beforethe rotation happens. Because the iPad always initializes in portrait mode, if you get the size values in viewDidLoad, they will always be the portrait sizes - this is irrespective of any orientations you've configured.

根据其他一些答案,您看到的问题是因为在轮换发生之前viewDidLoad被调用。因为 iPad 总是在纵向模式下初始化,如果您在 中获取尺寸值,它们将始终是纵向尺寸 - 这与您配置的任何方向无关。 viewDidLoad

To get the size afterthe orientation/rotation happens, get the size values in viewDidAppear.

发生方向/旋转获取大小,请获取viewDidAppear.



I don't particularly understand why iOS doesn't handle this better - especially given that you define the orientations in the project settings, and, in Xcode Interface Builder. But, I'm sure there is a good reason ;-).

我不太明白为什么 iOS 不能更好地处理这个问题 - 特别是考虑到您在项目设置和 Xcode Interface Builder 中定义了方向。但是,我相信有一个很好的理由;-)。

回答by luksfarris

I've always used:

我一直使用:

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

to get the size of the screen, and:

获取屏幕的大小,以及:

CGSize sizeOfView = self.view.bounds.size;

to get the view's size. I have just tested it on viewDidLoad and it returned:

获取view的大小。我刚刚在 viewDidLoad 上对其进行了测试,它返回:

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

Which is correct since the CGSize is defined as {Width, Height}.

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

回答by Dustin Kendall

I ran into this issue and found that getting the bounds in viewDidAppearworked for my needs.

我遇到了这个问题,发现设置界限viewDidAppear可以满足我的需求。

回答by Selkie

This is what I found in my last project: the frame of self.view will be adjusted after viewDidLoad according to if this screen has navigation bar etc.

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

So maybe you want to use that value after viewDidLoad (maybe in viewWillAppear or viewDidAppear) or adjust it manually by substract the height of bars.

因此,也许您想在 viewDidLoad(可能在 viewWillAppear 或 viewDidAppear 中)之后使用该值,或者通过减去条的高度来手动调整它。

回答by Joga singh

If you want to get correct bounds of view in ViewDidLoadmethod, you can use Dispatch async with delay for it. See this example below:

如果您想在ViewDidLoad方法中获得正确的视图边界,您可以使用 Dispatch async with delay 来实现。请参阅下面的示例:

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