Rob
Rob

Reputation: 15992

UIViewControllerAnimatedTransitioning: screen keeps getting black under custom view

I have found many related topics on SO (and elsewhere) but still couldn't find a solution to my problem. I want to display a custom alert on my view using UIViewControllerTransitioningDelegate. So first, in my initial view controller, here is the call:

@IBAction func tappedButton(_ sender: Any) { 
    MyAlertViewController.presentIn(viewController: self)
}

And here the code of MyAlertViewController:

import UIKit

open class MyAlertViewController: UIViewController, UIViewControllerTransitioningDelegate {

    @IBOutlet weak var overlayView: UIView?
    @IBOutlet weak var alertView: UIView?
    @IBOutlet weak var alertCenterYConstraint: NSLayoutConstraint?

    public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: "MyAlertView", bundle: nil)
        self.transitioningDelegate = self
    }

    required public init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    static func presentIn(viewController: UIViewController) {
        let alertViewController = MyAlertViewController()

        if Thread.isMainThread {
             viewController.present(alertViewController, animated: true, completion: nil)
        } else {
            DispatchQueue.main.async {
                viewController.present(alertViewController, animated: true, completion: nil)
            }
        }
    }

    public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return MyDismissAlertViewAnimationController()
    }

    public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return MyPresentAlertViewAnimationController()
    }
}

class MyPresentAlertViewAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
    public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.3
    }

    public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        let toViewController: MyAlertViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) as! MyAlertViewController
        let duration = self.transitionDuration(using: transitionContext)

        let containerView = transitionContext.containerView
        toViewController.view.frame = containerView.frame
        containerView.addSubview(toViewController.view)

        toViewController.overlayView?.alpha = 0.0
        UIView.animate(withDuration: duration, animations: {
            toViewController.overlayView?.alpha = 0.6
        })

        let finishFrame = toViewController.alertView?.frame
        var startingFrame = finishFrame
        startingFrame?.origin.y = -((finishFrame?.height)!)
        toViewController.alertView?.frame = startingFrame!

        UIView.animate(withDuration: 0.5, delay: 0.0, usingSpringWithDamping: 0.7, initialSpringVelocity: 1.0, options: .layoutSubviews, animations: {
            toViewController.alertView?.frame = finishFrame!
        }, completion: { result in
            transitionContext.completeTransition(result)
        })
    }
}

class MyDismissAlertViewAnimationController: NSObject,  UIViewControllerAnimatedTransitioning {
    public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.3
    }

    public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        let fromViewController: MyAlertViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from) as! MyAlertViewController
        let duration = self.transitionDuration(using: transitionContext)

        UIView.animate(withDuration: duration, animations: {
            fromViewController.overlayView?.alpha = 0.0
        })

        var finishFrame = fromViewController.alertView?.frame
        finishFrame?.origin.y = -(finishFrame?.height)!
        finishFrame?.origin.y = fromViewController.isDismissingByBottom ? fromViewController.view.frame.size.height : -(finishFrame?.height)!

        UIView.animate(withDuration: duration, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: .layoutSubviews, animations: {
            fromViewController.alertView?.frame = finishFrame!
        }, completion: { result in
            transitionContext.completeTransition(true)
        })
    }
}

The animation works fine, the black screen only appears after the call to completeTransition() as you can see below:

enter image description here

Thanks for your help...

Upvotes: 0

Views: 993

Answers (1)

Casper Zandbergen
Casper Zandbergen

Reputation: 3687

I think you need to either set your background color like so:

self.view.backgroundColor = .clear

This will make sure the background you see is not just the background color of your modal.

Or prevent the presenting view controller to be removed from the screen by making the modal presentation style overCurrentContext

self.modalPresentationStyle = .overCurrentContext

Upvotes: 1

Related Questions