Proper way to collectionview.reloadData() when using custom navigationcontroller transition

Here's my situation. I am trying to replicate the iOS Photos App zooming transition from its collectionView (in ViewController A) to the detail view showing the full pictures in a pageviewcontroller (in ViewControler B). All is good except when I start to delete photos in ViewController B. What I want is the collectionView in ViewController A to reloadData() to update its cells because of the deletion before the ViewController B to ViewController A animation transition starts. I used the following method from UIViewControllerAnimatedTransitioning as follows below:


func animateTransition(using transitionContext: UIViewControllerContextTransitioning)
{

  let duration = transitionDuration(using: transitionContext)
  //  fromViewController=ViewController B, toViewController=ViewController A
  let fromViewController = transitionContext.viewController(forKey: .from)!
  let toViewController = transitionContext.viewController(forKey: .to)!
  let containerView = transitionContext.containerView
  
  //trying to get the snapshot of the image cell after collectionView.reloadData()
  let collectionViewToReload = toViewController.collectionView
  collectionViewToReload.reloadData()
 
  let cellOfInterest = collectionView?.cellForItem(at: indexPathOfInterest) as! PhotoCell
  let imageView = cell.photoImageView

...

}


So, I am trying to get the snapshot of the final imageView layout after the transition animation completes. The thing is, when 'collectionViewToReload.reloadData()' is executed, somehow the 'cellOfInterest' returns nil. As such, is that another way to approach this problem?

Answered by Claude31 in 335210022

Thanks to KMT to have edited your post, I would not have read it otherwise ! Please pay attention to such "details" in the future.


What is the dataStore of the collection view ?

Do you delete item in it when you delete in the collection view ?

Then, even no need for reload.

Here is a typical function for deleting (first item in that case); reload is commented out, as not needed:


    @IBAction func deleteIt(_ sender: UIButton) {
        items.remove(at: 0)
        let theIndex = IndexPath(row: 0, section: 0)
        collectionView.deleteItems(at: [theIndex])
        // collectionView.reloadData()
    }

May be I misunderstand what you do.


Can't you reload collectionView when you delete photo in (B), not in anumation ?


Did you test the value of indexPathOfInterest ?

It is suprising it is nil as you used as!

What do you want to use cellOfInterest for ?

Thank you for your reply. Sorry if I am not clear.


I have tested indexPathOfInterest and it is okay.


I want the photo currently displayed on (B) to zoom back to the cellOfInterest's frame containing the same photo in (A) during the navigation transition.


cellOfInterest gave it as nil but crashed since I'm using as! The warning came up in the logs. I don't want cellOfInterest to be nil.


To your first question, I did try reloading (A)'s collectionView in (B) first, even without deletion or any kind of changes to the collectionView. Then when I go back to (A), it gives the same nil error catch. Could it be because collectionView.reloadData() only fully reloads cells when they are visible? I even tried placing collectionView.reloadData() in (A)'s viewWillAppear(), and it gave the same error.


Hope this clarifies my question. Feel free to ask more if you're still unclear.

In

let cellOfInterest = collectionView?.cellForItem(at: indexPathOfInterest) as! PhotoCell

Did you test collectionView as well to confirm it is not nil ?


But in fact, doc for cellForItem(at:) states:


The cell object at the corresponding index path or

nil
if the cell is not visible or
indexPath
is out of range.


So, it will only work if visible. But is it really a problm ? If not visible, why a zoom effect ?

Hmm, I have yet to test whether collectionView whether it becomes nil. Will test and let you know.


I prefer to have a zoom effect like that in Apple's Photos app for great user experience instead of the default navigationcontroller transition animation, and it will be a problem when deletions are done in (B). So for example, initially, I have 5 photo cells in (A)'s collectionView. Then I tap on one of the cells, say cell 1, to reveal the full picture of cell 1 in (B). (B) will also have the ability to swipe through the 5 photos that are present in (A). When I delete the cell 1 photo in (B), it should show cell 2's photo, and (A) should now have 4 photo cells, ranging from cell 2-5. So, when I press the 'Back' button in the navigation bar, I want cell 2's photo to zoom back to the updated collectionView with four cells left, and NOT to the original 5 cell collectionView. As such, it seems that collectionView.reloadData() maybe clears the collectionView cells, and even before (A) becomes visible, the transition animation happens first. Thus 'The cell object is

nil
if the cell is not visible'
as you mentioned at the beginning of the transition animation? If that is really the case, is there a work around to update the 'non-visible' collectionView completely before the zoom back transition animation occurs?

In an effort to make that comment readable...

-=-


I prefer to have a zoom effect like that in Apple's Photos app for great user experience instead of the default navigationcontroller transition animation, and it will be a problem when deletions are done in (B).


So for example, initially, I have 5 photo cells in (A)'s collectionView. Then I tap on one of the cells, say cell 1, to reveal the full picture of cell 1 in (B). (B) will also have the ability to swipe through the 5 photos that are present in (A). When I delete the cell 1 photo in (B), it should show cell 2's photo, and (A) should now have 4 photo cells, ranging from cell 2-5.


So, when I press the 'Back' button in the navigation bar, I want cell 2's photo to zoom back to the updated collectionView with four cells left, and NOT to the original 5 cell collectionView.


As such, it seems that collectionView.reloadData() maybe clears the collectionView cells, and even before (A) becomes visible, the transition animation happens first.


Thus 'The cell object is

nil
if the cell is not visible' as you mentioned at the beginning of the transition animation?


If that is really the case, is there a work around to update the 'non-visible' collectionView completely before the zoom back transition animation occurs

Accepted Answer

Thanks to KMT to have edited your post, I would not have read it otherwise ! Please pay attention to such "details" in the future.


What is the dataStore of the collection view ?

Do you delete item in it when you delete in the collection view ?

Then, even no need for reload.

Here is a typical function for deleting (first item in that case); reload is commented out, as not needed:


    @IBAction func deleteIt(_ sender: UIButton) {
        items.remove(at: 0)
        let theIndex = IndexPath(row: 0, section: 0)
        collectionView.deleteItems(at: [theIndex])
        // collectionView.reloadData()
    }

So sorry for the cramped post. Thanks KMT for the clear edit! Will take note of it in future.


I tried your way and it worked!


I did try collectionView.deleteItems before, and it did not work that time because I did not update items array which the collectionView uses as its dataStore. Careless mistake of mine. Thanks for pointing that out.

Proper way to collectionview.reloadData() when using custom navigationcontroller transition
 
 
Q