Xamarin iOS HybridWebView在iOS15+ iPad上无法执行JavaScript文件
问题:iOS15+ iPad上HybridWebView无法执行JavaScript文件
问题详情
我开发了一款基于HybridWebView的跨平台应用,用于展示本地HTML文件。wwwroot文件夹包含HTML、CSS、JS等资源,已设置为BundleResource,同时使用EmbedIO启动本地Web服务器。
应用在以下环境可正常运行:
- iOS15+ 的iPhone设备
- iOS12 的iPad设备
- 各设备的Safari浏览器
但仅在iOS15+的iPad上无法执行JavaScript文件。
已尝试的解决操作:
- 在
info.plist中添加NSAppTransportSecurity并设置NSAllowsArbitraryLoads = true - 开发Swift版WebView应用测试,同样无法执行JavaScript(已配置JS相关偏好)
补充说明:index.html仅加载一个JS文件,该文件会进一步加载其他JS文件,此逻辑在所有正常运行的设备上均有效。目前疑问是为何仅iOS15+ iPad出现该问题,怀疑需要开启JavaScript但未找到对应配置方案。
相关代码文件
MainPage.xaml
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:d="http://xamarin.com/schemas/2014/forms/design" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" xmlns:extensions="clr-namespace:Viewer.Extensions;assembly=Viewer" x:Class="Viewer.MainPage"> <StackLayout> <extensions:HybridWebView x:Name="HybridWebView" Uri="{Binding WebViewSource}" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" /> </StackLayout> </ContentPage>
MainPage.xaml.cs
namespace Viewer { [DesignTimeVisible(false)] [XamlCompilation(XamlCompilationOptions.Compile)] public partial class MainPage : ContentPage { private readonly LocalWebServer _server = new LocalWebServer(); private string _webViewSource; public string WebViewSource { get => _webViewSource; set { _webViewSource = value; OnPropertyChanged(nameof(WebViewSource)); } } public MainPage() { InitializeComponent(); BindingContext = this; NavigationPage.SetHasNavigationBar(this, false); HybridWebView.RegisterAction(data => { DisplayAlert("Alert", "Hello " + data, "OK"); }); HybridWebView.RegisterQRAction(() => { try { ZXingScannerPage scanPage = new ZXingScannerPage(); scanPage.OnScanResult += (result) => { scanPage.IsScanning = false; Device.BeginInvokeOnMainThread(async () => { await Navigation.PopAsync(); var barcode = result.Text.ParseBarcode(); switch (barcode.BarcodeType) { case BarcodeType.Hotspot: { await HybridWebView.EvaluateJavaScriptAsync( $"javascript:doLoadHS('{barcode.Datamodule}.html', '{barcode.Hotspot.figureId}', '{barcode.Hotspot.hotspotId}');"); break; } case BarcodeType.Datamodule: default: { await HybridWebView.EvaluateJavaScriptAsync( $"javascript:doLoad('{barcode.Datamodule}.html');"); break; } } }); }; Device.BeginInvokeOnMainThread(() => { Navigation.PushAsync(scanPage); }); } catch (Exception ex) { DisplayAlert("QR", $"Error while reading qr code: {ex.Message}", "OK"); } }); HybridWebView.RegisterProjectSelectionAction(() => { _server.Dispose(); Navigation.PopToRootAsync(); }); var docpath = Helper.PathAddBackslash(Path.Combine(DependencyService.Get<IApplicationConfigurationService>().DocumentationRootPath, Init.NAME_DIR_WWWROOT)); _server.StartWebServer(docpath, false, false); WebViewSource = $"{LocalWebServer.Url}/index.html"; NavigationPage.SetHasBackButton(this, false); } protected override bool OnBackButtonPressed() { return true; } } }
HybridWebView.cs
using System; using Xamarin.Forms; namespace Viewer.Extensions { public class HybridWebView : WebView { Action<string> action; Action qrAction; Action projectSelectionAction; public static readonly BindableProperty UriProperty = BindableProperty.Create( propertyName: "Uri", returnType: typeof(string), declaringType: typeof(HybridWebView), defaultValue: default(string)); public string Uri { get { return (string)GetValue(UriProperty); } set { SetValue(UriProperty, value); } } public void RegisterAction(Action<string> callback) { action = callback; } public void RegisterQRAction(Action callback) { qrAction = callback; } public void RegisterProjectSelectionAction(Action callback) { projectSelectionAction = callback; } public void Cleanup() { action = null; qrAction = null; projectSelectionAction = null; } public void InvokeAction(string data) { if (action == null || data == null) { return; } action.Invoke(data); } public void InvokeQRAction() { qrAction?.Invoke(); } public void InvokeProjectSelectionAction() { projectSelectionAction?.Invoke(); } } }
HybridWebViewRenderer.cs
[assembly: ExportRenderer(typeof(HybridWebView), typeof(HybridWebViewRenderer))] namespace Viewer.iOS.Views { public class HybridWebViewRenderer : WkWebViewRenderer, IWKScriptMessageHandler { const string JavaScriptFunction = "function invokeCSharpAction(data){window.webkit.messageHandlers.invokeAction.postMessage(data);}" + "function invokeCSharpQRAction(data){window.webkit.messageHandlers.invokeQRAction.postMessage(data);}"; WKUserContentController userController; public HybridWebViewRenderer() : this(new WKWebViewConfiguration()) { } public HybridWebViewRenderer(WKWebViewConfiguration config) : base(config) { userController = config.UserContentController; var script = new WKUserScript(new NSString(JavaScriptFunction), WKUserScriptInjectionTime.AtDocumentEnd, false); userController.AddUserScript(script); userController.AddScriptMessageHandler(this, "invokeAction"); userController.AddScriptMessageHandler(this, "invokeQRAction"); } protected override void OnElementChanged(VisualElementChangedEventArgs e) { base.OnElementChanged(e); if (e.OldElement != null) { userController.RemoveAllUserScripts(); userController.RemoveScriptMessageHandler("invokeAction"); userController.RemoveScriptMessageHandler("invokeQRAction"); HybridWebView hybridWebView = e.OldElement as HybridWebView; hybridWebView.Cleanup(); } if (e.NewElement != null) { LoadRequest(new NSUrlRequest(new NSUrl(((HybridWebView)Element).Uri))); } } public void DidReceiveScriptMessage(WKUserContentController userContentController, WKScriptMessage message) { var eventArgs = message.Body.ToString().ParseEventArgs(); switch (eventArgs.name) { case "invokeAction": ((HybridWebView)Element).InvokeAction(eventArgs.payload); break; case "invokeQRAction": ((HybridWebView)Element).InvokeQRAction(); break; } } } }
内容的提问来源于stack exchange,提问作者mehmet_demir
相关产品推荐
相关产品推荐

