WKWebView - Javascript Confirm和Alert不起作用。

16

我正在使用WKWebView打开example.com,在那里我有一个测试链接,它应该打开一个JS alert,但我无法在设备上显示它,只有在浏览器中查看网站时才有效。

我正在使用WKUIDelegate,并将以下代码添加到ViewController.swift文件中:

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

    NSLog("Hello")
}

当我点击生成JS警报的链接时,我在XCode控制台中看不到任何内容。

我错过了什么?


iOS的WKWebView现在可以显示JavaScript警告对话框。 - LF00
3个回答

32

虽然有点晚了,但我想分享一下我的经验供未来参考。在我尝试使用Swift 3和IOS 10进行编程时,@Bon Bon的答案引导了我找到解决方案,需要对代码进行一些修改。 首先,您需要实现WKUIDelegate,因此请将其添加到ViewController声明中:

class ViewController: UIViewController, WKUIDelegate {

当你实例化WKWebView对象时,例如像这样:

self.webView = WKWebView(frame: self.view.frame)

同时,您还需要为该实例分配正确的值到 uiDelegate 属性上:

self.webView?.uiDelegate = self

最后,您可以使用@Bon Bon提供的代码,但请注意,Swift 3需要一些小的差异,例如,presentViewController方法的名称变为present

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

    let alertController = UIAlertController(title: nil, message: message, preferredStyle: .actionSheet)

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

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

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)
    }))

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

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

    let alertController = UIAlertController(title: nil, message: prompt, preferredStyle: .alert)

    alertController.addTextField { (textField) in
        textField.text = defaultText
    }

    alertController.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (action) in
        if let text = alertController.textFields?.first?.text {
            completionHandler(text)
        } else {
            completionHandler(defaultText)
        }

    }))

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

        completionHandler(nil)

    }))

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

这使得alertconfirmation文本输入框可以在WKWebView中正常工作,而且在Xcode 8中没有任何编译警告。我并不是一位熟练的Swift程序员,如果对代码的正确性有任何有用的评论,将不胜感激。


1
感谢@Bon Bon提供原始代码,没有它我将需要通宵工作! - wiredolphin
2
应该为提示设置 preferredStyle: .alert,而不是 preferredStyle: .actionSheet - 如果不进行更改,将会出现文本字段只能添加到警报中的错误。 - godblessstrawberry
2
除非您应用godblessstrawberry的评论,否则在使用Swift 4时,似乎prompt()会出现信号SIGABRT失败。@wiredolphin可能需要更改您的答案纠正以使用preferredStyle:.alert。 - Raymond Naseef
1
这是所选答案。 - kchoi
对于iPad,preferredStyle必须是.alert,否则将抛出异常。 - Jerzy Kiler

20

您还需要在WKWebView中设置uiDelegate。

import UIKit
import WebKit

class ViewController: UIViewController, WKUIDelegate, WKNavigationDelegate {

    var wkWebView: WKWebView!

    public override func viewDidLoad() {
        super.viewDidLoad()

        wkWebView = WKWebView(frame: view.bounds, configuration: WKWebViewConfiguration())
        wkWebView.uiDelegate = self
        wkWebView.navigationDelegate = self
        view.addSubview(wkWebView!)
        let url = URL(string: "https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_alert")!
        wkWebView.load(URLRequest(url: url))
    }

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

        let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert)
        let title = NSLocalizedString("OK", comment: "OK Button")
        let ok = UIAlertAction(title: title, style: .default) { (action: UIAlertAction) -> Void in
            alert.dismiss(animated: true, completion: nil)
        }
        alert.addAction(ok)
        present(alert, animated: true)
        completionHandler()
    }

    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        wkWebView.evaluateJavaScript("alert('Hello from evaluateJavascript()')", completionHandler: nil)
    }
}

有关confirm()prompt(),请参阅其他委托方法。


是的,这个已经设置好了。你能让JS在WKWebView中工作吗?我不得不改变所有本地JS警告/确认对话框,使用bootbox对话框来解决这个问题。 - Mike Purcell
是的,对我来说它运行得很好。你的控制器是你的UIDelegate还是你有一个单独的对象?也许委托被释放了? - Onato
你能用这段代码打开本地的JS对话框吗? - Mike Purcell
是的。如果您将一个示例项目上传到Github,我可以查看为什么它对您不起作用。 - Onato
这对于wkwebview的evaluateJavaScript()无效,请参考此帖子https://dev59.com/wFcP5IYBdhLWcg3w8eW5。 - LF00

8

这里有一个Swift中各种JavaScript警告实现的示例代码:

关键是将JavaScript警告中的信息转换为原生UI,并调用completionHandler()将用户操作发送回JavaScript引擎。

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

        let alertController = UIAlertController(title: nil, message: message, preferredStyle: .ActionSheet)

        alertController.addAction(UIAlertAction(title: "Ok", style: .Default, handler: { (action) in
            completionHandler()
        }))

        self.presentViewController(alertController, animated: true, completion: nil)
}

func webView(webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: (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)
        }))

        self.presentViewController(alertController, animated: true, completion: nil)
}

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

        let alertController = UIAlertController(title: nil, message: prompt, preferredStyle: .ActionSheet)

        alertController.addTextFieldWithConfigurationHandler { (textField) in
            textField.text = defaultText
        }

        alertController.addAction(UIAlertAction(title: "Ok", style: .Default, handler: { (action) in
            if let text = alertController.textFields?.first?.text {
                completionHandler(text)
            } else {
                completionHandler(defaultText)
            }

        }))

        alertController.addAction(UIAlertAction(title: "Cancel", style: .Default, handler: { (action) in

            completionHandler(nil)

        }))

        self.presentViewController(alertController, animated: true, completion: nil)
}

你如何修改这段代码,使其也能在iPad上运行? - applecrusher
2
我需要以下这行代码,我将其作为编辑添加到了这个答案中:alertController.popoverPresentationController?.sourceView = self.view - applecrusher
@applecrusher
你是否遇到过这样的问题,即如果您尝试同时显示多个警报,则应用程序会崩溃?在iPhone上运行良好,但在iPad上崩溃。
- Crashalot

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接