如何在iOS端复刻Xamarin.Forms的Android风格Secondary ToolbarItem下拉“更多”按钮
实现iOS端Xamarin.Forms次级ToolbarItem的Android风格下拉效果
我完全懂你的需求——就是要让iOS端设置了Order = ToolbarItemOrder.Secondary的ToolbarItem,像Android那样折叠在“更多”按钮里,点击才下拉展示,而不是默认在导航栏下方多出一排工具栏对吧?Xamarin.Forms默认iOS没有这个行为,得靠自定义渲染器来搞定,下面给你具体的实现方案:
核心思路
我们要自定义NavigationRenderer,把所有次级ToolbarItem从默认位置移除,替换成一个系统样式的“更多”按钮,点击这个按钮时弹出ActionSheet(iPad上自动适配为弹出框)来展示所有次级项,完全模拟Android的交互逻辑。
具体代码实现
在你的iOS项目里创建一个自定义渲染器类:
using Xamarin.Forms; using Xamarin.Forms.Platform.iOS; using UIKit; using System.Collections.Generic; using System.Linq; [assembly: ExportRenderer(typeof(NavigationPage), typeof(YourAppName.iOS.Renderers.CustomNavigationRenderer))] namespace YourAppName.iOS.Renderers { public class CustomNavigationRenderer : NavigationRenderer { private UIBarButtonItem _moreButton; private List<ToolbarItem> _secondaryToolbarItems; protected override void OnElementChanged(VisualElementChangedEventArgs e) { base.OnElementChanged(e); if (e.NewElement != null) { // 初始化工具栏布局 UpdateNavigationToolbar(); // 监听ToolbarItems变化,实时更新导航栏 ((NavigationPage)e.NewElement).ToolbarItems.CollectionChanged += ToolbarItems_CollectionChanged; } if (e.OldElement != null) { // 移除监听避免内存泄漏 ((NavigationPage)e.OldElement).ToolbarItems.CollectionChanged -= ToolbarItems_CollectionChanged; } } private void ToolbarItems_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { UpdateNavigationToolbar(); } private void UpdateNavigationToolbar() { if (Element == null || NavigationBar == null) return; // 区分主项和次级ToolbarItem var primaryItems = Element.ToolbarItems.Where(item => item.Order == ToolbarItemOrder.Primary).ToList(); _secondaryToolbarItems = Element.ToolbarItems.Where(item => item.Order == ToolbarItemOrder.Secondary).ToList(); // 清空现有右侧按钮 NavigationBar.TopItem.RightBarButtonItems = null; // 生成主项对应的BarButtonItem var primaryButtons = primaryItems.Select(CreatePrimaryBarButton).ToList(); // 如果有次级项,添加“更多”按钮 if (_secondaryToolbarItems.Any()) { _moreButton = new UIBarButtonItem(UIBarButtonSystemItem.More, (sender, args) => ShowSecondaryMenu()); primaryButtons.Add(_moreButton); } // 把按钮设置到导航栏右侧 NavigationBar.TopItem.RightBarButtonItems = primaryButtons.ToArray(); } private UIBarButtonItem CreatePrimaryBarButton(ToolbarItem toolbarItem) { var barButton = new UIBarButtonItem(toolbarItem.Text, UIBarButtonItemStyle.Plain, (sender, args) => { // 触发原ToolbarItem的命令或点击事件 if (toolbarItem.Command != null && toolbarItem.Command.CanExecute(toolbarItem.CommandParameter)) { toolbarItem.Command.Execute(toolbarItem.CommandParameter); } else if (toolbarItem.Clicked != null) { toolbarItem.Clicked.Invoke(this, System.EventArgs.Empty); } }); // 如果ToolbarItem有图标,设置到按钮上 if (!string.IsNullOrEmpty(toolbarItem.Icon)) { barButton.Image = UIImage.FromBundle(toolbarItem.Icon); } return barButton; } private void ShowSecondaryMenu() { // 创建ActionSheet展示次级项 var actionSheet = UIAlertController.Create(null, null, UIAlertControllerStyle.ActionSheet); foreach (var item in _secondaryToolbarItems) { actionSheet.AddAction(UIAlertAction.Create(item.Text, UIAlertActionStyle.Default, (action) => { // 触发次级项的命令或点击事件 if (item.Command != null && item.Command.CanExecute(item.CommandParameter)) { item.Command.Execute(item.CommandParameter); } else if (item.Clicked != null) { item.Clicked.Invoke(this, System.EventArgs.Empty); } })); } // 添加取消按钮 actionSheet.AddAction(UIAlertAction.Create("Cancel", UIAlertActionStyle.Cancel, null)); // 处理iPad上的弹出位置,绑定到“更多”按钮 if (UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Pad) { actionSheet.PopoverPresentationController.BarButtonItem = _moreButton; } // 弹出菜单 PresentViewController(actionSheet, true, null); } } }
代码说明
- 渲染器绑定:通过
ExportRenderer属性把自定义渲染器绑定到NavigationPage,所有使用NavigationPage的页面都会自动应用这个逻辑。 - 动态更新逻辑:监听
ToolbarItems的变化,每次增删项都会重新整理主/次级按钮,保证导航栏状态实时同步。 - 交互逻辑保留:无论是主项还是次级项,点击后都会触发原ToolbarItem的
Command或Clicked事件,完全不影响原有业务逻辑。 - 多设备适配:自动区分iPhone和iPad,在平板上会把菜单以弹出框形式展示,避免布局错位。
注意事项
- 替换代码里的
YourAppName为你实际的应用命名空间。 - 如果ToolbarItem使用了图标,确保iOS项目里的资源文件路径正确,
UIImage.FromBundle能正常加载到资源。 - 测试时可以动态增删ToolbarItem,验证实时更新的效果是否正常。
这样实现后,iOS端的次级ToolbarItem就会和Android端完全一致,折叠在“更多”按钮里,点击展开下拉菜单,完美符合你的需求!
内容的提问来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

