indexOfSelectedItem of NSPopUpButton in menuDidClose and its action handler

I have worked on Windows project for many months, and today I took a look at my private project. Problem is that I lost some sense about Cocoa but good thing is that I had a chance to take a look at Cocoa at a little different angle.

Instead of setting an action handler for an NSPopUpButton, I defined an NSMenu delegator method, menuDidClose, and tried retrieving a selected item there like this.

#pragma mark NSMenu delegates
- (void)menuDidClose:(NSMenu *)menu
	NSLog(@"menuDidClose is called");

	// ???: Why doesn't indexOfSelectedItem return the actual chosen index always?

	NSInteger selectedIndex = [m_addressListPopUpButton indexOfSelectedItem];
	NSDictionary *selectedItem = [[m_addressController arrangedObjects] objectAtIndex:selectedIndex];

	CLLocationCoordinate2D coordinate2D;
	coordinate2D.latitude = [[selectedItem valueForKey:@"latitude"] doubleValue];
	coordinate2D.longitude = [[selectedItem valueForKey:@"longitude"] doubleValue];

	NSLog(@"Selected Item (%d) :\n%@", selectedIndex, selectedItem );
	NSLog(@"All Data :\n%@", [m_addressController arrangedObjects] );

	[self loadMapForLocation:coordinate2D];

Strangely, the indexOfSelectedItem of NSPopUpButton returned an item which was selected previously.
In its documentation, it doesn’t explain about indexOfSelectedItem clearly, but there is better explanation for selectedItem.

Returns the menu item last selected by the user.

– (NSMenuItem *)selectedItem

Return Value
The menu item that is currently selected, or nil if no item is selected.

The last selected menu item is the one that was highlighted when the user released the mouse button. It is possible for a pull-down menu’s selected item to be its first item.

So, it is correct behaviour. However, don’t we use the selectedItem and indexOfSelectedItem to retrieve a currently selected item? Then isn’t it wrong behaviour? Right. It seems to be wrong.
However, I have used the methods and didn’t see the problem. What’s happening here then?

Correct way of implementing action handlers for NSPopUpButton is to set action handler by connecting NSPopUpButton and a controller which contains IBAction action handler for it.

Then, the selectedItem or indexOfSelectedItem return the currently selected information.What is happening is : the menuDidClose is called before the action handler. So, when the menuDidClose is called, the internal information for NSPopUpButton is not updated to point to the current item yet. So, the two method returns an old information. When the action handler is called, it is updated to the latest. So, inside of the action handler, they return correct information.

So, if you want to do something before the information is updated, menuDidClose can be the right candidate for that.


2 responses to this post.

  1. Right. Generally the NSPopUpButton’s -mouseDown: looks like this:

    [NSMenu popUpContextMenu:[self menu] …];
    _indexOfSelectedItem = […];
    _selectedItem = […];
    [[self target] performSelector:[self action]];

    And the -menuDidClose: is called by the NSMenu – before _indexOfSelectedItem is assigned (I made up the variables, but it’s something like this).



Leave a Reply

Please log in using one of these methods to post your comment: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: