WKWebView javascript 警报、提示、确认不起作用
Posted
技术标签:
【中文标题】WKWebView javascript 警报、提示、确认不起作用【英文标题】:WKWebView javascript alert, prompt, confirm won't work 【发布时间】:2017-09-27 20:18:29 【问题描述】:您好,我正在实现简单的 WKWebView 应用程序,我希望能够通过提示对话框询问用户输入,我尝试使用此处提到的解决方案
https://***.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)
【问题讨论】:
【参考方案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://***.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://***.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)
【讨论】:
以上是关于WKWebView javascript 警报、提示、确认不起作用的主要内容,如果未能解决你的问题,请参考以下文章
iOS WKWebView javascript警报在ipad上崩溃
WKWebView javascript 警报、提示、确认不起作用
嵌入在 html 中的 javascript 未在 wkwebview 中运行
iOS 13 SceneDelegate 上的 WKWebView 警报崩溃