How to present UIAlertController when not in a view controller?

前端 未结 30 3127
庸人自扰
庸人自扰 2020-11-22 06:21

Scenario: The user taps on a button on a view controller. The view controller is the topmost (obviously) in the navigation stack. The tap invokes a utility class method call

30条回答
  •  粉色の甜心
    2020-11-22 06:54

    For iOS 13, building on the answers by mythicalcoder and bobbyrehm:

    In iOS 13, if you are creating your own window to present the alert in, you are required to hold a strong reference to that window or else your alert won't be displayed because the window will be immediately deallocated when its reference exits scope.

    Furthermore, you'll need to set the reference to nil again after the alert is dismissed in order to remove the window to continue to allow user interaction on the main window below it.

    You can create a UIViewController subclass to encapsulate the window memory management logic:

    class WindowAlertPresentationController: UIViewController {
    
        // MARK: - Properties
    
        private lazy var window: UIWindow? = UIWindow(frame: UIScreen.main.bounds)
        private let alert: UIAlertController
    
        // MARK: - Initialization
    
        init(alert: UIAlertController) {
    
            self.alert = alert
            super.init(nibName: nil, bundle: nil)
        }
    
        required init?(coder aDecoder: NSCoder) {
    
            fatalError("This initializer is not supported")
        }
    
        // MARK: - Presentation
    
        func present(animated: Bool, completion: (() -> Void)?) {
    
            window?.rootViewController = self
            window?.windowLevel = UIWindow.Level.alert + 1
            window?.makeKeyAndVisible()
            present(alert, animated: animated, completion: completion)
        }
    
        // MARK: - Overrides
    
        override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
    
            super.dismiss(animated: flag) {
                self.window = nil
                completion?()
            }
        }
    }
    

    You can use this as is, or if you want a convenience method on your UIAlertController, you can throw it in an extension:

    extension UIAlertController {
    
        func presentInOwnWindow(animated: Bool, completion: (() -> Void)?) {
    
            let windowAlertPresentationController = WindowAlertPresentationController(alert: self)
            windowAlertPresentationController.present(animated: animated, completion: completion)
        }
    }
    

提交回复
热议问题