0

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.

MartinW
  • 4,966
  • 2
  • 24
  • 60
  • 1
    What aren't you sure about? Do you know how to set up the target and action for a menu item? Do you know how to set the same target and action for several menu items? Do you know how to write action methods, generally? Within the action method, do you know how to identify the sender and distinguish it from other possible senders? Do you know how to set the state of a menu item? – Ken Thomases Jun 01 '14 at 20:30
  • Ok, i never have _identified the sender and distinguished it from other possible senders_ before. So it never came to my mind to do this in this particular situation. And then i need Outlets for each menu item so i can set the respective states all „by hand“? – MartinW Jun 01 '14 at 20:44

1 Answers1

3

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;
}
Ken Thomases
  • 88,520
  • 7
  • 116
  • 154
  • Thank you. I learned how to dinstinguish senders by assigning tags to my menu items. – MartinW Jun 03 '14 at 20:10
  • Still i found no elegant way of how to code my - (void)setSortOrder:(id)sender method. I have three menu item Outlets (sortByAuthorMenuItem, sortByTitleMenuItem, sortByYearMenuItem) and an outlet for my NSArrayController (booksArrayController). I know this is an unpopular request on SO, but could you show me what an elegant version of my - (void)setSortOrder:(id)sender could look like? – MartinW Jun 03 '14 at 20:14
  • 1
    You could set the menu item tags to be indexes into an array of sort descriptors. Then, you'd set the array controller's sort descriptor by indexing into the array by the sender's tag. Alternatively, you could set each menu item's `representedObject` to the appropriate sort descriptor. Then you'd set the array controller's sort descriptor to the sender's `representedObject`. – Ken Thomases Jun 04 '14 at 03:12