UIWindow not showing over content in iOS 13

后端 未结 12 1658
野的像风
野的像风 2020-12-07 17:41

I am upgrading my app to use the new UIScene patterns as defined in iOS 13, however a critical part of the app has stopped working. I have been using a UI

相关标签:
12条回答
  • 2020-12-07 18:35

    You just need to store strong reference of UIWindow that you want to present. It seems that under the hood view controller that presented does not references to the window.

    0 讨论(0)
  • 2020-12-07 18:35

    As everyone else mentioned, the issue is that a strong reference to the window is required. So to make sure that this window is removed again after use, I encapsulated everything needed in it's own class..

    Here's a little Swift 5 snippet:

    class DebugCheatSheet {
    
        private var window: UIWindow?
    
        func present() {
            let vc = UIViewController()
            vc.view.backgroundColor = .clear
    
            window = UIWindow(frame: UIScreen.main.bounds)
            window?.rootViewController = vc
            window?.windowLevel = UIWindow.Level.alert + 1
            window?.makeKeyAndVisible()
    
            vc.present(sheet(), animated: true, completion: nil)
        }
    
        private func sheet() -> UIAlertController {
            let alert = UIAlertController.init(title: "Cheatsheet", message: nil, preferredStyle: .actionSheet)
            addAction(title: "Ok", style: .default, to: alert) {
                print("Alright...")
            }
            addAction(title: "Cancel", style: .cancel, to: alert) {
                print("Cancel")
            }
            return alert
        }
    
        private func addAction(title: String?, style: UIAlertAction.Style, to alert: UIAlertController, action: @escaping () -> ()) {
            let action = UIAlertAction.init(title: title, style: style) { [weak self] _ in
                action()
                alert.dismiss(animated: true, completion: nil)
                self?.window = nil
            }
            alert.addAction(action)
        }
    }
    

    And here is how I use it.. It's from the lowest view controller in the whole apps view hierarchy, but could be used from anywhere else also:

    private let cheatSheet = DebugCheatSheet()
    
    override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
        if motion == .motionShake {
            cheatSheet.present()
        }
    }
    
    0 讨论(0)
  • 2020-12-07 18:36

    iOS 13 broke my helper functions for managing alerts.

    Because there may be cases where you need multiple alerts to be displayed at the same time (the most recent above the older) for example in case you're displaying a yes or no alert and in the meanwhile your webservice returns with a error you display via an alert (it's a limit case but it can happen),

    my solution is to extend the UIAlertController like this, and let it have its own alertWindow to be presented from.

    The pro is that when you dismiss the alert the window is automatically dismissed because there is any strong reference left, so no further mods to be implemented.

    Disclaimer : I just implemented it, so I still need to see if it's consistent...

    class AltoAlertController: UIAlertController {
    
    var alertWindow : UIWindow!
    
    func show(animated: Bool, completion: (()->(Void))?)
    {
        alertWindow = UIWindow(frame: UIScreen.main.bounds)
        alertWindow.rootViewController = UIViewController()
        alertWindow.windowLevel = UIWindow.Level.alert + 1
        alertWindow.makeKeyAndVisible()
        alertWindow.rootViewController?.present(self, animated: animated, completion: completion)
    }
    

    }

    0 讨论(0)
  • 2020-12-07 18:36

    Swift 4.2 iOS 13 UIAlertController extension

    This code full working in iOS 11, 12 and 13

    import Foundation
    import UIKit
    
    extension UIAlertController{
        private struct AssociatedKeys {
            static var alertWindow = "alertWindow"
        }
        var alertWindow:UIWindow?{
            get{
                guard let alertWindow = objc_getAssociatedObject(self, &AssociatedKeys.alertWindow) as? UIWindow else {
                    return nil
                }
                return alertWindow
            }
            set(value){
                objc_setAssociatedObject(self,&AssociatedKeys.alertWindow,value,objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            }
        }
    
        func show(animated:Bool) {
            self.alertWindow = UIWindow(frame: UIScreen.main.bounds)
            self.alertWindow?.rootViewController = UIViewController()
            self.alertWindow?.windowLevel = UIWindow.Level.alert + 1
            if #available(iOS 13, *){
                let mySceneDelegate = UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate
                mySceneDelegate!.window?.rootViewController?.present(self, animated: animated, completion: nil)
            }
            else{
                self.alertWindow?.makeKeyAndVisible()
                self.alertWindow?.rootViewController?.present(self, animated: animated, completion: nil)
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-07 18:38

    Thank you @glassomoss. My problem is with UIAlertController.

    I solved my problem in this way:

    • I added a variable
    var windowsPopUp: UIWindow?
    
    • I modified the code to display the PopUp:
    public extension UIAlertController {
        func showPopUp() {
            windowsPopUp = UIWindow(frame: UIScreen.main.bounds)
            let vc = UIViewController()
            vc.view.backgroundColor = .clear
            windowsPopUp!.rootViewController = vc
            windowsPopUp!.windowLevel = UIWindow.Level.alert + 1
            windowsPopUp!.makeKeyAndVisible()
            vc.present(self, animated: true)
        }
    }
    
    • In the action of the UIAlertController I added:
    windowsPopUp = nil
    

    without the last line the PopUp is dismissed but the windows remains active not allowing the iteration with the application (with the application window)

    0 讨论(0)
  • 2020-12-07 18:39

    Based on all the proposed solutions, I can offer my own version of the code:

    private var window: UIWindow!
    
    extension UIAlertController {
        func present(animated: Bool, completion: (() -> Void)?) {
            window = UIWindow(frame: UIScreen.main.bounds)
            window.rootViewController = UIViewController()
            window.windowLevel = .alert + 1
            window.makeKeyAndVisible()
            window.rootViewController?.present(self, animated: animated, completion: completion)
        }
    
        open override func viewDidDisappear(_ animated: Bool) {
            super.viewDidDisappear(animated)
            window = nil
        }
    }
    

    How to use:

    // Show message (from any place)
    let alert = UIAlertController(title: "Title", message: "Message", preferredStyle: .alert)
    alert.addAction(UIAlertAction(title: "Button", style: .cancel))
    alert.present(animated: true, completion: nil)
    
    0 讨论(0)
提交回复
热议问题