Let's say I have a UICollectionView with a UICollectionViewFlowLayout, and my items are different sizes. So I've implemented collectionView(_:layout:sizeForItemAt:)
.
Now let's say I permit the user to rearrange items (collectionView(_:canMoveItemAt:)
).
Here's the problem. As a cell is being dragged and other cells are moving out of its way, collectionView(_:layout:sizeForItemAt:)
is called repeatedly. But it's evidently called for the wrong index paths: a cell is sized with the index path for the place it has been visually moved to. Therefore it adopts the wrong size during the drag as it shuttles into a different position.
Once the drag is over and collectionView(_:moveItemAt:to:)
is called, and I update the data model and reload the data, all the cells assume their correct size. The problem occurs only during the drag.
We clearly are not being given enough information in collectionView(_:layout:sizeForItemAt:)
to know what answer to return while the drag is going on. Or maybe I should say, we're being asked for the size for the wrong index path.
My question is: what on earth are people doing about this?
The trick is to implement
During a drag, that method is called repeatedly, but there comes a moment where a cell crosses another and cells are shoved out of the way to compensate. At that moment,
orig
andprop
have different values. So at that moment you need to revise all your sizes in accordance with how the cells have moved.To do that, you need to simulate in your rearrangement of sizes what the interface is doing as the cells move around. The runtime gives you no help with this!
Here's a simple example. Presume that the user can move a cell only within the same section. And presume that our data model looks like this, with each Item remembering its own size once
collectionView(_:layout:sizeForItemAt:)
has initially calculated it:Here's how
sizeForItemAt:
memoizes the calculated sizes into the model:Then as we hear that the user has dragged in a way that makes the cells shift, we read in all the
size
values for this section, perform the same remove-and-insert that the interface has done, and put the rearrangedsize
values back into the model:The result is that
collectionView(_:layout:sizeForItemAt:)
now gives the right result during the drag.The extra piece of the puzzle is that when the drag starts you need to save off all the original sizes, and when the drag ends you need to restore them all, so that when the drag ends the result will be correct as well.