Swift的viewWillTransition方法未被调用

10
我正在使用UICollectionView创建全屏图库。当用户旋转设备时,我会在func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator)中对UICollectionView进行更新。
我以模态方式呈现此UIViewController,并使用UICollectionView占据整个屏幕。在viewDidLoad中,我创建流式布局如下:
let flowLayout = UICollectionViewFlowLayout()
flowLayout.scrollDirection = .horizontal
flowLayout.minimumInteritemSpacing = 0
flowLayout.minimumLineSpacing = 0
photosCollectionView.isPagingEnabled = true
photosCollectionView.setCollectionViewLayout(flowLayout, animated: true)

我也可以提供尺寸信息,它如下所示:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    return photosCollectionView.frame.size
}

当我旋转我的设备时,从未调用viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator),这会导致UICollectionViewLayout不更新。当我旋转设备时,确实收到消息:
The behavior of the UICollectionViewFlowLayout is not defined because: the item height must be less than the height of the UICollectionView minus the section insets top and bottom values, minus the content insets top and bottom values.

我在网上看到可以添加:

self.automaticallyAdjustsScrollViewInsets = false

针对UIViewController进行了调整,但没有任何影响。在UICollectionView中没有内容或部分插入。

我也在函数中调用了super.viewWillTransition。有谁能帮助我解决这个问题?


错误信息提到了“项目高度”。我没有看到你确定它的代码。你能展示一下吗? - matt
当我设置大小时,这是高度和宽度。我将“sizeForItem”设置为 “IndexPath” = “photosCollectionView.frame.size”。 - Mike Walker
好的,但如果你在错误的时间说这句话,我可以想象到你可能会得到一个尺寸,其中一个或两个维度比集合视图最终变得更大。 - matt
它是使用UICollectionViewDelegate函数collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize设置的。 - Mike Walker
6个回答

21

如果您只关心设备旋转时的布局,请使用:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    collectionView.collectionViewLayout.invalidateLayout()
    collectionView.layoutIfNeeded()
}

来自苹果文档:

public func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator)

当视图控制器的视图大小由其父级更改时调用此方法(即对于根视图控制器,当其窗口旋转或调整大小时)。 如果您覆盖此方法,则应调用 super 来将更改传播到子级别,或者手动将更改转发到子级别。

我猜你可能在视图的父级别上调用了这个函数,但没有调用 super。

另一种解决方法也可以是注册设备旋转:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    NotificationCenter.default.addObserver(self, selector: #selector(deviceOrientationDidChange), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    NotificationCenter.default.removeObserver(self)
}

@objc func deviceOrientationDidChange(_ notification: Notification) {
    let orientation = UIDevice.current.orientation
    print(orientation)
}

3
这是一种可能性,但这并不能解释为什么 viewWillTransition 函数没有被调用。让我想知道是否有设置错误。 - Mike Walker
2
我已经仔细检查了所有的super函数是否被调用。我还测试了iPad,但不知何故,在iPad上viewWillTransition可以正常调用,而在iPhone上却不能。 - Mike Walker
谢谢。我会接受这个权宜之计,但我也会继续研究为什么函数没有被调用。 - Mike Walker
UIDeviceOrientationDidChange 通知不仅在旋转时发布,而且如果设备从垂直变为水平也会发布。 - shoe
@MikeWalker viewWillTransition 没有被调用,可能是因为您的视图控制器作为子视图添加了 viewController,而且 viewWillTransition 也在父视图控制器中被调用。 - Prabhat
显示剩余2条评论

13

我想转发一个回答,该回答是我在另一个线程中留下的,该线程描述了相同的问题。


已经有人解释过,你必须调用 super。我想添加一些信息,这可能有助于遇到和我一样问题的人。

情况:父级 -> 子级(child)(子视图控制器中未调用viewWillTransition)


如果您的视图控制器是子视图控制器,则检查父视图控制器委托是否被调用,并且是否在那里调用了super。否则它不会传播到子视图控制器!

class ParentViewController: UIViewController {

    func presentChild() {
        let child = ChildViewController()
        present(child, animated: false, compeltion: nil)
    }

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator) // If this line is missing your child will not get the delegate call in it's viewWillTransition

        // Do something
    }
}

class ChildViewController: UIViewController {

    // This method will not get called if presented from parent view controller and super is not called inside the viewViewWillTransition available there.
    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
       super.viewWillTransition(to: size, with: coordinator)

       //Do something
    }
}

顺便说一下 - 这件事发生在我身上是因为父类的代码是由别人编写的,他们忘记调用super方法。



非常有用 --> super.viewWillTransition - Prashant Tukadiya

4

我替换了

func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator)

by

func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator)

并且它对我有效。


这对我也有效。谢谢。 - Lasantha Basnayake

1
这是我修复它的方法:
前往项目目标 - 通用 - 部署信息
勾选应用程序支持的设备方向
同时,您需要(将UIInterfaceOrientationMask.all更改为适当的内容):
override var supportedInterfaceOrientations : UIInterfaceOrientationMask {
    get {
        return UIInterfaceOrientationMask.all;
    }
}

针对视图控制器。


1
如果您的ViewController是一个子ViewController,并且您已将其视图添加到父ViewController的视图中,请执行以下操作:
在添加了子视图控制器的父ViewController中:
self.view.addSubview(childViewController.view)

还要添加

self.addChild(childViewController)

0
也许你需要在 Info.plist 中设置 Supported interface orientations:
<key>UISupportedInterfaceOrientations</key>
<array>
    <string>UIInterfaceOrientationLandscapeLeft</string>
    <string>UIInterfaceOrientationLandscapeRight</string>
    <string>UIInterfaceOrientationPortrait</string>
    <string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>

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