0

Warning: Attempt to present on whose view is not in the window hierarchy!

I have a custom presented view controller and would like to present UIAlertController on top of it. When presenting without custom transition everything works fine.

Tried adding definesPresentationContext = true without luck

Maybe my custom transition should include something like addChildViewController() ?

First VC

let adVC = AdViewController(with: adView)
adVC.setupAd(with: index)
let adNav = UINavigationController(rootViewController: adVC)
adNav.modalPresentationStyle = .custom
adNav.transitioningDelegate = adVC.adCustomTransition
self.present(adNav, animated: true, completion: nil)

AdViewController

class AdViewController: UIViewController {

    var adCustomTransition = AdCustomTransition()

    override func viewDidLoad() {
    super.viewDidLoad()

        definesPresentationContext = true;
        submitButton.addTarget(self, action: #selector(presentAlert), for: .touchUpInside)

    }

    func presentAlert() {
        DispatchQueue.main.async {
            let alertVC = UIAlertController(title: "Alert!", message: "test alert message", preferredStyle: .alert)
            alertVC.view.tintColor = .main
            alertVC.addAction(UIAlertAction(title: "Ok", style: .default, handler: nil))
            self.present(alertVC, animated: true, completion: nil)
        }
    }
}




    class AdCustomTransition : NSObject, UIViewControllerTransitioningDelegate {


        func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            return AdAnimationController(withDuration: 0.4, forTransitionType: .present)
        }

        func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            return AdAnimationController(withDuration: 0.4, forTransitionType: .dismiss)
        }
    }

    class AdAnimationController: NSObject, UIViewControllerAnimatedTransitioning {

        enum TransitionType {
            case present, dismiss
        }

        var duration: TimeInterval
        var isPresenting: Bool

        init(withDuration duration: TimeInterval, forTransitionType type: TransitionType) {
            self.duration = duration
            self.isPresenting = type == .present
            super.init()
        }



        func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
            return self.duration
        }

        func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {

            let containerView = transitionContext.containerView
            let toFrame = transitionContext.finalFrame(for: transitionContext.viewController(forKey: .to)!)

            if isPresenting == true {
                let toVC = (transitionContext.viewController(forKey: .to) as! MainNavigationController).viewControllers.first as! AdViewController

                containerView.addSubview(toVC.view)
                containerView.layoutIfNeeded()

                let bottomHeight = toVC.getSafeBottomHeight()
                let offset = toVC.containerView.bounds.height - 120 - bottomHeight

                toVC.containerView.transform = CGAffineTransform(translationX: 0, y: offset)

                UIView.animate(withDuration: self.duration, delay: 0, usingSpringWithDamping: 5.4, initialSpringVelocity: 0.8, options: .curveEaseInOut, animations: {
                    toVC.containerView.transform = CGAffineTransform(translationX: 0, y: -1 * bottomHeight)
                }, completion: { (_) in
                    toVC.view.frame = toFrame
                    transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
                })

            } else {

                let fromVC = (transitionContext.viewController(forKey: .from) as! MainNavigationController).viewControllers.first as! AdViewController
                let toVC = transitionContext.viewController(forKey: .to)!

                containerView.addSubview(toVC.view)
                containerView.addSubview(fromVC.view)

                containerView.layoutIfNeeded()

                let bottomHeight = fromVC.getSafeBottomHeight()

                let offset = fromVC.containerView.bounds.height - 110 - bottomHeight
                fromVC.containerView.transform = .identity

                UIView.animate(withDuration: self.duration, delay: 0, usingSpringWithDamping: 5.7, initialSpringVelocity: 0.8, options: .curveEaseInOut, animations: {
                    fromVC.containerView.transform = CGAffineTransform(translationX: 0, y: offset)
                }, completion: { (_) in
                    toVC.view.frame = toFrame
                    UIApplication.shared.keyWindow?.addSubview(toVC.view)
                    transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
                })
            }
        }
}
HelloimDarius
  • 695
  • 5
  • 23
  • Are you sure the custom VC is actually presented at the time you want to show the alert? The error message suggests that the presenting VC is not being presented itself. – dr_barto Feb 07 '18 at 09:25
  • Did you check this: https://stackoverflow.com/questions/11862883/attempt-to-present-uiviewcontroller-on-uiviewcontroller-whose-view-is-not-in-the – Andreas Oetjen Feb 07 '18 at 09:26
  • Presented indeed. It's on UIButton target action, so... – HelloimDarius Feb 07 '18 at 09:28
  • 1
    It would help *greatly* if you posted all the code needed to reproduce this. (1) How are you "presenting" this view controller? (2) How and *which* VC is trying to present the alert? (3) The error *is* correct. And no, you *shouldn't* need to use `addChildViewController`... but without some actual code it's really hard to say what is the issue. –  Feb 07 '18 at 09:31

0 Answers0