如何在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
相关产品推荐
相关产品推荐

