Xamarin Forms:iOS 中的内容页面辅助工具栏项与 Android 相同

Posted

技术标签:

【中文标题】Xamarin Forms:iOS 中的内容页面辅助工具栏项与 Android 相同【英文标题】:Xamarin Forms: Content Page Secondary Toolbar Items in iOS same as Android 【发布时间】:2018-05-27 09:06:17 【问题描述】:

我在我的内容页面中使用辅助工具栏项目。我希望 ios 中的菜单项与 android 中的菜单项相同。默认情况下,它显示在导航栏下。我怎样才能做到这一点。

【问题讨论】:

这就是 iOS 中的设计。除非您自定义工具栏,否则我认为没有解决方法。 【参考方案1】:

我找到了解决方案,因为我创建了一个自定义 PageRenderer 类。

public class RightToolbarMenuCustomRenderer : PageRenderer


    //I used UITableView for showing the menulist of secondary toolbar items.
    List<ToolbarItem> _secondaryItems;
    UITableView table;

    protected override void OnElementChanged(VisualElementChangedEventArgs e)
    
        //Get all secondary toolbar items and fill it to the gloabal list variable and remove from the content page.
        if (e.NewElement is ContentPage page)
        
            _secondaryItems = page.ToolbarItems.Where(i => i.Order == ToolbarItemOrder.Secondary).ToList();
            _secondaryItems.ForEach(t => page.ToolbarItems.Remove(t));
        
        base.OnElementChanged(e);
    

    public override void ViewWillAppear(bool animated)
    
        var element = (ContentPage)Element;
        //If global secondary toolbar items are not null, I created and added a primary toolbar item with image(Overflow) I         
                // want to show.
        if (_secondaryItems != null && _secondaryItems.Count > 0)
        
            element.ToolbarItems.Add(new ToolbarItem()
            
                Order = ToolbarItemOrder.Primary,
                Icon = "more.png",
                Priority = 1,
                Command = new Command(() =>
                
                    ToolClicked();
                )
            );
        
        base.ViewWillAppear(animated);
    

    //Create a table instance and added it to the view.
    private void ToolClicked()
    
        if (table == null)
        
            //Set the table position to right side. and set height to the content height.
            var childRect = new RectangleF((float)View.Bounds.Width - 250, 0, 250, _secondaryItems.Count() * 56);
            table = new UITableView(childRect)
            
                Source = new TableSource(_secondaryItems) // Created Table Source Class as Mentioned in the 
                                //Xamarin.iOS   Official site
            ;
            Add(table);
            return;
        
        foreach (var subview in View.Subviews)
        
            if(subview == table)
            
                table.RemoveFromSuperview();
                return;
            
        
        Add(table);
       

表源类继承UITableViewSource

public class TableSource : UITableViewSource


       // Global variable for the secondary toolbar items and text to display in table row
    List<ToolbarItem> _tableItems;
    string[] _tableItemTexts;
    string CellIdentifier = "TableCell";

    public TableSource(List<ToolbarItem> items)
    
            //Set the secondary toolbar items to global variables and get all text values from the toolbar items
        _tableItems = items;
        _tableItemTexts = items.Select(a => a.Text).ToArray();
    

    public override nint RowsInSection(UITableView tableview, nint section)
    
        return _tableItemTexts.Length;
    

    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 56; // Set default row height.
    

    public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
    
        //Used command to excute and deselct the row and removed the table.
        var command = _tableItems[0].Command;
        command.Execute(_tableItems[0].CommandParameter);
        tableView.DeselectRow(indexPath, true);
        tableView.RemoveFromSuperview();
    

在视图模型中我有

public ICommand CancelCommand  get; set; 

//In constructor of ViewModel I Created Event for Command 
CancelCommand = new Command(async () => await Cancel());

private async Task Cancel()

    await Task.CompletedTask;

在内容页面 Xaml 中,我将工具栏项用作

<ContentPage.ToolbarItems>
    <ToolbarItem Text ="Cancel" Priority="1" Order="Secondary" Command="Binding CancelCommand"/>
</ContentPage.ToolbarItems>

并且解决方案的输出与我想要的相同。

快乐的结局:)。请让我知道我是否可以使用更好的方法。准备学习。

【讨论】:

这里唯一的问题是用户必须点击工具来关闭它。你知道如果用户在弹出窗口之外点击我们是否可以关闭它?【参考方案2】:

对 Amit Manchanda 答案的一些更改:

Table Source 类继承 UITableViewSource

public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
        
            //Used command to excute and deselct the row and removed the table.
            var command = _tableItems[**indexPath.Row**].Command;
            command.Execute(_tableItems[**indexPath.Row**].CommandParameter);
            tableView.DeselectRow(indexPath, true);
            tableView.RemoveFromSuperview();
        

自定义 PageRenderer 类。

if (_secondaryItems != null && _secondaryItems.Count > 0 **&& element.ToolbarItems.FirstOrDefault(e => e.ClassId == "menu") == null**)
            
                element.ToolbarItems.Add(new ToolbarItem()
                
                    Order = ToolbarItemOrder.Primary,
                    Icon = "more.png",
                    Priority = 1,
                    Command = new Command(() =>
                    
                        ToolClicked();
                    )**,
                    ClassId = "menu"**
                );
            

【讨论】:

【参考方案3】:

非常感谢@Amit Manchanda 和@Igor Tsapko。这对我来说效果很好。

我和@BatMaci 有同样的问题。以下是我使用 MessagingCenter 解决该问题的方法:

在 RightToolbarMenuCustomRenderer 构造函数中,我订阅了来自 MessagingCenter 的消息:

public RightToolbarMenuCustomRenderer()

    MessagingCenter.Subscribe<BaseViewModel>(this, AppConstants.str_iOS_TAP_OUTSIDE_CUSTOM_TOOLBAR, CloseRenderedControl);


public void CloseRenderedControl(object sender)

    if (table != null)
        ToolClicked(); // same as the ToolClicked() method Amit posted

然后在我的页面的标记中,我添加了一个带有 TapGestureRecognizer 的 ContentView,它包装了视图的内容,并绑定到了一个命令。

<ContentView>
    <ContentView.GestureRecognizers>
        <TapGestureRecognizer Command="Binding OnTappedCommand" />
    </ContentView.GestureRecognizers>
    <StackLayout>
    <ListView .....

该命令是我的 BaseViewModel 上的 ICommand,它在该 VM 的构造函数中初始化,以执行我的 OnTappedMethod:

public BaseViewModel()

    OnTappedCommand = new Command(OnTappedMethod);


public ICommand OnTappedCommand  protected set; get; 

public void OnTappedMethod()

    if(Device.RuntimePlatform == Device.iOS)
        MessagingCenter.Send<BaseViewModel>(this, AppConstants.str_iOS_TAP_OUTSIDE_CUSTOM_TOOLBAR);

这几乎只是模拟单击自定义控件再次关闭。我想发布这个来节省一些时间。我应该指出我是 Xamarin 的新手,并且只在一台 iOS 设备(运行 12.4.1 的 iPhone 7)上进行了测试。

【讨论】:

以上是关于Xamarin Forms:iOS 中的内容页面辅助工具栏项与 Android 相同的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Xamarin Forms iOS 中的特定页面上导航?

在 Xamarin.forms 中的多个页面之间导航?

从Xamarin Forms中的iOS中的App.OnSleep弹出导航堆栈

Xamarin Forms - iOS - 后退按钮标题

Xamarin.Forms TabbedPage 错误

xamarin.forms.shell 框架中页面上视觉元素的生命周期