Tom van Zummeren
Tom van Zummeren

Reputation: 9220

Custom UIViewController transition bug in iOS 9

I encountered a strange bug. I am just using iOS's custom transitioning method for UIViewControllers using UIViewControllerTransitioningDelegate together with an implementation of UIViewControllerAnimatedTransitioning. It all seems to work fine, until I do exactly the following:

  1. open the app
  2. present another view controller with my custom transition
  3. rotate to landscape
  4. dismiss the just presented view controller

That's all! What happens now is the following: I see a large black bar on the right side of the initial view controller (as if that controller's view wasn't rotated to landscape).

The funny thing is this only goes wrong in iOS 9, in iOS 8 everything seems to work just fine. Did anything change with custom transition API I don't know of? Or is this simply a really nasty iOS 9 bug? If anyone can tell me what I did wrong or if anyone can provide me with a workaround I would really appreciate that!

These classes reproduce the problem:

import UIKit
class ViewController: UIViewController, UIViewControllerTransitioningDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: "tap")
        view.addGestureRecognizer(tapGestureRecognizer)
    }

    func tap() {
        let controller = ModalViewController()
        controller.transitioningDelegate = self
        presentViewController(controller, animated: true, completion: nil)
    }

    func animationControllerForPresentedController(presented: UIViewController,
                                                   presentingController presenting: UIViewController,
                                                   sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return Transitioning()
    }

    func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return Transitioning()
    }
}

The presented view controller:

import UIKit
class ModalViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = UIColor.redColor()

        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: "tap")
        view.addGestureRecognizer(tapGestureRecognizer)
    }

    func tap() {
        dismissViewControllerAnimated(true, completion: nil)
    }
}

And finally the UIViewControllerAnimatedTransitioning implementation:

import UIKit
class Transitioning: NSObject, UIViewControllerAnimatedTransitioning {

    func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
        return 0.5
    }

    func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
        let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey)
        let toView = transitionContext.viewForKey(UITransitionContextToViewKey)
        let containerView = transitionContext.containerView()
        if let fromView = fromView, toView = toView {
            containerView?.addSubview(fromView)
            containerView?.addSubview(toView)

            toView.alpha = 0
            UIView.animateWithDuration(0.5, animations: {
                toView.alpha = 1
            }, completion: {
                finished in
                transitionContext.completeTransition(true)
            })
        }
    }
}

Upvotes: 0

Views: 514

Answers (1)

Rob
Rob

Reputation: 438477

I generally use the following in animateTransition:

toView.frame = fromView.frame

FYI, you don't have to add fromView to the hierarchy, as it's already there.

Upvotes: 1

Related Questions