Content Page Secondary Toolbar Items in iOS same as Android

AmitManchandaAmitManchanda USMember ✭✭
edited December 2017 in Xamarin.Forms

I am using secondary toolbar items in my content page. I want the same menu items in iOS as in android. By default it is showing under navigation bar. How can I achieve this.

Best Answer

«1

Answers

  • AmitManchandaAmitManchanda USMember ✭✭

    Thanks for your reply @Charwaka, It seems like adding dynamic toolbar items to content page but I need secondary toolbar items as mentioned in screenshot(Just like android secondary toolbar items).

  • CharwakaCharwaka INMember ✭✭✭✭✭
    edited December 2017

    @AmitManchanda said:
    Thanks for your reply @Charwaka, It seems like adding dynamic toolbar items to content page but I need secondary toolbar items as mentioned in screenshot(Just like android secondary toolbar items). download sample in that answer

    yes this achieves exactly what you want see this

  • AmitManchandaAmitManchanda USMember ✭✭

    @Charwaka This is in android which is by default for secondary toolbar. I need exactly the same for iOS.

  • AmitManchandaAmitManchanda USMember ✭✭

    Hey @Charwaka, This is also not what I was looking for. I tried it but this is also for the bottom toolbar items and It simply displays toolbar items from top to bottom. I need to show drop down list of items. (*Just like an Android)

  • Really, really cool! Worked like a charm for me. 12 Thumbs up!

    I'd like to add two informations that might be useful for other users:

    1. The renderer class or rather the namespace declaration in it's file must be decorated with the ExportRenderer-Attribute as described in "Customizing a ContentPage" in the Xamarin docs. (developer.xamarin.com/guides/xamarin-forms/application-fundamentals/custom-renderer/contentpage/, sorry cannot post links as a newbie here) At least it didn't work for me without.
    2. In the RowSelected method you should refer to the indexPath's row. Your code only works for menus with one entry or will always call the first entry's command.

          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();
          }
      
  • pändaloupepändaloupe Member
    edited January 2018

    Oh, and if it's part of a navigation stack the Tool-Button will be added each time you return to the page. So you may want to introduce a class variable to prevent the new ToolbarItem from being added twice. Here's how I solved this.

    // in the class's variable section
    private bool _isRendered;
    
    // in the ViewWillAppear method
    if (_secondaryItems != null && _secondaryItems.Count > 0 && !_isRendered) // <-- here
    {
       element.ToolbarItems.Add(new ToolbarItem
       {
          Order = ToolbarItemOrder.Primary,
          Icon = "more.png",
          Priority = 1,
          Command = new Command(OpenSecondaryMenu)
       });
    }
    
    _isRendered = true; // <-- and here
    base.ViewWillAppear(animated);
    

    There may be more elegant solutions like checking the ToolbarItems collection for the "more" entry. That is just what i came up with...

  • AmitManchandaAmitManchanda USMember ✭✭
    edited January 2018

    @pändaloupe Thanks for the pick, I missed to add ExportRenderer in the post, Also Yes It is good to have a NsIndexPath.Row for all rows. Also yes you are right in case of multiple tool button adds.

    Also You can do some more UI changes to look like a menu as updating the tool clicked

                private void ToolClicked()
                {
                    if (table == null)
                    {
                        var childRect = new RectangleF((float)View.Bounds.Width - 250, 7, 250, _secondaryItems.Count() * 56);
                        table = new UITableView(childRect)
                        {
                            Source = new TableSource(_secondaryItems),
                            ClipsToBounds = false
                        };
                        table.Layer.ShadowColor = UIColor.Black.CGColor;
                        table.Layer.ShadowOpacity = 0.3f;
                        table.Layer.ShadowRadius = 5.0f;
                        table.Layer.ShadowOffset = new System.Drawing.SizeF(5f, 5f);
                        table.BackgroundColor = UIColor.White;
                        Add(table);
                        return;
                    }
                    foreach (var subview in View.Subviews)
                    {
                        if(subview == table)
                        {
                            table.RemoveFromSuperview();
                            return;
                        }
                    }
                    Add(table);
                }
    
  • sismailsismail AEMember ✭✭
    edited February 2018

    @AmitManchanda I am getting error on this line. if (e.NewElement is ContentPage page)
    can you please tell why it is coming.

    Error is gone when you do like this
    var page = e.NewElement as Xamarin.Forms.Page;

  • AmitManchandaAmitManchanda USMember ✭✭
    edited February 2018

    @sismail I think you are missing this

    **[assembly: Xamarin.Forms.ExportRenderer(typeof(ContentPage), typeof(RightToolbarMenuCustomRenderer))]**
    namespace Test
    {
    public class RightToolbarMenuCustomRenderer : PageRenderer
    {
    //your code here
    }
    }
    

    So just make export renderer type to content page. Hope this will help you. Don't forget to like. :smile:

  • sismailsismail AEMember ✭✭
    edited February 2018

    @AmitManchanda i did this. but in my case there was no Binding.
    <ContentPage.ToolbarItems BarBackgroundColor="#1c3f95" BarTextColor="White">

    </ContentPage.ToolbarItems>
    This case how to do without command

      var command = _tableItems[indexPath.Row].Command;  is null in row selected
    
  • AmitManchandaAmitManchanda USMember ✭✭

    @sismail What do you want to do, as without toolbar item why are you using empty

    <ContentPage.ToolbarItems BarBackgroundColor="#1c3f95" BarTextColor="White">
    
    </ContentPage.ToolbarItems>
    

    And also if you are adding dynamic items to it then without any command, it will not perform any action.
    So you can add a null check and place dependent code into that check region.

    In case you want to perform command action from view model (code behind) you will need to specify the command property of toolbar items as below

    ContentPage.ToolbarItems.Add(new ToolbarItem()
                        {
                            Text = "Edit",
                            Order = ToolbarItemOrder.Secondary,
                            Priority = 0,
                            Command = new Command(() =>
                            {
                                //your method(action) reference
                            })
                        });
    
  • sismailsismail AEMember ✭✭

    @AmitManchanda
    thanks for your reply. Since my model was not working, what i did was make it primary with logo in ios and secondary in android. So with only two toolbar menu home and logout, this way it works.

    <ToolbarItem.Order>

    </ToolbarItem.Order>

  • JohnHardmanJohnHardman GBUniversity mod

    @AmitManchanda - As well as the null check mentioned above, it appears that the code as shown above does not handle situations where the toolbar is populated in OnAppearing(). Am looking into it, but do you have any immediate thoughts on how to handle that situation?

  • JohnHardmanJohnHardman GBUniversity mod

    @AmitManchanda - Another cosmetic tweak - when the new dropdown menu is displayed in portrait orientation, if the user rotates the device to landscape orientation, the x coordination of the dropdown is not recalculated, leaving the dropdown in the wrong position for landscape orientation.

  • AmitManchandaAmitManchanda USMember ✭✭

    @JohnHardman I have just changed some code to fix that issue, Please implement it in your renderer
    Make this code out of table null check

    var childRect = new RectangleF((float)View.Bounds.Width - 250, 7, 250, _secondaryItems.Count() * 56);

    and Add

    table.Frame = childRect;

    after if condition. Now your Table view is responsive according to the display size.

  • JohnHardmanJohnHardman GBUniversity mod

    @AmitManchanda - Many thanks for such quick response. With that change, a newly opened menu will be in the right position, but won't it still be in the wrong place if the user rotates the device when the menu is already open?

    One more thing (sorry to be a pain) - when the menu is open, if the user taps outside the menu the menu does not close. Any immediate thoughts on how to implement that? If not, I'll investigate when I have time. Thanks again.

  • JohnHardmanJohnHardman GBUniversity mod

    @AmitManchanda - To get the dropdown menu to re-position when the orientation changes basically needs that same childRect/table.Frame code in public override void ViewDidLayoutSubviews()

  • AmitManchandaAmitManchanda USMember ✭✭
    @JohnHardman Thanks for resolving that issue. It is really good to work on my own code with you. It is never be a pain for me to work. I will soon update this code with all of the changes. Is there any other thing you found, on which I can give more thoughts. Please share.

    :smile:
  • JohnHardmanJohnHardman GBUniversity mod

    @AmitManchanda - The only other thing I have spotted is to remove the use of hard-coded colors, sizes etc., or at least allow them to be overridden.

  • AmitManchandaAmitManchanda USMember ✭✭
    @JohnHardman Okay, that's really cool. I will try to work on it and implement it asap.
  • JohnHardmanJohnHardman GBUniversity mod
    edited February 2018

    @AmitManchanda - I've done some work on this today. It's not finished, and it's designed for integration into what I am doing rather than being completely generic, but in case any of the code is of use to you, I've copied it below. It's not fully tested, and I haven't checked for memory leaks yet, so buyer beware :-)

    First the iOS PageRenderer (don't forget to export it):

        public interface ISecondaryMenuAsDropDown
        {
            void RemoveDropDown();
    
        } // public interface ISecondaryMenuAsDropDown
    
        public class MyAppPageRenderer : PageRenderer, ISecondaryMenuAsDropDown // inherits IDisposable
        {
            private UITableView _dropdownMenuAsTable;  // The dropdown menu that we create
            private ToolbarItem _moreItemsToolbarItem; // "More" button, placed on primary toolbar
            private List<ToolbarItem> _secondaryItems; // Cache of what were defined as secondary toolbar items, but which we show in a dropdown on iOS
            private UITapGestureRecognizer _tapGestureRecognizer; // Listens for taps outside dropdown menu area (although not on navigation bar)
            private EventHandler _toolbarItemsUpdated; // Event that is triggered by the corresponding ContentPage to tell the renderer to update our fake secondary toolbar
            private UIView _transparentView;           // Layer that covers page to allow detection of taps outside dropdown menu area
    
            public MyAppPageRenderer()
            {
                System.Diagnostics.Debug.WriteLine("MyAppPageRenderer: Constructor " + this.GetType());
            }
    
            ~MyAppPageRenderer()
            {
                System.Diagnostics.Debug.WriteLine("MyAppPageRenderer: Destructor " + this.GetType());
            }
    
            protected override void OnElementChanged(VisualElementChangedEventArgs e)
            {
                System.Diagnostics.Debug.WriteLine("MyAppPageRenderer: OnElementChanged " + Element.GetType());
    
                InsightsWrapper.SafeSynchronousBlock(() =>
                {
                    base.OnElementChanged(e);
    
                    if (e.NewElement == null)
                    {
                        ISecondaryMenuSupport secondaryMenuSupport = Element as ISecondaryMenuSupport;
                        if (secondaryMenuSupport != null)
                        {
                            System.Diagnostics.Debug.WriteLine("MyAppPageRenderer: NewElement == null");
                            InitiateTearDown(this, new EventArgs());
                        }
                    }
                }, true);
            }
    
            public override void ViewDidLayoutSubviews()
            {
                System.Diagnostics.Debug.WriteLine("MyAppPageRenderer: ViewDidLayoutSubviews " + Element.GetType());
    
                InsightsWrapper.SafeSynchronousBlock(() =>
                {
                    base.ViewDidLayoutSubviews();
    
                    if (_dropdownMenuAsTable != null)
                    {
                        ISecondaryMenuSupport secondaryMenuSupport = Element as ISecondaryMenuSupport;
                        if (secondaryMenuSupport != null)
                        {
                            PositionExistingDropDownMenu(
                                secondaryMenuSupport.RowHeight, 
                                secondaryMenuSupport.TableWidth);
                        }
                    }
                }, true);
            }
    
            public override void ViewWillAppear(bool animated)
            {
                System.Diagnostics.Debug.WriteLine("PAGERENDERER: ViewWillAppear " + Element.GetType());
    
                InsightsWrapper.SafeSynchronousBlock(() =>
                {
                    base.ViewWillAppear(animated);
    
                    if (Element is ISecondaryMenuSupport)
                    {
                        RelocateSecondaryMenuItemsToDropDownCache(Element as ISecondaryMenuSupport);
                        AddOrRemoveMoreToolbarItemsButton(Element as ContentPage);
                    }
    
                }, true);
            }
    
            private void AddOrRemoveMoreToolbarItemsButton(ContentPage contentPage)
            {
                if ((_secondaryItems != null) && (_secondaryItems.Count > 0))
                {
                    if (_moreItemsToolbarItem == null)
                    {
                        _moreItemsToolbarItem = CreateMoreItemsPrimaryToolbarItem();
                        contentPage.ToolbarItems.Add(_moreItemsToolbarItem);
                    }
                    return;
                }
    
                if (_moreItemsToolbarItem != null)
                {
                    contentPage.ToolbarItems.Remove(_moreItemsToolbarItem);
                    _moreItemsToolbarItem = null;
                }
            }
    
            private void CloseDropDownMenu()
            {
                if (_dropdownMenuAsTable != null)
                {
                    if (_tapGestureRecognizer != null)
                    {
                        _transparentView?.RemoveGestureRecognizer(_tapGestureRecognizer);
                        _tapGestureRecognizer = null;
                    }
    
                    if (View?.Subviews != null)
                    {
                        foreach (var subview in View.Subviews)
                        {
                            if (subview == _dropdownMenuAsTable)
                            {
                                _dropdownMenuAsTable.RemoveFromSuperview();
                                break;
                            }
                        }
    
                        if (_transparentView != null)
                        {
                            foreach (var subview in View.Subviews)
                            {
                                if (subview == _transparentView)
                                {
                                    _transparentView.RemoveFromSuperview();
                                    break;
                                }
                            }
                        }
                    }
    
                    _dropdownMenuAsTable = null;
                    _transparentView = null;
                }
            }
    
            private ToolbarItem CreateMoreItemsPrimaryToolbarItem()
            {
                return new ToolbarItem
                {
                    AutomationId = "MoreItem",
                    Order = ToolbarItemOrder.Primary,
                    Icon = "more.png",
                    Priority = 1,
                    Command = new Command(ToggleDropDownMenuVisibility)
                };
            }
    
            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 InitiateTearDown(object sender, EventArgs eventArgs)
            {
                CloseDropDownMenu();
    
                if (_moreItemsToolbarItem != null)
                {
                    _moreItemsToolbarItem.Command = null;
                    _moreItemsToolbarItem.CommandParameter = null;
                    _moreItemsToolbarItem = null;
                }
                if (_secondaryItems != null)
                {
                    _secondaryItems.Clear();
                    _secondaryItems = null;
                }
                _dropdownMenuAsTable = null;
                _toolbarItemsUpdated = null;
            }
    
             private void OnToolbarItemsUpdated(object sender, EventArgs eventArgs)
            {
                System.Diagnostics.Debug.WriteLine("PAGERENDERER: OnToolbarItemsUpdated " + Element.GetType());
    
                ContentPage contentPage = sender as ContentPage;
    
                if (Element is ISecondaryMenuSupport)
                {
                    RelocateSecondaryMenuItemsToDropDownCache(contentPage as ISecondaryMenuSupport);
                    AddOrRemoveMoreToolbarItemsButton(contentPage);
                }
            }
    
            private UITableView OpenDropDownMenu(ISecondaryMenuSupport 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 ((secondaryMenuSupport != null) && (_secondaryItems != null) && (_secondaryItems.Count > 0))
                {
                    table = new UITableView(GetPositionForDropDownMenu(
                        secondaryMenuSupport.RowHeight,
                        secondaryMenuSupport.TableWidth))
                    {
                        Source = new TableSource(
                            secondaryMenuSupport,
                            this,
                            _secondaryItems),
                        ClipsToBounds = false,
                    };
    
                    table.Layer.ShadowColor = secondaryMenuSupport.ShadowColor.ToCGColor();
                    table.Layer.ShadowOpacity = secondaryMenuSupport.ShadowOpacity;
                    table.Layer.ShadowRadius = secondaryMenuSupport.ShadowRadius;
                    table.Layer.ShadowOffset = new SizeF(
                        secondaryMenuSupport.ShadowOffsetDimension,
                        secondaryMenuSupport.ShadowOffsetDimension);
                    table.BackgroundColor = secondaryMenuSupport.MenuBackgroundColor.ToUIColor();
                }
    
                return table;
            }
    
            private void PositionExistingDropDownMenu(float rowHeight, float tableWidth)
            {
                if ((View?.Bounds != null)
                    && (_secondaryItems != null)
                    && (_secondaryItems.Count > 0)
                    && (_dropdownMenuAsTable != null))
                {
                    _dropdownMenuAsTable.Frame = GetPositionForDropDownMenu(rowHeight, tableWidth);
                }
            }
    
            private void RelocateSecondaryMenuItemsToDropDownCache(ISecondaryMenuSupport secondaryMenuSupport)
            {
                if (secondaryMenuSupport != null)
                {
                    _toolbarItemsUpdated = OnToolbarItemsUpdated;
                    _secondaryItems
                        = secondaryMenuSupport.GetToolbarItems(_toolbarItemsUpdated, InitiateTearDown)
                            .Where(i => i.Order == ToolbarItemOrder.Secondary)
                            .ToList();
                    _secondaryItems.ForEach(secondaryMenuSupport.RemoveToolbarItem);
                }
            }
    
            // Callback that is called when an item is selected in our fake dropdown menu
            void ISecondaryMenuAsDropDown.RemoveDropDown()
            {
                System.Diagnostics.Debug.WriteLine("MyAppPageRenderer: RemoveDropDown " + this.GetType());
                CloseDropDownMenu();
            }
    
            // if the drop down menu is open, close it, otherwise open it
            private void ToggleDropDownMenuVisibility()
            {
                if (_dropdownMenuAsTable == null)
                {
                    if ((View?.Subviews != null)
                        && (View.Subviews.Length > 0)
                        && (View.Bounds != null)
                        && (_secondaryItems != null)
                        && (_secondaryItems.Count > 0))
                    {
                        _dropdownMenuAsTable = OpenDropDownMenu(Element as ISecondaryMenuSupport);
                        Add(_dropdownMenuAsTable);
                    }
                }
                else
                {
                    CloseDropDownMenu();
                }
            }
    
        } // public class MyAppPageRenderer : PageRenderer, ISecondaryMenuAsDropDown // inherits IDisposable
    
        public class TableSource : UITableViewSource
        {
            private static string CellIdentifier = "TableCell";
    
            private readonly UIColor _cellBackgroundColor;
            private readonly UIColor _cellTextColor;
            private ISecondaryMenuAsDropDown _parent;
            private List<ToolbarItem> _tableItems;
            private readonly string[] _tableItemTexts;
    
            public TableSource(
                ISecondaryMenuSupport secondaryMenuSupport,
                ISecondaryMenuAsDropDown parent, 
                List<ToolbarItem> items)
            {
                _cellBackgroundColor = secondaryMenuSupport.CellBackgroundColor.ToUIColor();
                _cellTextColor = secondaryMenuSupport.CellTextColor.ToUIColor();
                _parent = parent;
                _tableItems = items;
                _tableItemTexts = items.Select(a => a.Text).ToArray();
            }
    
            public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
            {
                UITableViewCell cell = null;
    
                InsightsWrapper.SafeSynchronousBlock(() =>
                {
                    if ((tableView != null) && (indexPath != null) && (_tableItemTexts != null))
                    {
                        cell = tableView.DequeueReusableCell(CellIdentifier);
                        string item = _tableItemTexts[indexPath.Row];
                        if (cell == null)
                        {
                            cell = new UITableViewCell(UITableViewCellStyle.Default, CellIdentifier)
                            {
                                BackgroundColor = _cellBackgroundColor
                            };
                            cell.TextLabel.TextColor = _cellTextColor;
                        }
                        cell.TextLabel.Text = item;
                    }
                }, true);
    
                return cell;
            }
    
            public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
            {
                InsightsWrapper.SafeSynchronousBlock(() =>
                {
                    if ((_tableItems != null)
                        && (indexPath != null))
                    {
                        var command = _tableItems[indexPath.Row].Command;
                        command?.Execute(_tableItems[indexPath.Row].CommandParameter);
                        tableView?.DeselectRow(indexPath, true);
                        _parent?.RemoveDropDown();
                        _parent = null;
                    }
                }, true);
            }
    
            public override nint RowsInSection(UITableView tableview, nint section)
            {
                return _tableItemTexts?.Length ?? 0;
            }
    
        } // public class TableSource : UITableViewSource
    

    Any ContentPage that wants to make use of the renderer, should implement the following interface (I didn't want to add a requirement to use a common page class here):

        public interface ISecondaryMenuSupport
        {
            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; }
            IList<ToolbarItem> GetToolbarItems(EventHandler toolbarItemsUpdated, EventHandler initiateTearDown);
            void RemoveToolbarItem(ToolbarItem toolbarItem);
        }
    

    The two event handlers are used by the ContentPage to indicate when the toolbar items for the page have changed and to indicate when the renderer should cleanup any resources that are currently in use. The former is required as ToolbarItems are not an ObservableCollection (I only build the toolbar once, so only fire this event once). The latter may not be required at all - I'll remove it if testing confirms that cleanup is completed without it. Like I said, work in progress :-)

    InsightsWrapper.SafeSynchronousBlock is a helper that wraps Xamarin.Insights, HockeyApp, Mobile Center etc. The SafeSynchronousBlock helper can be replaced by a simple try/catch.

  • AmitManchandaAmitManchanda USMember ✭✭
    @JohnHardman Thank you so much for your more generic and acceptable answer.
  • JohnHardmanJohnHardman GBUniversity mod

    @AmitManchanda - There's still one thing I don't like about the way I've done it - that the renderer modifies the ContentPage's ToolbarItems. It would be better if ToolbarItems were left unchanged, but being rendered as if it had been changed as per the code above. That's a bigger job than what I have done above.

  • gmarburygmarbury Member

    @AmitManchanda can you post the entire code in a GIT hub with a MIT license?

  • MosCDMosCD Member ✭✭
    edited March 2018

    @AmitManchanda modified your solution to make the menu disappear on page disappear

     public override void ViewWillDisappear(bool animated)
     {
                foreach (var subview in View.Subviews)
                {
                     if (subview == table)
                     {
                         table.RemoveFromSuperview();
                         return;
                    }
                 }
        base.ViewWillDisappear(animated);
    }
    
  • MatthewRMatthewR USMember ✭✭
    edited April 2018

    @AmitManchanda you could also use a UIAlertController instead of a TableSource.

    https://developer.xamarin.com/api/type/MonoTouch.UIKit.UIAlertController/

    private void ToolClicked()
    {
        var alert = UIAlertController.Create(null, null, UIAlertControllerStyle.ActionSheet);
        foreach (var secondaryItem in _SecondaryItems)
        {
            var uiAlertAction = UIAlertAction.Create(secondaryItem.Text, UIAlertActionStyle.Default, action => secondaryItem.Command.Execute(secondaryItem.CommandParameter));
            if (!string.IsNullOrEmpty(secondaryItem.Icon.File)) uiAlertAction.SetValueForKey(new UIImage(secondaryItem.Icon.File), new NSString("image"));
            alert.AddAction(uiAlertAction);
        }
        alert.AddAction(UIAlertAction.Create("Cancel", UIAlertActionStyle.Cancel, null));
        PresentViewController(alert, true, null);
    }
    

    Edit: Nevermind, you said you wanted the toolbar to look similar to Android. I'll just leave this here in case someone likes the look of the alert controller instead.

  • mmakdessimmakdessi Member ✭✭

    Can someone please post the code with the using statement because it's not working compiling and not finding the using statement on Visual Studio for Mac. A complete project will be lovely as well. Thank you.

  • bEaSTbEaST Member

    @AmitManchanda
    or Anyone with final solution.

    Can You Please Share code on GIT for Reference,
    I've tried all as you said, but I ended up to totally lose Toolbar Icon :neutral:

  • mmakdessimmakdessi Member ✭✭

    @bEaST said:
    @AmitManchanda
    or Anyone with final solution.

    Can You Please Share code on GIT for Reference,
    I've tried all as you said, but I ended up to totally lose Toolbar Icon :neutral:

    PLEASSEEE SOMEONE!

  • AmitManchandaAmitManchanda USMember ✭✭

    @mmakdessi @bEaST Sorry for the late reply, Yes, I will post it on GIT soon and will share the details with you guys. Just wait for a day more.

  • mmakdessimmakdessi Member ✭✭

    @AmitManchanda Okay Great, Thanks!

  • TheoBTheoB FRMember ✭✭

    I found a particularly annoying issue with this implementation.
    It does not happen all the time, but if you go back and forth between the page with the secondary actions and it's parent page too quickly, the View in the following code has been Disposed:

        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) { // <--- right here
                if (subview == table) {
                    table.RemoveFromSuperview();
                    return;
                }
            }
            Add(table);
    
        }
    

    Debugging informs me that Dispose() is called when we leave the page and that the ctor is called when we navigate back on it. It seems that the view doesn't sync with the new object if we go back to it before the Dispose() is completed. It happens rarely on emulator, but almost all the time on devices.
    You could wrap it all up un a try catch, but then the button does nothing. Any idea how to do fix this?

  • AmitManchandaAmitManchanda USMember ✭✭

    @mmakdessi Here is the link I have uploaded this code on github

    https://github.com/AmitManchanda/iOSSecondaryToolbarMenubar

  • BrianLKBrianLK Member ✭✭

    @TheoB said:
    I found a particularly annoying issue with this implementation.
    It does not happen all the time, but if you go back and forth between the page with the secondary actions and it's parent page too quickly, the View in the following code has been Disposed:

        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) { // <--- right here
                if (subview == table) {
                    table.RemoveFromSuperview();
                    return;
                }
            }
            Add(table);
    
        }
    

    Debugging informs me that Dispose() is called when we leave the page and that the ctor is called when we navigate back on it. It seems that the view doesn't sync with the new object if we go back to it before the Dispose() is completed. It happens rarely on emulator, but almost all the time on devices.
    You could wrap it all up un a try catch, but then the button does nothing. Any idea how to do fix this?

    Is there any update on this? I am running with the same problem, and I don't know how to fix it.

  • @AmitManchanda I already use your code for the toolbar item on ios. It show no error for the code to my project. But when i run the project to ios, it seem no different like before. Can i know what i was missing? Or can you share your code and class files

  • AmitManchandaAmitManchanda USMember ✭✭
    edited November 2018

    @Sonicrays You can see it on github

    https://github.com/AmitManchanda/iOSSecondaryToolbarMenubar

    If still need any help, please let me know.

    Also @BrianLK Can you please try this github implementation. It has some changes based on the view. As you are using the older version of the code. Few changes were removed and added in new.

  • EsamSherifEsamSherif EGUniversity ✭✭

    Wow!, thats some real good work here.
    However there is a simple workaround idea might help someone wants the job simple and done fast.
    Just write the toolbar items in xaml the normal way, use secondary and primary, then just put this code
    Where on click show secondary toolbar (on iOS only, android just works fine)

    List<ToolbarItem> _secondaryToolbarItems;
    public MyContentPage()
        {
            InitializeComponent();
    
            if(Device.RuntimePlatform == Device.iOS)
            {
                _secondaryToolbarItems = ToolbarItems.Where(x => x.Order == ToolbarItemOrder.Secondary).ToList();
    
                ToolbarItems.Add(new ToolbarItem(null, "threeDotsIcon", ToggleSecondaryToolbarItems, ToolbarItemOrder.Primary, 20));
    
                ToggleSecondaryToolbarItems();
            }
        }
    
    
    
        private void ToggleSecondaryToolbarItems()
        {
            var secondaryToolbarItems = ToolbarItems.Where(x => x.Order == ToolbarItemOrder.Secondary).ToList();
    
            if (secondaryToolbarItems.Count > 0)
                foreach (var item in secondaryToolbarItems)
                {
                    ToolbarItems.Remove(item);
                }
            else
            {
                foreach (var item in _secondaryToolbarItems)
                {
                    ToolbarItems.Add(item);
                }
            }
        }
    

    You just need to put that code in a BaseContentPage of your own, or whatever makes you dont repeat yourself.

Sign In or Register to comment.