13

I have created a transition and it is working fine except that I get sometime black corners in the simulator. Additionally in iPad Pro I get a completely black screen if I run the simulator in full resolution. The resized resolutions work fine. Do you have an idea what might be the problem? Another thing that I recognized is that the content behind the black screen is there and responds to touches. E.g. on a touch I reload the cell of a collectionview. Then this cell is visible while the rest of the collectionview is black.

class ZoomInCircleViewTransition: NSObject, UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegate {

    var transitionContext: UIViewControllerContextTransitioning?

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

    func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
        self.transitionContext = transitionContext

        guard let toViewController: UIViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey) else {
            return
        }

        guard let fromViewController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey) else {            return
        }

        guard let fromViewTransitionFromView = fromViewController as? TransitionFromViewProtocol else {
            return
        }



        let imageViewSnapshot = fromViewTransitionFromView.getViewForTransition()

        let endFrame = CGRectMake(-CGRectGetWidth(toViewController.view.frame)/2, -CGRectGetHeight(toViewController.view.frame)/2, CGRectGetWidth(toViewController.view.frame)*2, CGRectGetHeight(toViewController.view.frame)*2)

        if let containerView = transitionContext.containerView(){
            containerView.addSubview(fromViewController.view)
            containerView.addSubview(toViewController.view)
            containerView.addSubview(imageViewSnapshot)
        }


        let maskPath = UIBezierPath(ovalInRect: imageViewSnapshot.frame)
        let maskLayer = CAShapeLayer()
        maskLayer.frame = toViewController.view.frame
        maskLayer.path = maskPath.CGPath
        toViewController.view.layer.mask = maskLayer

        let quadraticEndFrame = CGRect(x: endFrame.origin.x - (endFrame.height - endFrame.width)/2, y: endFrame.origin.y, width: endFrame.height, height: endFrame.height)
        let bigCirclePath = UIBezierPath(ovalInRect: quadraticEndFrame)

        let pathAnimation = CABasicAnimation(keyPath: "path")
        pathAnimation.delegate = self
        pathAnimation.fromValue = maskPath.CGPath
        pathAnimation.toValue = bigCirclePath
        pathAnimation.duration = transitionDuration(transitionContext)
        maskLayer.path = bigCirclePath.CGPath
        maskLayer.addAnimation(pathAnimation, forKey: "pathAnimation")


        let hideImageViewAnimation =  {
            imageViewSnapshot.alpha = 0.0
        }
        UIView.animateWithDuration(0.2, delay: 0.0, options: UIViewAnimationOptions.CurveLinear, animations: hideImageViewAnimation) { (completed) -> Void in
        }

        let scaleImageViewAnimation = {
            imageViewSnapshot.frame = quadraticEndFrame
        }
        UIView.animateWithDuration(transitionDuration(transitionContext), delay: 0.0, options: UIViewAnimationOptions.CurveLinear, animations: scaleImageViewAnimation) { (completed) -> Void in
            // After the complete animations hav endet
            imageViewSnapshot.removeFromSuperview()
        }
    }

    override func animationDidStop(anim: CAAnimation, finished flag: Bool) {
        if let transitionContext = self.transitionContext {
            transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
        }
    }

    // MARK: UIViewControllerTransitioningDelegate protocol methods

    // return the animataor when presenting a viewcontroller
    func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return self
    }

    // return the animator used when dismissing from a viewcontroller
    func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return self
    }
}

enter image description here

netshark1000
  • 7,245
  • 9
  • 59
  • 116

2 Answers2

8

You need remove mask layer once your are done with custom transition animations.

toViewController.view.layer.mask = nil

Please use this updated code:

class ZoomInCircleViewTransition: NSObject, UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegate {

var transitionContext: UIViewControllerContextTransitioning?

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

func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
    self.transitionContext = transitionContext

    guard let toViewController: UIViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey) else {
        return
    }

    guard let fromViewController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey) else {            return
    }

    guard let fromViewTransitionFromView = fromViewController as? TransitionFromViewProtocol else {
        return
    }

    let imageViewSnapshot = fromViewTransitionFromView.getViewForTransition()

    let endFrame = CGRectMake(-CGRectGetWidth(toViewController.view.frame)/2, -CGRectGetHeight(toViewController.view.frame)/2, CGRectGetWidth(toViewController.view.frame)*2, CGRectGetHeight(toViewController.view.frame)*2)

    if let containerView = transitionContext.containerView(){
        containerView.addSubview(fromViewController.view)
        containerView.addSubview(toViewController.view)
        containerView.addSubview(imageViewSnapshot)
    }


    let maskPath = UIBezierPath(ovalInRect: imageViewSnapshot.frame)
    let maskLayer = CAShapeLayer()
    maskLayer.frame = toViewController.view.frame
    maskLayer.path = maskPath.CGPath
    toViewController.view.layer.mask = maskLayer

    let quadraticEndFrame = CGRect(x: endFrame.origin.x - (endFrame.height - endFrame.width)/2, y: endFrame.origin.y, width: endFrame.height, height: endFrame.height)
    let bigCirclePath = UIBezierPath(ovalInRect: quadraticEndFrame)

    let pathAnimation = CABasicAnimation(keyPath: "path")
    pathAnimation.delegate = self
    pathAnimation.fromValue = maskPath.CGPath
    pathAnimation.toValue = bigCirclePath
    pathAnimation.duration = transitionDuration(transitionContext)
    maskLayer.path = bigCirclePath.CGPath
    maskLayer.addAnimation(pathAnimation, forKey: "pathAnimation")


    let hideImageViewAnimation =  {
        imageViewSnapshot.alpha = 0.0
    }

    UIView.animateWithDuration(0.2, delay: 0.0, options: UIViewAnimationOptions.CurveLinear, animations: hideImageViewAnimation) { (completed) -> Void in
    }

    let scaleImageViewAnimation = {
        imageViewSnapshot.frame = quadraticEndFrame
    }
    UIView.animateWithDuration(transitionDuration(transitionContext), delay: 0.0, options: UIViewAnimationOptions.CurveLinear, animations: scaleImageViewAnimation) { (completed) -> Void in
        // After the complete animations hav endet
        imageViewSnapshot.removeFromSuperview()
        toViewController.view.layer.mask = nil
    }
}

override func animationDidStop(anim: CAAnimation, finished flag: Bool) {
    if let transitionContext = self.transitionContext {
        transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
    }
}

// MARK: UIViewControllerTransitioningDelegate protocol methods

// return the animataor when presenting a viewcontroller
func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    return self
}

// return the animator used when dismissing from a viewcontroller
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    return self
}

}

Aruna Mudnoor
  • 4,795
  • 14
  • 16
  • Could you also have a look at the second problem? http://stackoverflow.com/questions/34993774/uiviewcontrolleranimatedtransitioning-black-screen-fragments-after-rotation-cha – netshark1000 Jan 25 '16 at 13:27
  • This causes other problems with a disapearing navigationbar: https://stackoverflow.com/questions/35826739/searchbar-behind-navigationbar Perhaps you can have a look? – netshark1000 Mar 06 '16 at 15:43
0

In my case, I had set the presentation style and delegate in the incorrect place. When placed in viewDidLoad(), the transitioningDelegate is honored, but the modalPresentationStyle is not — resulting in a black screen. I had to move the style to be set earlier on init.

class MyViewController: UIViewController {
  init() {
    super.init(nibName: String(describing: type(of: self)), bundle: .main)
    transitioningDelegate = self
    modalPresentationStyle = .custom
  }

  @available(*, unavailable)
  required init?(coder aDecoder: NSCoder) {
    fatalError()
  }

  override func viewDidLoad() {
    super.viewDidLoad()
    // I incorrectly had the delegate and presentationStyle set here.
  }
}

extension MyViewController: UIViewControllerTransitioningDelegate {
  //...
}

Calling class:

let vc = MyViewController()
present(vc, animated: true)
Jon
  • 74
  • 1
  • 9