Xamarin Forms IOS Secondary ToolbarItem Display

毛勇
2023-12-01

Hey guys! 最近在开发xamarin forms发现iOS系统对Secondary ToolbarItem显示不太友好,在Android上二级菜单是采用Popup Menu的方式展示,在IOS居然是Tab方式占据屏幕上方。在国内很少人用Xamarin 开发ios吧,资源不足后来在国外论坛找到一个解决方法,论坛地址Content Page Secondary Toolbar Items in iOS same as Android — Xamarin Community Forums

非常感谢AmitManchanda外国友人给我们提供这个解决方案 GitHub地址:GitHub - AmitManchanda/iOSSecondaryToolbarMenubar: This is a Xamarin forms based repository in which I created a menu bar in iPhone and iPad toolbar for secondary toolbar items same as android.

OK 开始正文

1.定义一个Secondary ToolbarItem Menu settings interface

public interface ISecondaryToolbarMenuSetting
    {
        
        Color CellBackgroundColor { get; }
        Color CellTextColor { get; }
        Color MenuBackgroundColor { get; }
        float RowHeight { get; }
        Color ShadowColor { get; }
        float ShadowOpacity { get; }
        float ShadowRadius { get; }
        float ShadowOffsetDimension { get; }
        float TableWidth { get; }
    }

2.定义通用ContentPage

    /// <summary>
    /// 需要Secondary toolbarItem的page 请用BaseContentPage
    /// 已做IOS二级菜单适配处理
    /// </summary>
    public class BaseContentPage : ContentPage, ISecondaryToolbarMenuSetting
    {
        public  Color CellBackgroundColor => Color.White;

        public  Color CellTextColor => Color.Black;

        public  Color MenuBackgroundColor => Color.White;

        public  float RowHeight => 50;

        public  Color ShadowColor => Color.Black;

        public  float ShadowOpacity => 0.3f;

        public  float ShadowRadius => 5.0f;

        public  float ShadowOffsetDimension => 5.0f;

        public  float TableWidth => 250;
       
        public BaseContentPage()
        {
        }

        
    }

3.在IOS项目添加一个CustomPageRenderer

using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: Xamarin.Forms.ExportRenderer(typeof(BaseContentPage), typeof(RightToolbarMenuCustomRenderer))]
namespace SGS.SIO.iOS.CustomRender
{
    public class RightToolbarMenuCustomRenderer : PageRenderer
    {
       
        private List<ToolbarItem> _secondaryItems;
        private UITableView _table;
        private UITapGestureRecognizer _tapGestureRecognizer;
        private UIView _transparentView;

        protected override void OnElementChanged(VisualElementChangedEventArgs e)
        {
            if (e.NewElement is BaseContentPage)
            {
                ToolbarItemInit();
            }
            base.OnElementChanged(e);
        }

        
        private void ToolbarItemInit()
        {
            if (Element is ContentPage page)
            {
                
                _secondaryItems = page.ToolbarItems.Where(i => i.Order == ToolbarItemOrder.Secondary).ToList();
                _secondaryItems.ForEach(t => page.ToolbarItems.Remove(t));

                if (_secondaryItems?.Count == 0 && page.ToolbarItems.Any(a => a.StyleId == "options"))
                {
                    page.ToolbarItems.Clear();
                }
                else if (_secondaryItems?.Count >= 1 && !page.ToolbarItems.Any(a => a.StyleId == "options"))
                {
                    page.ToolbarItems.Add(new ToolbarItem()
                    {
                        Order = ToolbarItemOrder.Primary,
                        IconImageSource = "options.png",
                        StyleId = "options",
                        Priority = 1,
                        Command = new Command(ToggleDropDownMenuVisibility)
                    });
                }
            }
        }

        private void ToggleDropDownMenuVisibility()
        {
            if (!IsTableExists())
            {
                if ((View?.Subviews != null)
                    && (View.Subviews.Length > 0)
                    && (View.Bounds != null)
                    && (_secondaryItems != null)
                    && (_secondaryItems.Count > 0))
                {
                    _table = OpenDropDownMenu(Element as ISecondaryToolbarMenuSetting);
                    Add(_table);
                }
            }
            else
                CloseDropDownMenu();
        }

        private bool IsTableExists()
        {
            if (View?.Subviews != null)
            {
                foreach (var subview in View.Subviews)
                {
                    if (_table != null && subview == _table)
                    {
                        return true;
                    }
                }
            }
            if (_tapGestureRecognizer != null)
            {
                _transparentView?.RemoveGestureRecognizer(_tapGestureRecognizer);
                _tapGestureRecognizer = null;
            }
            _table = null;
            _tapGestureRecognizer = null;
            return false;
        }

        private UITableView OpenDropDownMenu(ISecondaryToolbarMenuSetting secondaryMenuSupport)
        {
            _transparentView = _transparentView = new UIView(new CGRect(0, 0, View.Bounds.Width, View.Bounds.Height))
            {
                BackgroundColor = UIColor.FromRGBA(0, 0, 0, 0)
            };
            _tapGestureRecognizer = new UITapGestureRecognizer(CloseDropDownMenu);
            _transparentView.AddGestureRecognizer(_tapGestureRecognizer);
            Add(_transparentView);

            UITableView table = null;
            if (_secondaryItems != null && _secondaryItems.Count > 0)
            {
                table = new UITableView(GetPositionForDropDownMenu(secondaryMenuSupport.RowHeight, secondaryMenuSupport.TableWidth))
                {
                    Source = new TableSource(_secondaryItems, _transparentView, secondaryMenuSupport.RowHeight),
                    ClipsToBounds = false
                };

                table.ScrollEnabled = false;
                table.Layer.ShadowColor = secondaryMenuSupport.ShadowColor.ToCGColor();
                table.Layer.ShadowOpacity = secondaryMenuSupport.ShadowOpacity;
                table.Layer.ShadowRadius = secondaryMenuSupport.ShadowRadius;
                table.Layer.ShadowOffset = new System.Drawing.SizeF(secondaryMenuSupport.ShadowOffsetDimension, secondaryMenuSupport.ShadowOffsetDimension);
                table.BackgroundColor = secondaryMenuSupport.MenuBackgroundColor.ToUIColor();
            }
            return table;
        }

        public override void ViewWillDisappear(bool animated)
        {
            CloseDropDownMenu();
            
            base.ViewWillDisappear(animated);
        }

        private RectangleF GetPositionForDropDownMenu(float rowHeight, float tableWidth)
        {
            if ((View?.Bounds != null)
                && (_secondaryItems != null)
                && (_secondaryItems.Count > 0))
            {
                return new RectangleF(
                    (float)View.Bounds.Width - tableWidth,
                    0,
                    tableWidth,
                    _secondaryItems.Count() * rowHeight);
            }
            else
            {
                return new RectangleF(0.0f, 0.0f, 0.0f, 0.0f);
            }
        }

        private void CloseDropDownMenu()
        {
            if (_table != null)
            {
                if (_tapGestureRecognizer != null)
                {
                    _transparentView?.RemoveGestureRecognizer(_tapGestureRecognizer);
                    _tapGestureRecognizer = null;
                }

                if (View?.Subviews != null)
                {
                    foreach (var subview in View.Subviews)
                    {
                        if (subview == _table)
                        {
                            _table.RemoveFromSuperview();
                            break;
                        }
                    }

                    if (_transparentView != null)
                    {
                        foreach (var subview in View.Subviews)
                        {
                            if (subview == _transparentView)
                            {
                                _transparentView.RemoveFromSuperview();
                                break;
                            }
                        }
                    }
                }
                _table = null;
                _transparentView = null;
            }
        }

        public override void ViewDidLayoutSubviews()
        {
            base.ViewDidLayoutSubviews();

            if (_table != null)
            {
                if (Element is ISecondaryToolbarMenuSetting secondaryMenuSupport)
                    PositionExistingDropDownMenu(secondaryMenuSupport.RowHeight, secondaryMenuSupport.TableWidth);
            }
        }

        private void PositionExistingDropDownMenu(float rowHeight, float tableWidth)
        {
            if ((View?.Bounds != null)
                && (_secondaryItems != null)
                && (_secondaryItems.Count > 0)
                && (_table != null))
            {
                _table.Frame = GetPositionForDropDownMenu(rowHeight, tableWidth);
            }
        }

       
    }
}

4.还有一个TableSource

public class TableSource : UITableViewSource
    {
        List<ToolbarItem> _tableItems;
        string[] _tableItemTexts;
        string CellIdentifier = "TableCell";
        UIView _tableSuperView = null;
        float RowHeight;

        public TableSource(List<ToolbarItem> items, UIView tableSuperView,float rowHeight)
        {
            _tableItems = items;
            _tableSuperView = tableSuperView;
            _tableItemTexts = items.Select(a => a.Text).ToArray();
            RowHeight = rowHeight;
        }

        public override nint RowsInSection(UITableView tableview, nint section)
        {
            return _tableItemTexts?.Length ?? 0;
        }

        public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
        {
            UITableViewCell cell = tableView.DequeueReusableCell(CellIdentifier);
            string item = _tableItemTexts[indexPath.Row];
            if (cell == null)
            { cell = new UITableViewCell(UITableViewCellStyle.Default, CellIdentifier); }
            cell.TextLabel.Text = item;
            return cell;
        }

        public override nfloat GetHeightForRow(UITableView tableView, NSIndexPath indexPath)
        {
            return RowHeight;
        }

        public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
        {
            var command = _tableItems[indexPath.Row].Command;
            if (command != null)
                command.Execute(_tableItems[indexPath.Row].CommandParameter);   
           
            tableView.DeselectRow(indexPath, true);
            tableView.RemoveFromSuperview();
            if (_tableSuperView != null)
            {
                _tableSuperView.RemoveFromSuperview();
            }
        }
    }

到此基本就可以啦。以上代码在Github源码基础上修改一丁点。

 类似资料:

相关阅读

相关文章

相关问答