自动布局中的 viewDidLayoutSubviews 与 UIPageViewController 上的旋转

Posted

技术标签:

【中文标题】自动布局中的 viewDidLayoutSubviews 与 UIPageViewController 上的旋转【英文标题】:viewDidLayoutSubviews in Autolayout with rotation on UIPageViewController 【发布时间】:2014-03-17 12:47:46 【问题描述】:

我有一个UIPageViewController,其视图控制器包含一个UIScrollView,其中包含一个UIImageView。在加载的视图控制器viewWillAppear 中,如果视图旋转,则边界不正确。这是我试图根据this根据图像大小动态定位图像视图的地方。我尝试使用(void)viewDidLayoutSubviews,但我需要调用[self.view layoutSubviews],否则我会收到断言错误。当我添加它时,我无法再缩放图像视图。您可以看到它尝试缩放,但被调整回最小缩放。

我对自动布局完全陌生,并试图看看我是否遗漏了什么。

我当前的解决方法是检查框架的边界以查看方向是否与边界匹配。如果是这样,那么我按原样使用边界。如果没有,那么我在计算中切换高度和宽度。

我想知道如何正确使用viewDidLayoutSubviews,而不会让我无法放大图像视图。

从两个答案来看,viewDidLayoutSubviews 可能是布局视图的错误位置。我试图找到应该在哪里完成,并让视图知道它们的边界和方向,但在它们在屏幕上可见之前。根据我的阅读,我认为那是 viewDidLayoutSubviews,但似乎使用它可能会导致无限循环。

【问题讨论】:

【参考方案1】:

从两个答案看来,viewDidLayoutSubviews 可能是 布局视图的位置错误。我正在尝试找到应该在哪里 完成并让视图知道它们的界限和方向,但是 在它们出现在屏幕上之前。

我将继承UIScrollView 并覆盖layoutSubviews,系统只会在需要并且所有变量(方向、大小等)都具有正确值时才会为您调用。

【讨论】:

我想这是有道理的。似乎很奇怪,在 UIViewController 中没有调用方法,其中边界和旋转信息是已知的,这可能发生在哪里。如果是这种情况,那么子类化视图的理由似乎比自动布局之前要多得多。 对于控制器来说,唯一知道所有尺寸的地方是viewWillAppear。然而,它不会得到更新,此时调整子视图可能会导致等待新视图出现的用户感觉到一些滞后。【参考方案2】:

首先,您不应该自己打电话给-layoutSubviews。来自UIViewdocumentation for that method:

你不应该直接调用这个方法。如果你想强制一个 布局更新,调用 setNeedsLayout 方法而不是事先这样做 到下一次图纸更新。如果你想更新你的布局 立即查看,调用 layoutIfNeeded 方法。

即使没有警告,从-viewDidLayoutSubviews 调用该方法似乎也是一个问题的良方——调用-viewDidLayoutSubviews 是为了通知您的视图控制器它的视图只是将它的视图布置为子视图,因此调用[self.view layoutSubviews] 可能很容易导致循环。

在加载的视图控制器 viewWillAppear 中,如果视图旋转,则边界不正确。

请记住,当调用-viewWillAppear 时,视图hasn't yet been added to a view hierarchy。鉴于此,您当前的方法似乎还不错。该视图尚未设置其边界(因为尚未添加),因此您可以确定边界是什么并使用它来设置您的视图。另一种可能更好的方法是使用-viewDidAppear,因为此时您的视图将被添加并且到那时应该具有正确的边界。

添加后,我无法再缩放图像视图。你可以 看到它尝试缩放,但被调整回最小缩放。

听起来您在定位图像视图时没有考虑缩放状态。确保-viewDidLayoutSubviews 中的代码不会否定滚动视图正在执行的工作。

您根本不需要更改图像视图的位置——这正是自动布局擅长的事情。我注意到您用作示例的 ImageScrollViewController.m 文件不会更改图像视图的位置。

【讨论】:

为什么 viewWillAppear 不起作用是有道理的。在 ImageScrollViewController viewWillAppear 中调用了将图像缩放居中的方法。在我的例子中,视图的高度和宽度被切换了,因为视图实际上是旋转的。所以,由于我不能使用 viewWillAppear,我不确定使用什么方法。我不想使用 viewDidAppear,因为我不希望它错误地显示在屏幕上,然后跳转到应该如何定位和缩放。正如你提到的,我的 viewDidLayoutSubviews 似乎正在重置我的缩放。【参考方案3】:

我不完全确定您的视图层次结构,但是如果您有一个包含 UIScrollView 的视图控制器,这个答案解释了如何做到这一点。 (所述视图控制器稍后会显示在 UIPageViewController 中,但此处无关紧要)。

在控制滚动视图的视图控制器中覆盖以下方法:

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration

    [super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];

    CGRect bounds = self.view.bounds;
    self.scrollView.frame = bounds;

    //
    // Calculate a rect for your image view according to bounds
    //
    self.imageView.frame = calculatedFrame;

问题是您的视图将在滚动/缩放期间进行布局,如果您将视图定位在控制滚动视图的视图控制器的 viewDidLayoutSubviews 中,则每次用户滚动或缩放时都会定位它。

我怀疑您的视图控制器可能直接控制 UIScrollView(而不是控制具有 UIScrollView 子视图的视图),在这种情况下,只需删除 self.scrollView.frame = self.bounds 行。

你不应该从 viewDidLayoutSubviews 调用 [self.view layoutSubviews]。事实上,您根本不应该调用该函数。如果需要更新布局,请调用 [self.view setNeedsLayout]。但是当您的视图执行了它的 layoutSubviews 时,会自动调用 viewDidLayoutSubviews。这意味着如果您从那里调用该函数,您将得到一个无限循环。

【讨论】:

我应该澄清一下轮换。由于 UIPageViewController 在新的视图控制器中加载,如果我旋转我的设备然后滑动以显示一个新的视图控制器,那就是旋转的边界不正确。从阅读中我明白为什么会这样,但我不确定什么时候,我应该为我的滚动视图设置最小缩放。我想这样做,以便在不滚动的情况下显示整个图像。这就是为什么我想知道正确的界限,这样我就可以计算给定屏幕尺寸的缩放比例来给我的滚动视图。滚动视图是视图的子视图。

以上是关于自动布局中的 viewDidLayoutSubviews 与 UIPageViewController 上的旋转的主要内容,如果未能解决你的问题,请参考以下文章

Xcode 6 - 另一个自动布局视图中的自动布局视图

自动更新自动布局中的框架[关闭]

Interface Builder 中的自动布局根据外部视图的宽度/高度更改布局

自动布局中的比例距离约束

抑制 Xcode 中的运行时自动布局警告

自动布局中的相对中心