Search code examples
iosswiftobjective-cuicontroluicontextmenuinteraction

UIContextMenuInteraction for UIControl


I know UIButton has a menu property so that context menus can be added to the button. But for other UIControl subclasses, such as custom UIControls, this property does not exist.

I know that one way of adding a context menu to a UIControl is to call addInteraction and then adopt the delegate for the context menu. But then if I have multiple controls on the page, how do I add the menus for all the different controls since they all share the same delegate?

Alternatively, how could I add a menu property to my UIControl subclass? I know that UIControl has a property called contextMenuInteraction and that is apparently automatically populated, but I do not understand how to use that property. I know the control has to implement the delegate method (UIContextMenuConfiguration *)contextMenuInteraction:(UIContextMenuInteraction *)interaction configurationForMenuAtLocation:(CGPoint)location, but then inside that method, do I just construct the UIContextMenuInteraction object? And when the menu property is set, how do I get it to call the delegate method?

Essentially I would like to be able to imitate the UIButton class's menu property in my UIControl subclass. But if not that, then I would like to at least figure out how to support multiple context menus all sharing the same delegate.


Solution

  • You can create and add a new UIContextMenuInteraction object to views (or controls, etc) in the same way you add a new gesture recognizer.

    Then, in your menu action handler, you can get the view that was long-pressed to show the menu via the .sender:

        UIAction *someAction = [UIAction actionWithTitle:@"Some Menu Option"
                                                   image:nil
                                              identifier:nil
                                                 handler:^(__kindof UIAction* _Nonnull action) {
    
            // get the view that presented the context menu
            UIView *v = ((UIContextMenuInteraction *)(action.sender)).view;
    
            // do something
        }];
    

    So, if I have a custom UIControl called MySwitch, and I want to add 7 of them to a stack view, giving each a context menu, I could do something like this:

    for (int i = 1; i < 8; i++) {
        MySwitch *v = [MySwitch new];
        [v setTitle:[NSString stringWithFormat:@"Switch: %d", i]];
        [v.heightAnchor constraintEqualToConstant:60.0].active = YES;
        UIContextMenuInteraction *interaction = [[UIContextMenuInteraction alloc] initWithDelegate:self];
        [v addInteraction:interaction];
        [stack addArrangedSubview:v];
    }