iOS UIMenuController Custom Renderer for Xamarin Forms

384 views Asked by At

I am trying to create a custom renderer so that a context menu is displayed when a user clicks a button. I have it working in Android and UWP but iOS is proving more difficult. When I click the button, everything runs with no errors but the UIMenuController is not displayed, although I cannot click the button again almost as though the view containing the button has overlaid the screen preventing access to the button. I've tried attaching the menu controller to the button, the ContextMenuView.

Here's the custom Xamarin Forms View -

public class ContextMenuView : View
{
    public EventHandler MenuRequested;
    public void RequestMenu(object sender)
    {
        if(MenuRequested != null)
        {
            MenuRequested(sender, EventArgs.Empty);
        }
    }
}

The ContextMenuView is instantiated from the click event of a button on Main.xaml. Main.xaml consists of an AbsoluteLayout that contains the button being clicked. Here's the click event of the button -

    private void ContextMenuButton_Clicked(object sender, EventArgs e)
    {
        System.Diagnostics.Debug.WriteLine("Click");
        var button = sender as Button;
        if (_popupMenu == null)
        {
            _popupMenu = new ContextMenuView();
            Rectangle menuPosition = new Rectangle { X = button.X, Y = button.Y, Width = 50, Height = 50 };
            _popupMenu.Layout(((Button)sender).Bounds);
            AbsLayout.Children.Add(_popupMenu, menuPosition);
            _popupMenu.IsVisible = true;
        }
        else
        {
            Rectangle menuPosition = new Rectangle { X = button.X, Y = button.Y + button.Height, Width = 50, Height = 50 };
            _popupMenu.Layout(((Button)sender).Bounds);
        }
        _popupMenu.RequestMenu(sender);
    }

And the iOS renderer -

public class ContextMenuViewRendererIOS : ViewRenderer<ContextMenuView, UIView>
{
    private UIView _nativeControl;
    private ContextMenuView _xamarinControl;
    private Xamarin.Forms.AbsoluteLayout _container;
    private UIView _iosView;
    private nfloat _height;
    private nfloat _width;

    protected override void OnElementChanged(ElementChangedEventArgs<ContextMenuView> e)
    {
        base.OnElementChanged(e);
        if (Control == null)
        {
            if (e.NewElement != null)
            {
                _xamarinControl = e.NewElement;
                _xamarinControl.MenuRequested += OnMenuRequested;
            }
            _height = UIScreen.MainScreen.Bounds.Height;
            _width = UIScreen.MainScreen.Bounds.Width;
            _nativeControl = new UIView(new CGRect(0, 0, _width, _height));
            SetNativeControl(_nativeControl);

        }
    }

    private void OnMenuRequested(object sender, EventArgs e)
    {
        try
        {
            var _menu = UIMenuController.SharedMenuController;
            BecomeFirstResponder();
            var iterm = new UIMenuItem("John", new ObjCRuntime.Selector("MenuItemAction:"));
            _menu.MenuItems = new[] { iterm };
            _menu.SetTargetRect(new CGRect(10, 10, 100, 100), _nativeControl);
            _menu.MenuVisible = true;
        }
        catch (Exception ex)
        {

            throw;
        }
    }

    [Export("MenuItemAction:")]
    private void MenuItemAction(UIMenuController controller)
    {
        System.Diagnostics.Debug.WriteLine("MenuItemAction");
    }
}

Thanks in advance.

1

There are 1 answers

0
John Hunter On

The custom renderer needs to override CanBecomeFirstResponder and CanPerform(Selector action, NSObject withSender) and return true from both.