Search code examples
iosswiftxcodeuiviewcontrolleruianimation

UIViewControllerAnimatedTransitioning shows a black screen when animating to and from view


I'm trying to create a easeInEaseOut animation for my view controllers, however, when I try to animate both the 'to' and 'from' views, the 'from' view turns black until the animation is finished. If I only animate the 'to' view, then the new view controller animates correctly but over the top of the 'from' view. I would like both of these views to move together, to give the impression that the first is sliding off to make way for the second (I guess being pushed off by the second is more accurate). Can anyone tell me why this would be? I've tried to playing around with numerous settings, see code below:

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        guard let toVC = transitionContext.viewController(forKey: .to), let fromVC = transitionContext.viewController(forKey: .from) else {
            dLog("No to or from VC")
            transitionContext.completeTransition(false)
            return
        }

        switch animationType {
        case .present:
            dLog("Present VC")
            transitionContext.containerView.addSubview(fromVC.view)
            transitionContext.containerView.addSubview(toVC.view)
            presentAnimation(with: transitionContext, fromView: fromVC.view, toView: toVC.view)
        case .dismiss:
            dLog("Dismiss VC")
        }
    }

    func presentAnimation(with transitionContext: UIViewControllerContextTransitioning, fromView: UIView, toView: UIView) {
        fromView.clipsToBounds = true
        toView.clipsToBounds = true
        fromView.transform = CGAffineTransform(translationX: 0, y: 0)
        toView.transform = CGAffineTransform(translationX: toView.frame.size.width, y: 0)

        let duration = transitionDuration(using: transitionContext)

        UIView.animate(withDuration: duration, delay: 0.0, usingSpringWithDamping: 0.0, initialSpringVelocity: 0.0, options: .curveEaseInOut, animations: {
            toView.transform = CGAffineTransform(translationX: 0.0,  y: 0.0)
            fromView.transform = CGAffineTransform(translationX: -fromView.frame.size.width, y: 0)
        }) { _ in
            transitionContext.completeTransition(true)
        }
    }

Solution

  • If anyone else is struggling with this, I figured it out. You have to use animateKeyFrames instead:

    UIView.animateKeyframes(withDuration: duration, delay: 0.0, options: .calculationModeLinear, animations: {
                UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 2.0) {
                    toView.transform = CGAffineTransform(translationX: 0, y: 0)
                }
                UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 2.0) {
                    fromView.transform = CGAffineTransform(translationX: -fromView.frame.size.width, y: 0)
                }
            }) { _ in
                transitionContext.completeTransition(true)
            }