Search code examples
objective-ccocoacocoa-bindingsappkitnspopupbutton

Separator item in NSPopupButton with bindings


The contents of a NSPopupButton are bound to an NSArray of strings.

How can we insert a separator item via bindings?

The "-" strings (like in the olden/Classic days) doesn't work, i.e. shows up literally as a "-" menu item.

Is there any out-of-the-box solution with standard Cocoa classes and bindings?

This should be a trivial problem but I can't find any solution to the problem that doesn't involve silly hacks like subclassing NSMenu, NSPopupButton or other non-intuitive work arounds.


Solution

  • I couldn't find a clean way to dynamically add separators to a menu when using bindings. The easiest (and most reusable) way I've found is to use an NSMenuDelegate to dynamically swap out NSMenuItems with a specific title like @"---" with separator items in the menuNeedsUpdate: delegate method.

    Step 1: Create an NSObject that conforms to NSMenuDelegate protocol

    #import <Cocoa/Cocoa.h>
    
    @interface SeparatorMenuDelegate : NSObject <NSMenuDelegate>
    @end
    @implementation SeparatorMenuDelegate
    
    -(void)menuNeedsUpdate:(NSMenu *)menu {
        NSArray* fakeSeparators = [[menu itemArray] filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"title == '---'"]];
        for (NSMenuItem* fakeSep in fakeSeparators) {
            [menu insertItem:[NSMenuItem separatorItem] atIndex:[menu indexOfItem:fakeSep]];
            [menu removeItem:fakeSep];
        }
    }
    
    @end
    

    Step 2: Link things up in Interface Builder.

    Drag out an Object into the scene that contains the NSPopupButton instance. Drag out an object

    Set the object's class to SeparatorMenuDelegate

    Set the object's class

    Twirl open the NSPopupButton control in the Document Outline and select the Menu inside it. Then set the delegate for the Menu to the SeparatorMenuDelegate object that you dragged in earlier.

    Set the menu's delegate

    After this, all items in the menu with a title of @"---" will be converted to separator items.

    If you have multiple NSPopupButton instances in the same scene, you can set the delegate of their Menu to the same object (you only need one SeparatorMenuDelegate per scene).