UiCollectionView jump on next cell when previous cell removed

I have custom layout with fullscreen cells. When removing cell from the left (it's not visible at the time), UICollectionView jumps to the next cell. It's like current cell was at index 4 and when cell on the left removed the next cell has index 4 now and immediately scroll to the next cell. Describing in 3 steps (A is cell that need to be fullscreen, x will be removed, o other cells, large letter is fullscreen):

  1. ooooAoo
  2. oooxAoo
  3. oooaOo

But must keep this oooAoo

Answered by mr.bodich in 409384022

Here is my solution if it can help to anybody, did not found how to achieve desired offset natively:



//some model manipulating code, removing desired items here...

let currentList = currentCell?.parentList
reloadData()
if let list = currentList, let index = self.lists.firstIndex(of: list) {
     self.scrollToItem(at: IndexPath(row: index, section: 0), at: .centeredHorizontally, animated: false)
}


  • currentCell is computed property, returns optional middle cell. Detect which cell is the largest, because of custom flowlayout logic.
  • parentList is the model item, I can compare cell by it to make life easier. I check which list was attached to the cell before reloadData().

This is not very clear.


When removing cell from the left (it's not visible at the time)

How do you remove this cell x, if A is full screen ? You cannot select x to remove it ?


So, what are the actions ?

oooxAwo

A is full screen

What do you do to go to step 2 ?

2. oooxAwo

A is still full screen. Did you remove something ?

what is x, compared to o ?

3. oooaWo

You remove x

So, you jump to next cell, o (I named it ww to be clear)

And you want to see A as full screen.


Is that correct ?


If so, why don't you compute the position of A at step 2 (in didSelectCell for instance)

Then on step 3, change the selected cell programmatically (in this case by adding 1 to selectedCell) ?

Let's say, 'x' is removing in background. Each item has willRemove property and once needed, when A goes fullscreen actually, cleanRemovedCells method triggered.

So updating your suggestion:

oooxAwo

A is full screen

What do you do to go to step 2 ? — method triggered that is removing x. Either using reloadData or deleteItems(at:)

2. oooxAwo

A is still full screen. Did you remove something ?

what is x, compared to o ? — x is the same cell, it's just cell that is in process of removing.

3. oooaWo

You remove x — on this step x cell removed

So, you jump to next cell, o (I named it ww to be clear) — collectionview is moving all cells on the right from removed x to fill empty space left after deleting x. ooo ... <-Awo oooaWo. It's keeps contentOffset so another cell is on screen now.

And you want to see A as full screen. — Yes, I want keep A in place, want UICollectionView to move left side cells to the right to fill empty space that is left after deleting x.

You control the removing of x, right ?


- before removing, get the position (

selectionIndexPaths
and its indexPath) of A

- just after removing, and after reload, compute the new indexPath of A (you may compute of search through the cells to find it)

- then set the

selectionIndexPaths to the right value


Does that make sense for you ?



Note

var selectionIndexPaths: Set<IndexPath> { get set }

Discussion

This property reflects the index paths of the currently selected items, where each index path contains a

section
number and an index number for the
item
in that section. This property is updated automatically when the user selects items interactively. You can also change the selection programmatically by assigning a new value to this property. To animate changes to the selection, call this method on the collection view’s
animator()
proxy object instead.

It is a programmer error to specify an index path that does not refer to a valid item in the data source. If you specify an invalid index path, this method raises an exception.

This property is key-value observable. Other methods that modify the selection automatically update this property.

Thank you, that's what I did yesterday to fix an issue))


Not exactly as you said because I've disabled selection, but the meaning is the same. I remember linked to the cell data item and then getting it's index and call scrollToItem(at: at: animated:). Works fine only when called after reloadData.

I just was afraid it will cause noticeable delays when cell will contain heavier content. Like you will notice content of one cell for few milliseconds and after that you will see the correct one. But for now looks fine and smooth.


Thanks for your help, I just was hoping it can be solved is some more natural and safe way...

That's a safe way to do it if you check that the indexes always fall inside valid bounds.


Thanks for the feedback and don't forget to close the thread on the correct answer.

Sure, it's absolutely safe regarding the model and collectionview errors.

I mean it looks not as safe regarding possible animation imperfections. Hope that imperfections will never go out)))

Accepted Answer

Here is my solution if it can help to anybody, did not found how to achieve desired offset natively:



//some model manipulating code, removing desired items here...

let currentList = currentCell?.parentList
reloadData()
if let list = currentList, let index = self.lists.firstIndex(of: list) {
     self.scrollToItem(at: IndexPath(row: index, section: 0), at: .centeredHorizontally, animated: false)
}


  • currentCell is computed property, returns optional middle cell. Detect which cell is the largest, because of custom flowlayout logic.
  • parentList is the model item, I can compare cell by it to make life easier. I check which list was attached to the cell before reloadData().

What is sorting doing ? What if all cells have the same frame width ?

Then, you will just get the first visible (and that is probably what's happening).


Not sure this code is very robust (test by setting a frame for cell A that is neither min nor max).

I think that keeping track of "A" indexPath in didSelectItem and recomputing the new indexPath of A after deletion, would be more robust.

If frames are the same size, that means cell is not fullscreen and detecting the biggest one will affect only scrolling in carousel. When cell is fullsize, it's only one on screen and centered and that's when I am removing other cells, so mistake is impossible here.

Anyway, if we are between two cells, what's the difference where to scroll (anyway that will not happen because I made custom flow layout for scrolling, it's always stop on centered cell)


Ok. I've removed currentCell implementation as that's what only I need. Everyone can detect it as he want. So no confusing anymore)))

UiCollectionView jump on next cell when previous cell removed
 
 
Q