简体   繁体   English

WKWebView JavaScript警报,提示,确认将不起作用

[英]WKWebView javascript alert, prompt, confirm won't work

hi I'm implementing simple WKWebView appplication and I want to be able to ask user for input via prompt dialogue, I tried to use solution mentioned here 您好,我正在实现简单的WKWebView应用程序,我希望能够通过提示对话框要求用户输入,我试图使用这里提到的解决方案

https://stackoverflow.com/a/40157363/1665293 https://stackoverflow.com/a/40157363/1665293

but I'm not sure how should it work when implemented - should this just add extension to WKWebView for triggering eg regular alert() from javascript or I should pass some different instructions in js to trigger this native alert? 但是我不确定在实现时应该如何工作-应该只是向WKWebView添加扩展以触发例如来自javascript的常规alert()还是我应该在js中传递一些不同的指令来触发此本机警报?

so my question is: 1) how this should work when implemented 2) what am I missing in implementation 所以我的问题是:1)在实施时应该如何工作2)在实施中我缺少什么

here's my controller code (giving whole controller, as I dont know what could be important here) 这是我的控制器代码(提供整个控制器,因为我不知道在这里有什么重要意义)

thanks in advance! 提前致谢!

import UIKit
import WebKit

class ViewController:
    UIViewController
    , WKNavigationDelegate
    , UIScrollViewDelegate
    , WKUIDelegate
{

    @IBOutlet var webView: WKWebView!
    let getUrlAtDocumentStartScript = "GetUrlAtDocumentStart"
    let getUrlAtDocumentEndScript = "GetUrlAtDocumentEnd"

    override func loadView() {
        self.webView = WKWebView()
        self.webView.navigationDelegate = self

        //for prompt
        self.webView?.uiDelegate = self

        view = webView
    }

    override func viewWillAppear(_ animated: Bool) {//white status bar
        super.viewWillAppear(animated)
        webView.isOpaque = false //removes white flash on WKWebView load
        webView.backgroundColor = UIColor(red: 41/255, green: 45/255, blue: 91/255, alpha: 1)
        UIApplication.shared.statusBarStyle = .lightContent

        do {

            let paid = Bundle.main.infoDictionary?["paid"]  as? Bool;
            var fileName = "none"

            if(paid!){
                fileName = "index-ios-wvd-inlined--paid"
            } else {
                fileName = "index-ios-wvd-inlined"
            }
            guard let filePath = Bundle.main.path(forResource: fileName, ofType: "html")
                else {
                    print ("File reading error")
                    return
            }


            let contents =  try String(contentsOfFile: filePath, encoding: .utf8)
            let baseUrl = URL(fileURLWithPath: filePath)
            webView.loadHTMLString(contents as String, baseURL: baseUrl)

        }
        catch {
            print ("File HTML error")
        }

    }

    override var preferredStatusBarStyle : UIStatusBarStyle {//white status bar
        return .lightContent
    }

    override func viewDidLoad() {
        webView.scrollView.bounces = false;
        super.viewDidLoad()
        webView.scrollView.delegate = self //disable zoom


        //for haptics
        let config = WKWebViewConfiguration()
        config.addScript(script: WKUserScript.getUrlScript(scriptName: getUrlAtDocumentStartScript), scriptHandlerName:getUrlAtDocumentStartScript, scriptMessageHandler: self, injectionTime: .atDocumentStart)
        config.addScript(script: WKUserScript.getUrlScript(scriptName: getUrlAtDocumentEndScript), scriptHandlerName:getUrlAtDocumentEndScript, scriptMessageHandler: self, injectionTime: .atDocumentEnd)
        webView = WKWebView(frame:  UIScreen.main.bounds, configuration: config)
        webView.navigationDelegate = self
        view.addSubview(webView)
    }

    //disable zoom        
    func viewForZooming(in: UIScrollView) -> UIView? {
        return nil;
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func tapped(i:Int) {
        print("Triggering haptic #\(i)")

        switch i {
        case 1:
            let generator = UINotificationFeedbackGenerator()
            generator.notificationOccurred(.error)

        case 2:
            let generator = UINotificationFeedbackGenerator()
            generator.notificationOccurred(.success)

        case 3:
            let generator = UINotificationFeedbackGenerator()
            generator.notificationOccurred(.warning)

        case 4:
            let generator = UIImpactFeedbackGenerator(style: .light)
            generator.impactOccurred()

        case 5:
            let generator = UIImpactFeedbackGenerator(style: .medium)
            generator.impactOccurred()

        case 6:
            let generator = UIImpactFeedbackGenerator(style: .heavy)
            generator.impactOccurred()

        default:
            let generator = UISelectionFeedbackGenerator()
            generator.selectionChanged()
        }
    }

    //alert/prompt/confirm dialogs
    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()
        }))

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

        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: .actionSheet)

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

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

}

//sending scripts commands to JS and back
extension ViewController: WKScriptMessageHandler {
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        switch message.name {

        case getUrlAtDocumentStartScript:
            tapped(i: message.body as! Int)
            //print("start: \(message.body)")

        case getUrlAtDocumentEndScript:
            tapped(i: message.body as! Int)
            //print("tapped: \(message.body)")

        default:
            break;
        }
    }
}

extension WKUserScript {
    class func getUrlScript(scriptName: String) -> String {
        return "webkit.messageHandlers.\(scriptName).postMessage(1)"
    }
}

extension WKWebView {
    func loadUrl(string: String) {
        if let url = URL(string: string) {
            load(URLRequest(url: url))
        }
    }
}

extension WKWebViewConfiguration {
    func addScript(script: String, scriptHandlerName:String, scriptMessageHandler: WKScriptMessageHandler, injectionTime:WKUserScriptInjectionTime) {
        let userScript = WKUserScript(source: script, injectionTime: injectionTime, forMainFrameOnly: false)
        userContentController.addUserScript(userScript)
        userContentController.add(scriptMessageHandler, name: scriptHandlerName)
    }
}

okay, so I found answers and solution, 好吧,所以我找到了答案和解决方案,

1) this will add support for native JS methods. 1)这将增加对本机JS方法的支持。 alert() , prompt() and confirm() could be called from JS or via alert()prompt()confirm()可以从JS或通过

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

2) here's the implementation of methods I'm using right now (I insert this in the bottom of ViewController class: 2)这是我现在正在使用的方法的实现(我将其插入ViewController类的底部:

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,
                 runJavaScriptTextInputPanelWithPrompt prompt: String,
                 defaultText: String?,
                 initiatedByFrame frame: WKFrameInfo,
                 completionHandler: @escaping (String?) -> Void) {

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

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

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

        }))

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

            completionHandler(nil)

        }))

        self.present(alert, animated: true, completion: nil)

//        if ipad will crash on this do this (https://stackoverflow.com/questions/42772973/ios-wkwebview-javascript-alert-crashing-on-ipad?noredirect=1&lq=1):
//        if let presenter = alertController.popoverPresentationController {
//            presenter.sourceView = self.view
//        }
//        
//        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)
    }

also to the bottom of viewDidLoad() I added this code: 同样在viewDidLoad()的底部,我添加了以下代码:

webView.uiDelegate = self
webView.navigationDelegate = self
view.addSubview(webView!)

Adding full code into collapsed snippet - in case someone will be confused how exactly this should be used: 将完整的代码添加到折叠的代码段中-以防万一有人混淆应该如何使用它:

 import UIKit import WebKit class ViewController: UIViewController , WKNavigationDelegate , UIScrollViewDelegate , WKUIDelegate { //wk webvew set @IBOutlet var webView: WKWebView! let getUrlAtDocumentStartScript = "GetUrlAtDocumentStart" let getUrlAtDocumentEndScript = "GetUrlAtDocumentEnd" //webkit.messageHandlers.GetUrlAtDocumentEnd.postMessage('1') override func loadView() { self.webView = WKWebView() self.webView.navigationDelegate = self //for prompt ?? self.webView?.uiDelegate = self view = webView } override func viewWillAppear(_ animated: Bool) {//white status bar super.viewWillAppear(animated) webView.isOpaque = false //removes white flash on WKWebView load webView.backgroundColor = UIColor(red: 41/255, green: 45/255, blue: 91/255, alpha: 1) UIApplication.shared.statusBarStyle = .lightContent do { let paid = Bundle.main.infoDictionary?["paid"] as? Bool; var fileName = "none" if(paid!){ fileName = "index-ios-wvd-inlined--paid" } else { fileName = "index-ios-wvd-inlined" } guard let filePath = Bundle.main.path(forResource: fileName, ofType: "html") else { // File Error print ("File reading error") return } let contents = try String(contentsOfFile: filePath, encoding: .utf8) let baseUrl = URL(fileURLWithPath: filePath) webView.loadHTMLString(contents as String, baseURL: baseUrl) } catch { print ("File HTML error") } } override var preferredStatusBarStyle : UIStatusBarStyle {//white status bar return .lightContent } override func viewDidLoad() { webView.scrollView.bounces = false; super.viewDidLoad() //disable zoom webView.scrollView.delegate = self let config = WKWebViewConfiguration() config.addScript(script: WKUserScript.getUrlScript(scriptName: getUrlAtDocumentStartScript), scriptHandlerName:getUrlAtDocumentStartScript, scriptMessageHandler: self, injectionTime: .atDocumentStart) config.addScript(script: WKUserScript.getUrlScript(scriptName: getUrlAtDocumentEndScript), scriptHandlerName:getUrlAtDocumentEndScript, scriptMessageHandler: self, injectionTime: .atDocumentEnd) webView = WKWebView(frame: UIScreen.main.bounds, configuration: config) webView.navigationDelegate = self view.addSubview(webView) webView.uiDelegate = self webView.navigationDelegate = self view.addSubview(webView!) // Do any additional setup after loading the view, typically from a nib. } func viewForZooming(in: UIScrollView) -> UIView? { return nil; } //disable zoom override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } func tapped(i:Int) { print("Running \\(i)") switch i { case 1: let generator = UINotificationFeedbackGenerator() generator.notificationOccurred(.error) case 2: let generator = UINotificationFeedbackGenerator() generator.notificationOccurred(.success) case 3: let generator = UINotificationFeedbackGenerator() generator.notificationOccurred(.warning) case 4: let generator = UIImpactFeedbackGenerator(style: .light) generator.impactOccurred() case 5: let generator = UIImpactFeedbackGenerator(style: .medium) generator.impactOccurred() case 6: let generator = UIImpactFeedbackGenerator(style: .heavy) generator.impactOccurred() default: let generator = UISelectionFeedbackGenerator() generator.selectionChanged() } } //default alert/confirm/prompt dialogs 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, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) { let alert = UIAlertController(title: nil, message: prompt, preferredStyle: .alert) alert.addTextField { (textField) in textField.text = defaultText } alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (action) in if let text = alert.textFields?.first?.text { completionHandler(text) } else { completionHandler(defaultText) } })) alert.addAction(UIAlertAction(title: "Cancel", style: .default, handler: { (action) in completionHandler(nil) })) self.present(alert, animated: true, completion: nil) // if ipad will crash on this try to uncomment (based on https://stackoverflow.com/questions/42772973/ios-wkwebview-javascript-alert-crashing-on-ipad?noredirect=1&lq=1): // if let presenter = alertController.popoverPresentationController { // presenter.sourceView = self.view // } // // 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, didFinish navigation: WKNavigation!) { webView.evaluateJavaScript("confirm('Hello from evaluateJavascript()')", completionHandler: nil) } } extension ViewController: WKScriptMessageHandler { func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { switch message.name { case getUrlAtDocumentStartScript: tapped(i: message.body as! Int) //print("start: \\(message.body)") case getUrlAtDocumentEndScript: tapped(i: message.body as! Int) //print("tapped: \\(message.body)") default: break; } } } extension WKUserScript { class func getUrlScript(scriptName: String) -> String { return "webkit.messageHandlers.\\(scriptName).postMessage(1)" } } extension WKWebView { func loadUrl(string: String) { if let url = URL(string: string) { load(URLRequest(url: url)) } } } extension WKWebViewConfiguration { func addScript(script: String, scriptHandlerName:String, scriptMessageHandler: WKScriptMessageHandler, injectionTime:WKUserScriptInjectionTime) { let userScript = WKUserScript(source: script, injectionTime: injectionTime, forMainFrameOnly: false) userContentController.addUserScript(userScript) userContentController.add(scriptMessageHandler, name: scriptHandlerName) } } 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM