Search code examples
objective-ccocoansmenuitem

How to relate the states of several NSMenuItems, so that they are mutually exclusive?


What is the best way to relate the states of several NSMenuItems as seen in many application’s View → Sort By menus, so that they are mutually exclusive? Here a screenshot from Mail as example:

enter image description here

I found the following passage in the documentation but am not sure how to implement this:

You can use states to implement a group of mutually exclusive menu items, much like a group of radio buttons. For example, a game could have three menu items to show the level of play: Beginner, Intermediate, and Advanced. To implement a such a group, create one action message that they all use. This action message changes the appropriate setting, and then reflects that change by unchecking the currently checked item and checking the newly selected item.


Solution

  • The usual way to distinguish senders is to assign each a unique tag in IB. Then use [sender tag] to get that tag in the action method.

    To find the old checked item for the state which is being switched away from, you could use [[sender menu] itemWithTag:tagForOldState]. However, if there's any chance of the same state being reflected in multiple menus (e.g. the main menu and a contextual menu), you should consider implementing -validateMenuItem: in the same class that implements the action method. In that method, you can check the item's -action and -tag to decide if it should be checked based on the current program state. Then, call -setState: to apply the appropriate state.

    For example:

    - (BOOL) validateMenuItem:(NSMenuItem*)menuItem
    {
        if ([menuItem action] == @selector(sortBy:))
            [menuItem setState:([menuItem tag] == currentSortOrderTag) ? NSOnState : NSOffState];
        return YES;
    }