首页 > 解决方案 > SwiftUI 中的全局警报

问题描述

我正在尝试在 SwiftUI 中显示全局警报。无论当前在屏幕上显示/呈现什么(例如工作表),此警报都应显示在所有内容之上。

这是我的代码:

@main
struct MyApp: App {
    
    @State private var showAlert = false
    
    var body: some Scene {
        WindowGroup {
            MainView()
                .onReceive(NotificationCenter.default.publisher(for:NSNotification.Name.SomeNotification), perform: { _ in
                    showAlert = true
                })
                .alert(
                    isPresented: $showAlert,
                    content: {Alert(title: Text("Alert!"))}
                )
        }
    }
}

这在某些情况下不起作用,例如,如果在屏幕上当前显示工作表时收到通知。在这种情况下,不会显示警报,控制台上会显示以下消息:

Blockquote [Presentation] Attempt to present <SwiftUI.PlatformAlertController: 0x7fbee6921400> on < TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier _: 0x7fbee642ac60> (from < TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier _: 0x7fbee642ac60>) which is already presenting < TtGC7SwiftUI22SheetHostingControllerVS_7AnyView : 0x7fbee8405360>.

这是有道理的,因为我试图在已经显示工作表的视图上显示警报。

在 UIKit 上,我使用以下类实现了这一点:

class GlobalAlertController: UIAlertController {
    
    var globalPresentationWindow: UIWindow?
    
    func presentGlobally(animated: Bool, completion: (() -> Void)?) {
        globalPresentationWindow = UIWindow(frame: UIScreen.main.bounds)
        globalPresentationWindow?.rootViewController = UIViewController()
        globalPresentationWindow?.windowLevel = UIWindow.Level.alert + 1
        globalPresentationWindow?.backgroundColor = .clear
        globalPresentationWindow?.makeKeyAndVisible()
        globalPresentationWindow?.rootViewController?.present(self, animated: animated, completion: completion)
    }
    
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        globalPresentationWindow?.isHidden = true
        globalPresentationWindow = nil
    }

}

此类允许我以这种方式在所有内容之上显示全局警报:

let alertController = GlobalAlertController(title: "Title", message: "Message", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Done", style: .cancel, handler: nil))
alertController.presentGlobally(animated: true, completion: nil)

任何人都知道如何在 SwiftUI 中实现类似的东西?

标签: swiftui

解决方案


刚刚发现我实际上可以使用我的旧 UIKit 代码来实现这一点。唯一需要更改的是添加对场景的支持(SwiftUI 设计使用场景),如下所示:

class GlobalAlertController: UIAlertController {
    
    var globalPresentationWindow: UIWindow?
    
    func presentGlobally(animated: Bool, completion: (() -> Void)?) {
        globalPresentationWindow = UIWindow(frame: UIScreen.main.bounds)
        
        //This is needed when using scenes.
        if let currentWindowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene {
            globalPresentationWindow?.windowScene = currentWindowScene
        }

        globalPresentationWindow?.rootViewController = UIViewController()
        globalPresentationWindow?.windowLevel = UIWindow.Level.alert + 1
        globalPresentationWindow?.backgroundColor = .clear
        globalPresentationWindow?.makeKeyAndVisible()
        globalPresentationWindow?.rootViewController?.present(self, animated: animated, completion: completion)
    }
    
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        globalPresentationWindow?.isHidden = true
        globalPresentationWindow = nil
    }

}

现在我可以像这样显示全局警报:

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            MainView()
                .onReceive(NotificationCenter.default.publisher(for:NSNotification.Name.SomeNotification), perform: { _ in
                    let alertController = GlobalAlertController(title: "Title", message: "Message", preferredStyle: .alert)
                    alertController.addAction(UIAlertAction(title: "Done", style: .cancel, handler: nil))
                    alertController.presentGlobally(animated: true, completion: nil)
                })
        }
    }
}

它可以工作,尽管更类似于 SwiftUI 的方法会很好。


推荐阅读