【问题标题】:SwiftUI runJavaScriptConfirmPanelWithMessage does not work with WKWebViewSwiftUI runJavaScriptConfirmPanelWithMessage 不适用于 WKWebView
【发布时间】:2019-12-18 22:16:43
【问题描述】:

我正在使用 SwiftUI 构建一个简单的 webapp,它只是将我的网站加载到 webview 中。在我的网站上,它必须显示 Javascript confirm 以便用户能够从应用程序中注销,并且我在 runJavaScriptConfirmPanelWithMessage 中的函数在触发 Javascript 确认时会导致应用程序崩溃。

这是我的 webapp 的完整代码。

import SwiftUI
import WebKit

struct Webview : UIViewRepresentable {
    let request: URLRequest
    var webview: WKWebView?

    init(web: WKWebView?, req: URLRequest) {
        self.webview = WKWebView()
        self.request = req
    }

    class Coordinator: NSObject, WKUIDelegate {
        var parent: Webview

        init(_ parent: Webview) {
            self.parent = parent
        }

        // Delegate methods go here

        func webView(_ webView: WKWebView,
        runJavaScriptAlertPanelWithMessage message: String,
             initiatedByFrame frame: WKFrameInfo,
             completionHandler: @escaping () -> Void) {

            // alert functionality goes here
            let alertController = UIAlertController(title: nil, message: message, preferredStyle: .alert)

            alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))

            alertController.present(alertController, animated: true)

            completionHandler()

        }

        func webView(_ webView: WKWebView,
        runJavaScriptConfirmPanelWithMessage message: String,
             initiatedByFrame frame: WKFrameInfo,
             completionHandler: @escaping (Bool) -> Void) {

            // confirm functionality goes here. THIS CRASHES THE APP
            let alertController = UIAlertController(title: nil, message: message, preferredStyle: .alert)

            alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in
                completionHandler(true)
            }))

            alertController.addAction(UIAlertAction(title: "Cancel", style: .default, handler: { (action) in
                completionHandler(false)
            }))

            alertController.present(alertController, animated: true, completion: nil)
        }

        func webView(_ webView: WKWebView,
        runJavaScriptTextInputPanelWithPrompt prompt: String,
                  defaultText: String?,
             initiatedByFrame frame: WKFrameInfo,
             completionHandler: @escaping (String?) -> Void) {

            // prompt functionality goes here

        }
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIView(context: Context) -> WKWebView  {
        return webview!
    }

    func updateUIView(_ uiView: WKWebView, context: Context) {
        uiView.uiDelegate = context.coordinator
        uiView.load(request)
    }

    func goBack(){
        webview?.goBack()
    }

    func goForward(){
        webview?.goForward()
    }

    func reload(){
        webview?.reload()
    }
}

struct ContentView: View {

    let webview = Webview(web: nil, req: URLRequest(url: URL(string: "https://google.com")!))

    var body: some View {
        VStack {
            webview
            HStack() {
                Button(action: {
                    self.webview.goBack()
                }){
                    Image(systemName: "chevron.left")
                }.padding(32)

                Button(action: {
                    self.webview.reload()
                }){
                    Image(systemName: "arrow.clockwise")
                }.padding(32)

                Button(action: {
                    self.webview.goForward()
                }){
                    Image(systemName: "chevron.right")
                }.padding(32)
            }.frame(height: 40)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

我不确定我在本节中对导致应用程序崩溃的runJavaScriptConfirmPanelWithMessage 做错了什么。

这是我遇到问题的部分。

        func webView(_ webView: WKWebView,
        runJavaScriptConfirmPanelWithMessage message: String,
             initiatedByFrame frame: WKFrameInfo,
             completionHandler: @escaping (Bool) -> Void) {

            // confirm functionality goes here. THIS CRASHES THE APP
            let alertController = UIAlertController(title: nil, message: message, preferredStyle: .alert)

            alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in
                completionHandler(true)
            }))

            alertController.addAction(UIAlertAction(title: "Cancel", style: .default, handler: { (action) in
                completionHandler(false)
            }))

            alertController.present(alertController, animated: true, completion: nil)
        }

谁能告诉我我在这里做错了什么?

【问题讨论】:

  • 您必须在此工作流程中使用 SwiftUI Alert。我会通过Webview 周围的附加包装器来实现,并绑定到当前警报,可能还有附加视图模型来传输 JS 警报参数。
  • @Asperi 感谢您的评论。您的意思是我需要将 Javascript Alert 映射到 SwiftUI alert(developer.apple.com/documentation/swiftui/alert) 才能完成这项工作?那么 Confirm 也一样吗?
  • 是的,这些警报应该通过 SwiftUI View.alert 函数呈现

标签: swift swiftui wkwebview


【解决方案1】:

请注意,您尝试从alertController 呈现alertController,这是一个崩溃原因。

【讨论】:

    【解决方案2】:

    您需要一个视图控制器来显示警报。试试这个:

    func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {
        
        let alertController = UIAlertController(title: nil, message: message, preferredStyle: .actionSheet)
        alertController.addAction(
            UIAlertAction(title: "OK", style: .default, handler: { (action) in completionHandler(true) })
        )
        alertController.addAction(
            UIAlertAction(title: "Cancel", style: .default, handler: { (action) in completionHandler(false) })
        )
        
        if let controller = topMostViewController() {
            controller.present(alertController, animated: true, completion: nil)
        }
        
    }
    
    private func topMostViewController() -> UIViewController? {
        guard let rootController = keyWindow()?.rootViewController else {
            return nil
        }
        return topMostViewController(for: rootController)
    }
    
    private func keyWindow() -> UIWindow? {
        return UIApplication.shared.connectedScenes
            .filter {$0.activationState == .foregroundActive}
            .compactMap {$0 as? UIWindowScene}
            .first?.windows.filter {$0.isKeyWindow}.first
    }
    
    private func topMostViewController(for controller: UIViewController) -> UIViewController {
        if let presentedController = controller.presentedViewController {
            return topMostViewController(for: presentedController)
        } else if let navigationController = controller as? UINavigationController {
            guard let topController = navigationController.topViewController else {
                return navigationController
            }
            return topMostViewController(for: topController)
        } else if let tabController = controller as? UITabBarController {
            guard let topController = tabController.selectedViewController else {
                return tabController
            }
            return topMostViewController(for: topController)
        }
        return controller
    }
    

    感谢这个答案https://stackoverflow.com/a/57877120/11212894

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多