You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 23:15:44