10

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?

Mike Walker
  • 2,944
  • 8
  • 30
  • 62
  • The error message talks about "the item height". I don't see your code that determines that. Could you show it? – matt Feb 10 '17 at 23:34
  • When I set the size, that's the height and the width. I set the `sizeForItem` at `IndexPath` = `photosCollectionView.frame.size`. – Mike Walker Feb 10 '17 at 23:36
  • Okay, but if you say that at the wrong time, I can imagine that you might get a size where one or both dimensions is bigger than the collection view eventually becomes. – matt Feb 11 '17 at 00:07
  • It's set using the `UICollectionViewDelegate` function `collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize`. – Mike Walker Feb 11 '17 at 00:11

6 Answers6

21

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

@objc func deviceOrientationDidChange(_ notification: Notification) {
    let orientation = UIDevice.current.orientation
    print(orientation)
}
David Villegas
  • 458
  • 3
  • 18
zombie
  • 5,069
  • 3
  • 25
  • 54
  • 3
    That is a possibility, but it doesn't explain why the `viewWillTransition` function isn't called. Makes me wonder if I have a setting wrong. – Mike Walker Feb 10 '17 at 23:41
  • as it says in the docs it should be called unless you called it on a parent without calling super so instead of going to your code i would suggest the way above – zombie Feb 10 '17 at 23:47
  • 2
    I have double checked all of the `super` functions have been called. I have also tested with iPads, and for some reason the `viewWillTransition` is called with no problems on the iPad, just not iPhone. – Mike Walker Feb 11 '17 at 00:08
  • @MikeWalker I still don't know why it's not called but I tried to provide a work around solution – zombie Feb 11 '17 at 00:38
  • Thanks. I'll accept the work around solution, but I'll also keep looking into why the function isn't called. – Mike Walker Feb 11 '17 at 02:20
  • `UIDeviceOrientationDidChange` notifications are not just posted for rotations, they are also posted if device goes from being vertical to horizontal – shoe Feb 15 '17 at 01:15
  • @MikeWalker `viewWillTransition` not called, may be because your view controller add as child `viewController` and `viewWillTransition` is also called in parent `viewController` – Prabhat May 23 '22 at 06:52
13

I'd like to repost an answer which i left in another thread which describes the same issue.


People have already explained that you have to call super. I'd like to add a piece of information that might help people who would have faced what i faced.

Scenario: Parent -> Child (viewWillTransition not called in child)


If your view controller is a child view controller, then check if the parent view controller delegate is called and if super is called there. Otherwise it won't be propagated to the child view controller!

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

P.S - This happened to me because the code for the parent was written by someone else and they forgot to call super.

Rakesha Shastri
  • 11,053
  • 3
  • 37
  • 50
4

I replaced

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

by

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

and it works for me.

David
  • 81
  • 2
  • 13
1

This is how I fixed that:

Go to Project Target - General - Deployment Info

Tick Device Orientations the app supports

Also you need (change UIInterfaceOrientationMask.all for whatever is appropriate):

override var supportedInterfaceOrientations : UIInterfaceOrientationMask {
    get {
        return UIInterfaceOrientationMask.all;
    }
}

for ViewController.

cyanide
  • 3,885
  • 3
  • 25
  • 33
1

If your ViewController is a child ViewController and you have added it’s view to a parent ViewController’s view, do the following:

In parent ViewController where you added

self.view.addSubview(childViewController.view)

Also add

self.addChild(childViewController)
Pranav Kasetti
  • 8,770
  • 2
  • 50
  • 71
Nix
  • 11
  • 2
0

Maybe you need to set Supported interface orientations in Info.plist:

<key>UISupportedInterfaceOrientations</key>
<array>
    <string>UIInterfaceOrientationLandscapeLeft</string>
    <string>UIInterfaceOrientationLandscapeRight</string>
    <string>UIInterfaceOrientationPortrait</string>
    <string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
Levan Karanadze
  • 739
  • 1
  • 11
  • 21