preferredTransition not working when using setViewControllers(_:animated:)

I’m using the new preferredTransition = .zoom(...) API introduced in iOS 18.

Here’s a simplified version of what I do on app startup:

let listVC = CollectionViewController(collectionViewLayout: layout)
let detailVC = DetailViewController()
detailVC.preferredTransition = .zoom(sourceViewProvider: { context in
    let indexPath = IndexPath(row: 0, section: 0)
    let cell = listVC.collectionView.cellForItem(at: indexPath)
    return cell
})
let nav = UINavigationController()
nav.setViewControllers([listVC, detailVC], animated: false)
window?.rootViewController = nav
window?.makeKeyAndVisible()

This is meant to restore the UI state from a previous session — the app should launch directly into the DetailViewController.

The Problem

When I launch the app with setViewControllers([listVC, detailVC], animated: false), the transition from listVC to detailVC appears correctly (i.e., no animation, as intended), but the drag-to-dismiss gesture does not work. The back button appears, and tapping it correctly triggers the zoom-out transition back to the cell, so the preferredTransition = .zoom(...) itself is properly configured.

Interestingly, if I delay the push with a DispatchQueue.main.async and instead do:

nav.setViewControllers([listVC], animated: false)
DispatchQueue.main.async {
    nav.pushViewController(detailVC, animated: true)
}

…then everything works perfectly — including the interactive swipe-to-dismiss gesture — but that introduces an unwanted visual artifact: the user briefly sees the listVC, and then it pushes to detailVC, which I’m trying to avoid.

My Question

Is there a way to enable the swipe-to-dismiss gesture when using setViewControllers([listVC, detailVC], animated: false)

It can be very confusing for users if swipe-to-dismiss only works in certain cases inconsistently.

Thanks

@chou_ios are you trying to implement a Split View with a sidebar and detail view?

@DTS Engineer

I'm not building split view, nor am I using UISplitViewController — my view hierarchy is quite simple.

I have a standard UICollectionView, and tapping a cell is supposed to show a detail view. This is conceptually similar to the Apple sample code, where a detail view controller is pushed using navigationController.pushViewController.

However, due to the existing product behavior, I need to present the detail screen using: navigationController.setViewControllers([listViewController, detailViewController])

The issue is: I would still like to implement the fluid zoom transition effect as demonstrated in the Apple documentation, but it seems the transition isn't applied when using setViewControllers API

sample project for reproducing the issue I mentioned.

Thank you for the post. I just downloaded your code

Upon reviewing your code, I observe the following:

  • The thumbnails images are set up correctly.
  • The view is animated with animation set to true, allowing the iOS device to determine the most suitable animation.
override func collectionView(
        _ collectionView: UICollectionView,
        didSelectItemAt indexPath: IndexPath
    ) {
        guard let cell = collectionView.cellForItem(at: indexPath) else { return }

        let dvc = DetailViewController(imageurl: thumbnailUrl[indexPath.item])
        dvc.setZoomProvider {
            return cell.contentView
        }
        navigationController?.pushViewController(dvc, animated: true)
    }

Are you anticipating a new animation or an animation that is not currently happen?

If you'd like us to consider adding the necessary functionality, please file an enhancement request using Feedback Assistant. Once you file the request, please post the FB number here.

If you're not familiar with how to file enhancement requests, take a look at Bug Reporting: How and Why?

Albert Pascual
  Worldwide Developer Relations.

Thanks for your response.

When using navigationController?.pushViewController(dvc, animated: true), everything works fine.

My Issue:

When I call nav.setViewControllers([listVC, detailVC], animated: false) in the SceneDelegate of my sample project, the swipe-to-dismiss gesture no longer works.

Question:

Is there a way to enable the swipe-to-dismiss gesture when using setViewControllers([listVC, detailVC], animated: true) API?

Thank you for this information. The animation should always function when set to true. If it does not work, I suggest submitting a bug report to the team.

Don't forget to include a sample but with the setViewControllers([listVC, detailVC], animated: true) and remove everything else. Once you open the bug report, please post the FB number here for my reference.

If you have any questions about filing a bug report, take a look at Bug Reporting: How and Why?

I'll follow up with the team after I get your FB number.

Albert Pascual
  Worldwide Developer Relations.

preferredTransition not working when using setViewControllers(_:animated:)
 
 
Q