【问题标题】:WKWebView javascript alert, prompt, confirm won't workWKWebView javascript 警报、提示、确认不起作用
【发布时间】:2017-09-27 20:18:29
【问题描述】:

您好,我正在实现简单的 WKWebView 应用程序,我希望能够通过提示对话框询问用户输入,我尝试使用此处提到的解决方案

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

但我不确定它在实现时应该如何工作 - 这是否应该只是向 WKWebView 添加扩展以触发例如来自 javascript 的常规 alert() 还是我应该在 js 中传递一些不同的指令来触发这个原生警报?

所以我的问题是: 1)这在实施时应该如何工作 2)我在实施中缺少什么

这是我的控制器代码(给出整个控制器,因为我不知道这里有什么重要的)

提前致谢!

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

【问题讨论】:

    标签: javascript xcode swift3 wkwebview


    【解决方案1】:

    好的,所以我找到了答案和解决方案,

    1) 这将增加对原生 JS 方法的支持。 alert()prompt()confirm() 可以从 JS 或通过

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

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

    也在viewDidLoad()的底部添加了这段代码:

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

    将完整代码添加到折叠的 sn-p 中 - 以防有人会混淆应该如何使用它:

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

    【讨论】:

      猜你喜欢
      • 2016-03-15
      • 2023-03-15
      • 1970-01-01
      • 2016-10-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多