Search code examples
iosswiftanimationuinavigationcontrolleruiviewanimationtransition

How to create a navigation transition like the Apple news app?


I found this article to create a navigation transition like the Apple news app: https://blog.rocketinsights.com/how-to-create-a-navigation-transition-like-the-apple-news-app/.

The transition is a zoom effect.

The code works great for push animation, but for pop animation (to close DetailViewController), I have a black screen instead my main viewcontroller.

As the article doesn't provide the full source code to download, I publish it on github, apply to UICollectionViewController for my needs: testZoomTransition

enter image description here


Solution

  • I found a solution, by simplifying the source code, maybe less elegant, but effective.

    Git is updated.

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        let presenting = operation == .push
    
        // Determine which is the master view and which is the detail view that we're navigating to and from. The container view will house the views for transition animation.
        let containerView = transitionContext.containerView
    
        guard let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from),
            let toView = transitionContext.view(forKey: UITransitionContextViewKey.to) else {
                transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
                return
        }
    
        containerView.backgroundColor = fromView.backgroundColor
    
        let mainView = presenting ? fromView : toView
        let detailView = presenting ? toView : fromView
    
        // Determine the starting frame of the detail view for the animation. When we're presenting, the detail view will grow out of the thumbnail frame. When we're dismissing, the detail view will shrink back into that same thumbnail frame.
        let finalFrame = presenting ? detailView.frame : thumbnailFrame
    
        //scale factor between thumbnailFrame and size of
        let scaleFactorX = thumbnailFrame.size.width / detailView.frame.size.width
        let scaleFactorY = thumbnailFrame.size.height / detailView.frame.size.height
    
        if presenting {
    
            // Shrink the detail view for the initial frame. The detail view will be scaled to CGAffineTransformIdentity below.
            detailView.transform = CGAffineTransform(scaleX: scaleFactorX, y: scaleFactorY)
            detailView.center = CGPoint(x: thumbnailFrame.midX, y: thumbnailFrame.midY)
            detailView.clipsToBounds = true
        }
    
    
        // Set the initial state of the alpha for the master and detail views so that we can fade them in and out during the animation.
        detailView.alpha = presenting ? 0 : 1
        mainView.alpha = presenting ? 1 : 0
    
        // Add the view that we're transitioning to to the container view that houses the animation.
        containerView.addSubview(toView)
        containerView.bringSubview(toFront: detailView)
    
    
        // Animate the transition.
        UIView.animate(withDuration: duration, delay: 0.0, usingSpringWithDamping: 1, initialSpringVelocity: 1.0, options: .curveEaseInOut, animations: {
            // Fade the master and detail views in and out.
            detailView.alpha = presenting ? 1 : 0
            mainView.alpha = presenting ? 0 : 1
    
            if presenting {
                detailView.transform = CGAffineTransform.identity
                detailView.center = CGPoint(x: finalFrame.midX, y: finalFrame.midY)
            }
            else {
                detailView.transform = CGAffineTransform(scaleX: scaleFactorX, y: scaleFactorY)
                detailView.center = CGPoint(x: self.thumbnailFrame.midX, y: self.thumbnailFrame.midY)
            }
    
    
        }) { finished in
            transitionContext.completeTransition(finished)
        }
    }