4
votes

In my project there is a Window with a NSButton and a NSView (which is a container for other views). A click on the button changes the container's subview as follow:

[containerView replaceSubview:displayedSubview with:nextView];

The first subview added to containerView has a View Based TableView filled (using bindings) with object of a NSTableCellView subclass.

This subclass NSTableCellView has a tracking area which lets me show/hide a button when the mouse enters/leaves the cell.

Methods mouseEntered: and mouseExited: respectively show and hide _buttonInsideTableCellView using setAlphaValue: method.

The tracking area is created in the init method as follow:

NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited|NSTrackingActiveInActiveApp;
_trackingArea  = [[NSTrackingArea alloc] initWithRect:[self bounds] options:options owner:self userInfo:nil];

The updateTrackingAreas method is simply:

- (void)updateTrackingAreas
{
    [_buttonInsideTableCellView setAlphaValue:0.0];

    [self removeTrackingArea:_trackingArea];

    NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited|NSTrackingActiveInActiveApp;
    _trackingArea  = [[NSTrackingArea alloc] initWithRect:[self bounds] options:options owner:self userInfo:nil];

    [self addTrackingArea:_trackingArea];

    [super updateTrackingAreas];
}

The problem

When the program is launched everything works as expected: if the mouse is over a cell, its button gets displayed.

But after I change the containerView's subview to nextView and back to the first view, the method updateTrackingAreas begins to be called many times (2 or more) when the subview is replaced, when the app becomes active (???), when the mouse enter or leaves the trackingArea (???).

The call stack

Here is the call stack if I put a breakpoint in updateTrackingAreas:

#0  0x0000000100019e33 in -[CustomTableCellView updateTrackingAreas]
#1  0x00007fff8a1b24e4 in -[NSView(NSInternal) _updateTrackingAreas] ()
#2  0x00007fff894740b6 in __NSArrayEnumerate ()
#3  0x00007fff8a1b2960 in -[NSView(NSInternal) _updateTrackingAreas] ()
#4  0x00007fff894740b6 in __NSArrayEnumerate ()
#5  0x00007fff8a1b2960 in -[NSView(NSInternal) _updateTrackingAreas] ()
#6  0x00007fff894740b6 in __NSArrayEnumerate ()
#7  0x00007fff8a1b2960 in -[NSView(NSInternal) _updateTrackingAreas] ()
#8  0x00007fff894740b6 in __NSArrayEnumerate ()
#9  0x00007fff8a1b2960 in -[NSView(NSInternal) _updateTrackingAreas] ()
#10 0x00007fff8a1b36bd in -[NSScrollView _updateTrackingAreas] ()
#11 0x00007fff894740b6 in __NSArrayEnumerate ()
#12 0x00007fff8a1b2960 in -[NSView(NSInternal) _updateTrackingAreas] ()
#13 0x00007fff894740b6 in __NSArrayEnumerate ()
#14 0x00007fff8a1b2960 in -[NSView(NSInternal) _updateTrackingAreas] ()
#15 0x00007fff894740b6 in __NSArrayEnumerate ()
#16 0x00007fff8a1b2960 in -[NSView(NSInternal) _updateTrackingAreas] ()
#17 0x00007fff894740b6 in __NSArrayEnumerate ()
#18 0x00007fff8a1b2960 in -[NSView(NSInternal) _updateTrackingAreas] ()
#19 0x00007fff894740b6 in __NSArrayEnumerate ()
#20 0x00007fff8a1b2960 in -[NSView(NSInternal) _updateTrackingAreas] ()
#21 0x00007fff894740b6 in __NSArrayEnumerate ()
#22 0x00007fff8a1b2960 in -[NSView(NSInternal) _updateTrackingAreas] ()
#23 0x00007fff8a1b237c in _handleInvalidCursorRectsNote ()
#24 0x00007fff8a6ac851 in __35-[NSWindow _postInvalidCursorRects]_block_invoke_02794 ()
#25 0x00007fff894420c7 in __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ ()
#26 0x00007fff89442031 in __CFRunLoopDoObservers ()
#27 0x00007fff8941d4a8 in __CFRunLoopRun ()
#28 0x00007fff8941cdd2 in CFRunLoopRunSpecific ()
#29 0x00007fff89a72774 in RunCurrentEventLoopInMode ()
#30 0x00007fff89a72454 in ReceiveNextEventCommon ()
#31 0x00007fff89a723a3 in BlockUntilNextEventMatchingListInMode ()
#32 0x00007fff8a0d7fa3 in _DPSNextEvent ()
#33 0x00007fff8a0d7862 in -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] ()
#34 0x00007fff8a0cec03 in -[NSApplication run] ()
#35 0x00007fff8a073656 in NSApplicationMain ()
#36 0x00000001000013a2 in main
#37 0x0000000100001374 in start ()
1
Why are you using updateTrackingAreas? If your not changing the bounds of your table cell view, I don't think you need to do that. - rdelmar
The TableView (and its cells) are resized with the window. - user1471997
I'm not sure there is anything wrong with what you're seeing. With all the question marks in your post, it's hard to tell exactly when the update is being called, but I think it should be called whenever your view appears (once for each visible row in your table) and when you scroll or resize. - rdelmar
There are question marks because is strange that the method is called in those situations. - user1471997
it is called when a view goes off/on screen .. sounds reasonable - Daij-Djan

1 Answers

3
votes

It's not exactly clear there is a problem here. updateTrackingAreas method is intended to be called multiple times by AppKit when the views geometry changes or whenever it feels it needs you to recalculate the tracking areas. For instance in the case of animation when the bounds is changing rapidly over time, your method would be called in rapid succession each runloop.

FYI, it's probably not a good idea to change the state of any view, or really do anything other than add or remove tracking areas in that method. Here you are changing the alpha state of the button. That's probably best placed elsewhere.