skip to Main Content

I have a UICollectionView in my view controller defined as follows:

let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .vertical
layout.minimumLineSpacing = Constants.minimumLineSpacing
layout.minimumInteritemSpacing = Constants.minimumInteritemSpacing

let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.backgroundColor = Constants.backgroundColor
collectionView.contentInset = Constants.collectionViewContentInsets

collectionView.refreshControl = refreshControl

I set the cell size in sizeForItemAt:

func collectionView(
    _ collectionView: UICollectionView,
    layout collectionViewLayout: UICollectionViewLayout,
    sizeForItemAt indexPath: IndexPath
) -> CGSize {
    let collectionViewHorizontalInset = Constants.collectionViewInsets.left + Constants.collectionViewInsets.right

    let cellWidth = isPortrait
        ? (screenSize.width - collectionViewHorizontalInset - Constants.minimumInteritemSpacing) / 2.0
        : (screenSize.width - collectionViewHorizontalInset - Constants.minimumInteritemSpacing * 2.0) / 3.0

    return CGSize(width: cellWidth, height: Constants.cellHeight)
}

with sceenSize as follows:

private var screenSize: CGSize {
    view.bounds.size
}

The property isPortrait keeps track of the orientation, in viewDidLoad:

isPortrait = view.bounds.size.width < view.bounds.size.height

and in viewWillTransition I have the following:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)

    isPortrait = size.width < size.height
    collectionView.collectionViewLayout.invalidateLayout()
}

When I rotate the device it seems that it is getting the incorrect screen size and therefore calculating an incorrect width for the cells. e.g If I load the app in landscape there will be 3 cell per row which is correct. However if I then rotate to portrait and back to landscape there will be 4 cells per row which is incorrect.

What am I doing wrong here? How can I get the correct screen size after the rotation?

2

Answers


  1. Chosen as BEST ANSWER

    Answering my own question with what worked for me:

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)
    
        coordinator.animate { _ in
            self.collectionView.collectionViewLayout.invalidateLayout()
        }
    }
    

    Seems that the key was to use coordinator.animate, that way the screen size would be correct when sizeForItemAt does its calculation.


  2. This helped me lot a earlier to solve your issue. Details are added in the comments.

    class CollectionViewFlowLayout: UICollectionViewFlowLayout{
        
        /// The default implementation of this method returns false.
        /// Subclasses can override it and return an appropriate value
        /// based on whether changes in the bounds of the collection
        /// view require changes to the layout of cells and supplementary views.
        /// If the bounds of the collection view change and this method returns true,
        /// the collection view invalidates the layout by calling the invalidateLayout(with:) method.
        override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
            
            return (self.collectionView?.bounds ?? newBounds) != newBounds
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search