Swift viewWillTransition not called

Mike Walker picture Mike Walker · Feb 11, 2017 · Viewed 14.4k times · Source

I'm creating a full screen image gallery using a UICollectionView. When the user rotates the device, I perform updates to the UICollectionView within

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

I present this UIViewController modally and have a UICollectionView taking up the full screen. Within viewDidLoad, I create the flow layout as:

let flowLayout = UICollectionViewFlowLayout()
flowLayout.scrollDirection = .horizontal
flowLayout.minimumInteritemSpacing = 0
flowLayout.minimumLineSpacing = 0
photosCollectionView.isPagingEnabled = true
photosCollectionView.setCollectionViewLayout(flowLayout, animated: true)

I also have the size as:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    return photosCollectionView.frame.size
}

When I rotate my device, viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) is never called, which causes the UICollectionViewLayout to not update. While I rotate the device, I do get the message:

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.

I've read online that I can add:

self.automaticallyAdjustsScrollViewInsets = false

to the UIViewController, but that had no affect. There are no content or section insets with the UICollectionView.

I also have the super.viewWillTransition called within the function as well. Can anyone assist me on what could be causing this issue?

Answer

zombie picture zombie · Feb 11, 2017

If you are just concern about the layout when the device rotate then please use:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

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

From apple docs:

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

This method is called when the view controller's view's size is changed by its parent (i.e. for the root view controller when its window rotates or is resized). If you override this method, you should either call super to propagate the change to children or manually forward the change to children.

I guess you might called this function on a parent of that view without calling super

A work around would also be to register for the device rotation:

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)
}

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