Home > Mobile >  UICollectionView with dynamic sizing cells resets Content Offset on reloadData on iOS 13/14
UICollectionView with dynamic sizing cells resets Content Offset on reloadData on iOS 13/14

Time:11-04

We successfully created UICollectionView with dynamic sizing cells.
The initial layout is perfect and the cells look good with no warnings. But when we use reloadData after selection occurs the UICollectionView contentOffset resets to zero.

I have created a demo project showing the issue: enter image description here

CodePudding user response:

It looks like you're doing a whole lotta stuff that doesn't need to be (and shouldn't be) done.

Couple examples...

1 - You're overriding intrinsicContentSize and contentSize, and calling layout funcs at the same time. But... you have set a size for your collection view via constraints. So, intrinsicContentSize has no effect, but the funcs you're calling in contentSize may be causing issues.

2 - You're setting flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize and returning .init(width: 1, height: 1) for sizeForItemAt ... you should use a reasonable estimated size, and not implement sizeForItemAt.

3 - There is no need to reloadData() to update the appearance of the selected cell... Collection Views track the selected cell(s) for us. It's easier to override var isSelected: Bool in the cell class. Unless...

4 - You are repeatedly calling reloadData(). When you reloadData(), you are telling the collection view to de-select any selected cells.

I put up a modification to your repo here: https://github.com/DonMag/CollectionViewSelfSizingIssue


Edit - After comments...

OK - the intrinsicContentSize implementation is there to allow the collection view to be centered when we have only a few items. That appears to work fine.

The reason the collection is getting "shifted back to the start" on reloadData() is due to this:

public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    return .init(width: 1, height: 1)
}

I'm not an Apple engineer, but as I understand it (and this can be confirmed with some debug print() statements):

  • on reloadData(), the collection view calls sizeForItemAt for every item in the data set
  • the collection view then starts laying out the cells

if we do this:

items = (0..<150).map { "\($0)" }

sizeForItemAt will be called 150 times before the first call to cellForItemAt.

So, if we have 10 items, and we're returning a width of 1 for every cell, the collection view thinks:

10 * 1 == 10
 9 * 5 == 45  // 9 5-point inter-item spaces
------------
Total collection view width will be 55-points

And, naturally, it shifts back to starting with cell 0, because all 10 cells are going to fit.

I've updated my repo to allow both collection views to "center themselves" when there are only a few items.

  • Related