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

如何在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);
        }
    }
}

代码说明

  1. 渲染器绑定:通过ExportRenderer属性把自定义渲染器绑定到NavigationPage,所有使用NavigationPage的页面都会自动应用这个逻辑。
  2. 动态更新逻辑:监听ToolbarItems的变化,每次增删项都会重新整理主/次级按钮,保证导航栏状态实时同步。
  3. 交互逻辑保留:无论是主项还是次级项,点击后都会触发原ToolbarItem的Command或Clicked事件,完全不影响原有业务逻辑。
  4. 多设备适配:自动区分iPhone和iPad,在平板上会把菜单以弹出框形式展示,避免布局错位。

注意事项

  • 替换代码里的YourAppName为你实际的应用命名空间。
  • 如果ToolbarItem使用了图标,确保iOS项目里的资源文件路径正确,UIImage.FromBundle能正常加载到资源。
  • 测试时可以动态增删ToolbarItem,验证实时更新的效果是否正常。

这样实现后,iOS端的次级ToolbarItem就会和Android端完全一致,折叠在“更多”按钮里,点击展开下拉菜单,完美符合你的需求!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:26:53