Rather than re-adding the fromVC.view as @mohpor suggests in the transitionWasCancelled case, it’s more correct to change the line:
containerView.addSubview(toVC.view)
in the animateTransition: method of FlipDismissAnimationController to:
containerView.insertSubview(toVC.view,atIndex:0)
The reason for this is that the toVC.view should be visually behind the fromVC.view and its snapshot, and when you use addSubview, it puts the toVC.view on top of the fromVC.view, regardless of its visibility, and when you cancel the transition, the snapshot is not “on top” of its associated view, the fromVC.view, so the transition can get confused. Also, the toVC.view needs to be removed if the transition was cancelled.
When doing this tutorial’s flipping animation, you won’t see the confused views show up (at least I didn’t in the environments I was using), but if you change the animations to just a simple grow/shrink/alpha-channel-change, and then test the left-edge-drag dismissal by dragging slightly in and then all the way back out to the left (much easier in the simulator), you see the issue.
In the case of the similar code in FlipPresentAnimationController, it’s fine since the toVC.view needs to be on top, and addSubview ensures that as well as that its snapshot is on top of it.
Essentially, FlipDismissAnimationController.swift could be written this way, along with the animation changes that demonstrate the issue if not changed (see the #if true sections and their associated comments).
//
// FlipDismissAnimationController.swift
// GuessThePet
//
// Created by Vesza Jozsef on 08/07/15.
// Copyright (c) 2015 Razeware LLC. All rights reserved.
//
import UIKit
class FlipDismissAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
var destinationFrame = CGRectZero
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return 0.6
}
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
guard let fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey),
let containerView = transitionContext.containerView(),
let toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey) else {
return
}
#if true // Non-flipping animation
let finalFrame = CGRectInset (destinationFrame,destinationFrame.size.width / 2.0,destinationFrame.size.height / 2.0)
#else // Flipping animation
let finalFrame = destinationFrame
#endif // Non-flipping or Flipping animation
let snapshot = fromVC.view.snapshotViewAfterScreenUpdates(false)
snapshot.layer.cornerRadius = 25
snapshot.layer.masksToBounds = true
#if true // Non-flipping animation
snapshot.alpha = 1.0
#endif // Non-flipping or Flipping animation
#if true // Better way to ensure correct order of subviews and the snapshot
// We need the toVC.view behind the fromVC.view and its snapshot, or
// things get confused if a transition cancel happens.
containerView.insertSubview(toVC.view,atIndex:0)
#else // Original way that caused the subviews to be out of order
containerView.addSubview(toVC.view)
#endif // Better way to ensure correct order of subviews and the snapshot
containerView.addSubview(snapshot)
fromVC.view.hidden = true
#if true // Non-flipping animation
#else // Flipping animation
AnimationHelper.perspectiveTransformForContainerView(containerView)
toVC.view.layer.transform = AnimationHelper.yRotation(-M_PI_2)
#endif // Non-flipping or Flipping animation
let duration = transitionDuration(transitionContext)
UIView.animateKeyframesWithDuration(
duration,
delay: 0,
options: .CalculationModeCubic,
animations: {
#if true // Non-flipping animation
UIView.addKeyframeWithRelativeStartTime(2/3, relativeDuration: 1/3, animations: {
snapshot.alpha = 0.0
})
UIView.addKeyframeWithRelativeStartTime(0.0, relativeDuration: 1.0, animations: {
snapshot.frame = finalFrame
})
#else // Flipping animation
UIView.addKeyframeWithRelativeStartTime(0.0, relativeDuration: 1/3, animations: {
snapshot.frame = finalFrame
})
UIView.addKeyframeWithRelativeStartTime(1/3, relativeDuration: 1/3, animations: {
snapshot.layer.transform = AnimationHelper.yRotation(M_PI_2)
})
UIView.addKeyframeWithRelativeStartTime(2/3, relativeDuration: 1/3, animations: {
toVC.view.layer.transform = AnimationHelper.yRotation(0.0)
})
#endif // Non-flipping or Flipping animation
},
completion: { _ in
fromVC.view.hidden = false
snapshot.removeFromSuperview()
#if true // Need to remove the toVC.view if cancelled
if transitionContext.transitionWasCancelled()
{
toVC.view.removeFromSuperview()
}
#endif // Need to remove the toVC.view if cancelled
transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
})
}
}