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

如何在iOS上通过MAUI Handler控制WebView导航时的ActivityIndicator?

MAUI WebView iOS导航状态同步解决方案

问题背景

将Xamarin应用迁移至MAUI时,遇到WebView导航状态同步问题:Android平台上WebView的Navigating/Navigated事件可准确控制ActivityIndicator显示/隐藏,但iOS平台上这两个事件会多次触发,且最后一次Navigating后可能不触发Navigated,导致ActivityIndicator持续显示。

原Xamarin方案通过自定义渲染器绑定WKWebView原生导航事件解决,MAUI中需改用Handler替代自定义渲染器实现相同逻辑。

MAUI实现步骤

1. 跨平台自定义WebView

创建带导航状态变更事件的自定义WebView,用于传递原生事件状态:

namespace MyMauiApp.CustomControls;

public class CustomWebView : WebView
{
    // 导航状态变更事件:true=开始加载,false=加载完成/失败
    public event Action<bool>? NavigateStateChanged;

    internal void OnNavigateStateChanged(bool isLoading)
    {
        NavigateStateChanged?.Invoke(isLoading);
    }
}

2. iOS平台自定义Handler

实现iOS平台的WebViewHandler,替换原生WKWebView的NavigationDelegate,绑定原生导航事件:

using MyMauiApp.CustomControls;
using WebKit;
using Microsoft.Maui.Handlers;

namespace MyMauiApp.Platforms.iOS;

public class CustomWebViewHandler : WebViewHandler
{
    protected override WKWebView CreatePlatformView()
    {
        var webView = base.CreatePlatformView();
        // 替换自定义NavigationDelegate
        webView.NavigationDelegate = new CustomNavigationDelegate(this);
        return webView;
    }

    private class CustomNavigationDelegate : WKNavigationDelegate
    {
        private readonly CustomWebViewHandler _handler;

        public CustomNavigationDelegate(CustomWebViewHandler handler)
        {
            _handler = handler;
        }

        // 页面开始加载时触发
        public override void DidStartProvisionalNavigation(WKWebView webView, WKNavigation navigation)
        {
            if (_handler.VirtualView is CustomWebView customWebView)
            {
                customWebView.OnNavigateStateChanged(true);
            }
        }

        // 页面加载完成时触发
        public override void DidFinishNavigation(WKWebView webView, WKNavigation navigation)
        {
            if (_handler.VirtualView is CustomWebView customWebView)
            {
                customWebView.OnNavigateStateChanged(false);
            }
        }

        // 导航失败时触发
        public override void DidFailNavigation(WKWebView webView, WKNavigation navigation, NSError error)
        {
            if (_handler.VirtualView is CustomWebView customWebView)
            {
                customWebView.OnNavigateStateChanged(false);
            }
        }

        // 初始导航失败时触发(比如DNS解析失败)
        public override void DidFailProvisionalNavigation(WKWebView webView, WKNavigation navigation, NSError error)
        {
            if (_handler.VirtualView is CustomWebView customWebView)
            {
                customWebView.OnNavigateStateChanged(false);
            }
        }
    }
}

3. 注册Handler

在MauiProgram.cs中注册自定义Handler,仅对iOS平台生效:

var builder = MauiApp.CreateBuilder();
builder
    .UseMauiApp<App>()
    .ConfigureFonts(fonts =>
    {
        fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
    })
    .ConfigureMauiHandlers(handlers =>
    {
#if IOS
        handlers.AddHandler<CustomWebView, CustomWebViewHandler>();
#endif
    });

return builder.Build();

4. 页面中使用自定义WebView

在XAML页面中组合CustomWebView和ActivityIndicator,并绑定导航状态事件:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:controls="clr-namespace:MyMauiApp.CustomControls"
             x:Class="MyMauiApp.WebViewPage">
    <RelativeLayout>
        <!-- 自定义WebView -->
        <controls:CustomWebView x:Name="MyWebView"
                                Source="https://example.com"
                                RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width}"
                                RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height}" />
        
        <!-- 加载指示器 -->
        <ActivityIndicator x:Name="LoadingIndicator"
                           IsRunning="False"
                           IsVisible="False"
                           Color="Blue"
                           RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.5, Constant=-25}"
                           RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=0.5, Constant=-25}" />
    </RelativeLayout>
</ContentPage>

后台代码绑定事件控制指示器:

public partial class WebViewPage : ContentPage
{
    public WebViewPage()
    {
        InitializeComponent();
        
        // 绑定导航状态变更事件
        MyWebView.NavigateStateChanged += (isLoading) =>
        {
            LoadingIndicator.IsVisible = isLoading;
            LoadingIndicator.IsRunning = isLoading;
        };
    }
}

更优方案思路

如果不想自定义WebView,也可以直接通过MAUI的WebView Handler扩展,在原生层拦截导航事件,利用附加属性传递状态到ViewModel,实现MVVM模式下的状态控制,避免直接操作UI元素。

内容的提问来源于stack exchange,提问作者MSibalik

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 03:12:36