UIViewControllerAnimatedTransitioning: Black scree

2020-07-23 06:36发布

问题:

I have created a Viewcontrollertransition and everything is working as long as I don't change the device orientation.

Image 1 shows the screen as it should be. I then switch to the next viewcontroller where I change the orientation. Now I go back to first viewcontroller and again switch the orientation. Then I get the result visible in image 2. A black border appears. Please don't mind the white box in the center of the screen.

Below you find the code of my animation. Can you see what is wrong?

import Foundation

import UIKit

class ZoomOutCircleViewTransition: NSObject, UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegate {

    var hideDelayed = false
    var transitionContext: UIViewControllerContextTransitioning?

    init(hideDelayed : Bool = true) {
        self.hideDelayed = hideDelayed
        super.init()
    }


    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 toViewTransitionFromView = toViewController as? TransitionFromViewProtocol else {
            return
        }

        let containerView = transitionContext.containerView()

        let imageViewSnapshot = toViewTransitionFromView.getViewForTransition()
        imageViewSnapshot.alpha = 0.0
        let imageViewSnapshotOriginalFrame = imageViewSnapshot.frame

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

        let quadraticStartFrame = CGRect(x: startFrame.origin.x - (startFrame.height - startFrame.width)/2, y: startFrame.origin.y, width: startFrame.height, height: startFrame.height)

        containerView!.insertSubview(toViewController.view, atIndex: 0)
        containerView!.addSubview(imageViewSnapshot)


        // UIViewController circle shrink animation
        let bigCirclePath = UIBezierPath(ovalInRect: quadraticStartFrame)

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

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

        //        pathAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)

        imageViewSnapshot.frame = quadraticStartFrame

        // Make imageView visible with animation
        let showImageViewAnimation =  {
            imageViewSnapshot.alpha = 1.0
        }
        let showImageViewDelay = 0.3
        UIView.animateWithDuration(transitionDuration(transitionContext) - showImageViewDelay , delay: showImageViewDelay, options: UIViewAnimationOptions.CurveLinear, animations: showImageViewAnimation) { (completed) -> Void in
        }

        // Shrink the imageView to the original size
        let scaleImageViewAnimation = {
            imageViewSnapshot.frame = imageViewSnapshotOriginalFrame
        }
        UIView.animateWithDuration(transitionDuration(transitionContext), delay: 0.0, options: UIViewAnimationOptions.CurveLinear, animations: scaleImageViewAnimation) { (completed) -> Void in
            // After the complete animations have endet

            // Hide ImageView after it is completely resized. Added some animation delay to not abruptly change the contactImage
            if self.hideDelayed {
                let hideImageViewAnimation =  {
                    imageViewSnapshot.alpha = 0.0
                }
                UIView.animateWithDuration(0.2 , delay: 0.2, options: UIViewAnimationOptions.CurveLinear, animations: hideImageViewAnimation) { (completed) -> Void in
                    imageViewSnapshot.removeFromSuperview()
                }
            }
            else {
                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
    }

}

回答1:

You need set new frame to toViewController.view. This will update the view to current orientation.

toViewController.view.frame = fromViewController.view.frame

Use my updated code :

class ZoomOutCircleViewTransition: NSObject, UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegate {

    var hideDelayed = false
    var transitionContext: UIViewControllerContextTransitioning?

    init(hideDelayed : Bool = true) {
        self.hideDelayed = hideDelayed
        super.init()
    }


    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 toViewTransitionFromView = toViewController as? TransitionFromViewProtocol else {
            return
        }

        toViewController.view.frame = fromViewController.view.frame

        let containerView = transitionContext.containerView()

        let imageViewSnapshot = toViewTransitionFromView.getViewForTransition()
        imageViewSnapshot.alpha = 0.0
        let imageViewSnapshotOriginalFrame = imageViewSnapshot.frame

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

        let quadraticStartFrame = CGRect(x: startFrame.origin.x - (startFrame.height - startFrame.width)/2, y: startFrame.origin.y, width: startFrame.height, height: startFrame.height)

        containerView!.insertSubview(toViewController.view, atIndex: 0)
        containerView!.addSubview(imageViewSnapshot)


        // UIViewController circle shrink animation
        let bigCirclePath = UIBezierPath(ovalInRect: quadraticStartFrame)

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

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

        //        pathAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)

        imageViewSnapshot.frame = quadraticStartFrame

        // Make imageView visible with animation
        let showImageViewAnimation =  {
            imageViewSnapshot.alpha = 1.0
        }
        let showImageViewDelay = 0.3
        UIView.animateWithDuration(transitionDuration(transitionContext) - showImageViewDelay , delay: showImageViewDelay, options: UIViewAnimationOptions.CurveLinear, animations: showImageViewAnimation) { (completed) -> Void in
        }

        // Shrink the imageView to the original size
        let scaleImageViewAnimation = {
            imageViewSnapshot.frame = imageViewSnapshotOriginalFrame
        }
        UIView.animateWithDuration(transitionDuration(transitionContext), delay: 0.0, options: UIViewAnimationOptions.CurveLinear, animations: scaleImageViewAnimation) { (completed) -> Void in
            // After the complete animations have endet

            // Hide ImageView after it is completely resized. Added some animation delay to not abruptly change the contactImage
            if self.hideDelayed {
                let hideImageViewAnimation =  {
                    imageViewSnapshot.alpha = 0.0
                }
                UIView.animateWithDuration(0.2 , delay: 0.2, options: UIViewAnimationOptions.CurveLinear, animations: hideImageViewAnimation) { (completed) -> Void in
                    imageViewSnapshot.removeFromSuperview()
                }
            }
            else {
                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
    }

}

class ZoomOutCircleViewTransition: NSObject, UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegate {

    var hideDelayed = false
    var transitionContext: UIViewControllerContextTransitioning?

    init(hideDelayed : Bool = true) {
        self.hideDelayed = hideDelayed
        super.init()
    }


    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 toViewTransitionFromView = toViewController as? TransitionFromViewProtocol else {
            return
        }

        toViewController.view.frame = fromViewController.view.frame

        let containerView = transitionContext.containerView()

        let imageViewSnapshot = toViewTransitionFromView.getViewForTransition()
        imageViewSnapshot.alpha = 0.0
        let imageViewSnapshotOriginalFrame = imageViewSnapshot.frame

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

        let quadraticStartFrame = CGRect(x: startFrame.origin.x - (startFrame.height - startFrame.width)/2, y: startFrame.origin.y, width: startFrame.height, height: startFrame.height)

        containerView!.insertSubview(toViewController.view, atIndex: 0)
        containerView!.addSubview(imageViewSnapshot)


        // UIViewController circle shrink animation
        let bigCirclePath = UIBezierPath(ovalInRect: quadraticStartFrame)

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

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

        //        pathAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)

        imageViewSnapshot.frame = quadraticStartFrame

        // Make imageView visible with animation
        let showImageViewAnimation =  {
            imageViewSnapshot.alpha = 1.0
        }
        let showImageViewDelay = 0.3
        UIView.animateWithDuration(transitionDuration(transitionContext) - showImageViewDelay , delay: showImageViewDelay, options: UIViewAnimationOptions.CurveLinear, animations: showImageViewAnimation) { (completed) -> Void in
        }

        // Shrink the imageView to the original size
        let scaleImageViewAnimation = {
            imageViewSnapshot.frame = imageViewSnapshotOriginalFrame
        }
        UIView.animateWithDuration(transitionDuration(transitionContext), delay: 0.0, options: UIViewAnimationOptions.CurveLinear, animations: scaleImageViewAnimation) { (completed) -> Void in
            // After the complete animations have endet

            // Hide ImageView after it is completely resized. Added some animation delay to not abruptly change the contactImage
            if self.hideDelayed {
                let hideImageViewAnimation =  {
                    imageViewSnapshot.alpha = 0.0
                }
                UIView.animateWithDuration(0.2 , delay: 0.2, options: UIViewAnimationOptions.CurveLinear, animations: hideImageViewAnimation) { (completed) -> Void in
                    imageViewSnapshot.removeFromSuperview()
                }
            }
            else {
                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
    }
}


回答2:

I had the same problem and tried solution proposed by @Arun but it did not work. For me worked this:

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

and in completion block of animation:

    }) { finished in
        toViewController.view.frame = toFrame
        transitionContext.completeTransition(finished)
    }

Hope that helps!



标签: ios swift uiview