UICollectionView Compositional Layout - items size not updated properly on iPad rotation

I have a problem that I cannot resolve at the moment namely I'm trying to set a different number of columns in the collection view based on the iPad position (collection should update on device rotation)

Currently, (depending on how the device is positioned on start) on the first rotation items width is not calculated properly:

iPad screen

It'd be great if someone could help and point out where is an issue. Code for this project is on my GitHub: https://github.com/ceboolion/compositionalLayout

Thanks for your help.


Solution 1:

I gave your UICollectionView a background of orange and ran your code and got similar results:

UICollectionView Rotation UICollectionViewCompositionalLayout

I noticed few things

  1. From this I could conclude the UICollectionView seems to be set up and working well on rotation, probably due to auto layout so I feel the issue is more to do with the layout code.
  2. I also noticed when you start scrolling, the View fixes itself to what you want
  3. Finally, I added one line of code to your func relayoutCollectionView(with size: CGSize) function to see what the dimensions are when you want update the layout calculations and columns
func relayoutCollectionView(with size: CGSize) {
    collectionView.collectionViewLayout.shouldInvalidateLayout(forBoundsChange: CGRect(x: 0,
                                                                                       y: 0,
                                                                                       width: size.width,
                                                                                       height: size.height))
    collectionView.setCollectionViewLayout(createFlowLayout(),
                                           animated: true,
                                           completion: nil)
    
    collectionView.collectionViewLayout.invalidateLayout()
    
    // I added this line
    print("Collection View Width: \(collectionView.frame.size.width), Collection View Height: \(collectionView.frame.size.height)")
}

When I turned from portrait to landscape, the output printed was Collection View Width: 810.0, Collection View Height: 1080.0 which does not seem to be right.

So from this I assume that it is too early for invalidateLayout to be called to get the right calculations.

When I do something like this from a UIViewController, I will make invalidateLayout() call from viewWillLayoutSubviews()

In your case, what I tried and worked was adding the similar override func layoutSubviews() in your class CollectionView: UIView

    override func layoutSubviews() {
        super.layoutSubviews()
        collectionView.collectionViewLayout.invalidateLayout()
    }

I removed relayoutCollectionView from your func relayoutCollectionView

Now it seems to work for the first time as well although I can still not explain why your current works every other time except for the first time.