iOS WKWebView无法显示javascript的alert()对话框。

79

我在iOS 8中使用WKWebView时遇到了一些问题,无法显示从Javascript调用的警告对话框。在创建标准WKWebView并加载HTML文件后,页面上有一个按钮,可以创建带有一些文本的简单警告。这在UIWebView和Google Chrome / Safari中有效,但似乎在WKWebView中无效。谢谢您的任何帮助。

我的设置如下:

WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
config.allowsInlineMediaPlayback = YES;
config.mediaPlaybackRequiresUserAction = false;
_wkViewWeb = [[WKWebView alloc] initWithFrame:_viewWeb.frame config];
_wkViewWeb.scrollView.scrollEnabled = NO;
NSString *fullURL = @"file://.../TestSlide.html";
NSURL *url = [NSURL URLWithString:fullURL];
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:10];

[_wkViewWeb loadRequest:request];

这个 HTML 具有以下功能:

<SCRIPT Language="JavaScript">
function alertTest() {
    alert("Testing Alerts");
}
</SCRIPT>

还有一个按钮:

<b>Test Alerts: <input type="button" value="Alert Popup" onclick="alertTest()"><br></b> <br>

这个设置在UIWebView和普通浏览器中有效,但在WKWebView中无效。 我的配置是否有误? 我应该使用WK委托之一来控制警告/确认对话框行为吗? 谢谢。


你可以尝试将HTML文件托管并使用"http://"而不是"file://",这可能是由于wkwebview中的此错误引起的:http://www.openradar.me/radar?id=5839348817723392 - krisrak
HTML文件被本地存储,因此可以离线访问。文件可以正常加载,只是当按钮被点击时警告对话框没有显示。 - Charlie
7个回答

93
为了解决这个问题,您需要为 Web 视图添加 WKUIDelegate。委托的职责是决定是否显示警报以及以何种方式显示。您需要为警报、确认和文本输入(提示)实现此功能。
以下是一个示例代码,没有对页面网址或安全特性进行任何验证:
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
{
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:message
                                                                             message:nil
                                                                      preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:[UIAlertAction actionWithTitle:@"OK"
                                                        style:UIAlertActionStyleCancel
                                                      handler:^(UIAlertAction *action) {
                                                          completionHandler();
                                                      }]];
    [self presentViewController:alertController animated:YES completion:^{}];
}

更多内容请参考官方文档


6
如果你仍然没有明白这个委托函数的调用方式,记得尝试另外两个:runJavaScriptConfirmPanelWithMessagerunJavaScriptTextInputPanelWithPrompt。委托事件取决于警报类型。 - Tim Chen
6
这里有一个非常好的模板可用于实现所有3个委托方法:https://gist.github.com/davbeck/8613f2e74e8fc335c14d - Guy Lowe
这对于wkwebview的evaluateJavaScript()无效,请参考此帖子https://dev59.com/wFcP5IYBdhLWcg3w8eW5。 - LF00

61

Swift 3所有三个可选函数已实现:

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

1
这段代码应该插入在哪里?我将其直接插入到“class ViewController”中 - 它可以编译,但是我的JS无法触发prompt() - godblessstrawberry
@godblessstrawberry 这段代码应该插入到你的 WKView 的 uiDelegate (WKUIDelegate) 中,通常是实现了该协议的视图控制器。 - Lio
2
@Crashalot:只需更改(preferredStyle: .alert),它就能完美运行。谢谢。 - Vijay
1
另外,您需要将以下代码添加到您的viewController中:webView.uiDelegate = self; - simdrouin
使用UIAlertController.Style.alert而不是.actionSheet作为preferredStyle。 - MichaelB

21

稍微扩展一下,WKWebView要求您自己显示警报、提示和确认。通过成为WKUIDelegate来实现:

#import <WebKit/WebKit.h>
@interface MyController : UIViewController<WKUIDelegate>

然后指定代表:

web.UIDelegate = self;

然后你需要实际实现 alert、prompt 和 confirm。我创建了 WKWebViewPanelManager.h/m 作为一种简单的实现方式,这是我的操作:

- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
    [WKWebViewPanelManager presentAlertOnController:self.view.window.rootViewController title:@"Alert" message:message handler:completionHandler];
}

- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler {
    [WKWebViewPanelManager presentConfirmOnController:self.view.window.rootViewController title:@"Confirm" message:message handler:completionHandler];
}

- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler {
    [WKWebViewPanelManager presentPromptOnController:self.view.window.rootViewController title:@"Prompt" message:prompt defaultText:defaultText handler:completionHandler];
}

当然,过滤掉坏的alert/confirm/prompt请求取决于你。


16

这是用实现的:

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

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

    alertController.addAction(UIAlertAction(title: "OK", style: .cancel) {
        _ in completionHandler()}
    )

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

8
  1. Implement the protocol to your WKWebview container controller. WKUIDelegate
  2. Add preference to WKWebview to enable javascript in viewDidLoad() as.

    // enable JS
    webView.configuration.preferences.javaScriptEnabled = true
    
  3. Register WKWebview UI Delegate in viewDidLoad() as

    self.webView.uiDelegate = self
    
  4. Implement the below delegate in your class.

    func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: 
    String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> 
    Void) {
    let alertController = UIAlertController(title: message,message: nil,preferredStyle: 
    .alert)
    
    alertController.addAction(UIAlertAction(title: "OK", style: .cancel) {_ in 
    completionHandler()})
    
    self.present(alertController, animated: true, completion: nil)
    }
    

方法签名已更改:func webView(_ webView:WKWebView,runJavaScriptConfirmPanelWithMessage message:String,initiatedByFrame frame:WKFrameInfo,completionHandler:@escaping(Bool)-> Void)... - Joel B

7

以下是 Swift 4.2 的代码:

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

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

    alertController.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.cancel) {
        _ in completionHandler()}
    );

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

使用未解决的标识符“UIAlertController”。使用XCode 10.2和Swift 5。 - Heitor

0
  • 视图控制器


  • ViewController: UIViewController, WKUIDelegate, WKNavigationDelegate 
    
    1. 启用JavaScript
    webView.configuration.preferences.javaScriptEnabled = true
    
  • 在ViewDidLoad()函数中添加以下项目
  •    webView.uiDelegate = self
       webView.navigationDelegate = self
       view.addSubview(webView)
    

    4. 将委托添加如下:
        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()
        }
    

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