iOS WKWebView:PostMessage/EvaluateJavascript 中断文件输入

iOS WKWebView: PostMessage/EvaluateJavascript breaks file input

提问人:user3678429 提问时间:9/9/2023 最后编辑:user3678429 更新时间:9/9/2023 访问量:66

问:

我正在开发一个 Xamarin 移动应用程序,其中 iOS 版本使用 WKWebView 作为其 UI。我们的用户输入处理工作原理是在 Web 视图中的应用和框架之间传递 JSON 序列化消息。为了将消息从应用发送到帧,我们使用 EvaluateJavascript 来执行帧中的侦听器可以响应的函数。为了将消息从框架发送到应用程序,我们在框架中设置了一个消息端口,并使用 postMessage API——以这种方式发送的消息由 WKWebView 的 didReceiveScriptMessage 处理程序接收。

这是错误。我们应用程序的一个页面包含一个文件输入元素,即:

<input type="file"/>

iOS file input

这应该会打开一个文件选取器菜单或对话框,供用户从其设备中选择文件。我们发现,在大多数情况下(桌面 Safari/Chrome、移动 Safari/Chrome 和 Android 版本的 Xamarin 应用)可按预期工作,但在 iOS 应用中则不然。似乎发生的事情是,在 iOS 应用程序中,文件输入一直有效,直到我们从 webview 中启动消息循环。之后,任何文件输入元素都将停止响应点击。刷新或导航到另一个页面也无法解决问题 - 此时您似乎必须重置应用程序。

以下是简短的片段。

设置消息端口 (C#):

// add a script message handler when initializing the webview
var config = new WKWebViewConfiguration();
config.UserContentController.AddScriptMessageHandler(new HybridScriptMessageHandler(logger), "AppMessageChannel");
var wkWebView = new WKWebView(UIScreen.MainScreen.Bounds, config);
public void EvaluateJavascript(string script)
{
    MainThread.BeginInvokeOnMainThread(() =>
    {
        Exception handlerEx = null;
        base.EvaluateJavaScript(script, (res, err) =>
        {
            if (err != null)
            {
                handlerEx = new Exception(err.Domain);
            }
        });
    });
}
// evaluate javascript to configure the message port within the webview frame
var script =  @"
    (function () {
        const loadedHandler = function () {
            const event = new CustomEvent('MessagePortReady', { detail: window.webkit.messageHandlers['AppMessageChannel'] });
            window.dispatchEvent(event);
        };  
        loadedHandler();    
        window.addEventListener('DOMContentLoaded', loadedHandler, { once: true });
    })();";

EvaluateJavascript(script);

在框架中,添加使用消息端口 (js) 的侦听器和函数:

window.addEventListener("MessagePortReady", function (e) {
    // set designated message port
    MESSAGE_PORT = e.detail;

    window.addEventListener("WebViewMessage", function (e) {
        const message = JSON.parse(e.data);
        // handle the message
    });
});

function postMessageToApp(message) {
    const messageJson = JSON.stringify(message);
    MESSAGE_PORT.postMessage(messageJson);
}

当从框架中调用函数时,它会在应用中接收,如下所示 (C#):postMessageToApp

public override void DidReceiveScriptMessage(WKUserContentController userContentController, WKScriptMessage message)
{
    Task.Run(() =>
    {
        // dispatch an event to the pertinent UI handler
        OnMessage?.Invoke(this, new MessageEventArgs((NSString)msg.Body));
    });
}

处理完消息后(这可能涉及从设备存储中检索数据、发出 HTTP 请求等),我们再次将响应发布回帧 (C#):EvaluateJavascript

public void PostMessageToUI(string data)
{
    // assume that data is some JSON-serialized response object
    var script = $@"
        (function () {
            const msgEvent = new MessageEvent('WebViewMessage', { 'data': '{data}' });
            window.dispatchEvent(msgEvent);
        })();";

    EvaluateJavascript(script);
}

我试图使这些摘录简明扼要,但如果需要,可以详细说明。谢谢!

xamarin.ios wkwebview postmessage 输入类型文件 评估

评论


答: 暂无答案