如何在Xamarin.iOS的AppShell中禁用对应路由的ShellContent按钮
问题描述
- 我的AppShell包含3个
ShellContent,在iOS平台出现异常:当处于某路由的二级或更深层级页面时,点击该路由对应的ShellContent按钮,会自动返回该路由的初始页面;此问题在UWP平台不存在。 - 尝试通过设置
IsEnabled="False"禁用当前路由的ShellContent按钮,但未生效。
AppShell布局截图:
当前AppShell代码:
<TabBar> <ShellContent x:Name="BureauShell" Title="Bureau" ContentTemplate="{DataTemplate local:ViewLogin}" Icon="office-desk.png"/> <ShellContent x:Name="ServicesShell" Title="Services" ContentTemplate="{DataTemplate local:ViewMenu}" Icon="booking.png"/> <ShellContent x:Name="PiecesShell" Title="Batiment" ContentTemplate="{DataTemplate local:ViewChat}" Icon="building.png" /> </TabBar>
解决方案
方法一:拦截iOS默认Tab点击行为(无需禁用按钮)
iOS的Shell默认会在点击当前选中Tab时返回根页面,可通过自定义Shell渲染器拦截该行为:
- 在iOS项目中创建自定义渲染器:
using Microsoft.Maui.Controls.Handlers.Compatibility; using Microsoft.Maui.Controls.Platform.Compatibility; using UIKit; namespace YourAppNamespace.iOS.Renderers; public class CustomShellRenderer : ShellRenderer { protected override IShellTabBarAppearanceTracker CreateTabBarAppearanceTracker() { return new CustomTabBarTracker(); } } public class CustomTabBarTracker : ShellTabBarAppearanceTracker { public override void UpdateLayout(UITabBarController controller) { base.UpdateLayout(controller); if (controller.TabBar.Items == null) return; foreach (var tabItem in controller.TabBar.Items) { tabItem.ValueChanged += (sender, e) => { var currentItem = sender as UITabBarItem; var selectedIndex = Array.IndexOf(controller.TabBar.Items, currentItem); // 若点击的是当前已选中的Tab,阻止默认返回根页面行为 if (controller.SelectedIndex == selectedIndex) { e.Handled = true; } }; } } }
- 在iOS项目的
MauiProgram.cs中注册渲染器:
builder.ConfigureMauiHandlers(handlers => { #if IOS handlers.AddHandler(typeof(Shell), typeof(CustomShellRenderer)); #endif });
方法二:动态禁用当前路由的ShellContent按钮
若需严格禁用按钮,需结合导航监听和渲染器同步状态:
- 在AppShell后台代码添加导航监听:
public partial class AppShell : Shell { public AppShell() { InitializeComponent(); Navigated += (_, __) => UpdateShellContentStates(); } private void UpdateShellContentStates() { // 重置所有按钮状态 BureauShell.IsEnabled = true; ServicesShell.IsEnabled = true; PiecesShell.IsEnabled = true; var currentRoute = CurrentState.Location.OriginalString; // 判断是否处于当前路由的深层页面,禁用对应按钮 if (currentRoute.StartsWith("//Bureau") && currentRoute != "//Bureau") BureauShell.IsEnabled = false; else if (currentRoute.StartsWith("//Services") && currentRoute != "//Services") ServicesShell.IsEnabled = false; else if (currentRoute.StartsWith("//Batiment") && currentRoute != "//Batiment") PiecesShell.IsEnabled = false; } }
- 在自定义渲染器中同步TabBarItem的启用状态:
修改CustomTabBarTracker类,添加状态同步逻辑:
public override void UpdateAppearance(UITabBarController controller, ShellAppearance appearance) { base.UpdateAppearance(controller, appearance); if (controller.TabBar.Items == null || Shell.Current is not AppShell appShell) return; controller.TabBar.Items[0].Enabled = appShell.BureauShell.IsEnabled; controller.TabBar.Items[1].Enabled = appShell.ServicesShell.IsEnabled; controller.TabBar.Items[2].Enabled = appShell.PiecesShell.IsEnabled; }
方法三:通过Shell导航事件阻止返回
直接在AppShell中重写导航事件,拦截当前Tab的返回操作:
public partial class AppShell : Shell { protected override void OnNavigating(ShellNavigatingEventArgs args) { base.OnNavigating(args); // 若点击Tab试图返回当前路由根页面,取消操作 if (args.Source == ShellNavigationSource.TabBar && args.Target.Location.OriginalString == CurrentState.Location.Root.OriginalString) { args.Cancel(); } } }
内容的提问来源于stack exchange,提问作者Antoine21000
相关产品推荐
相关产品推荐

