3

In a 10.7+ project, I am trying to enable UI elements in an NSCollectionViewItem when the mouse is within the bounds of the collection view item's view. For each collection view item which populates the NSCollectionView, I have a custom view which creates an individual NSTrackingArea using its bounds:

- (void) initializeMouseTracking
{
    self.trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] options:NSTrackingMouseEnteredAndExited|NSTrackingActiveAlways owner:self userInfo:nil];
    [self addTrackingArea:self.trackingArea];

    NSPoint mouseLocation = [[self window] mouseLocationOutsideOfEventStream];
    mouseLocation = [self convertPoint: mouseLocation fromView: nil];

    if (NSPointInRect(mouseLocation, [self bounds]) == YES)
    {
        [self mouseEntered:nil];
    }
    else
    {
        [self mouseExited:nil];
    }
}

The tracking areas work great until the NSCollectionView content is scrolled. It is obvious that the tracking areas need to be reset during and after scrolling. I attempted to invalidate and recreate the NSTrackingAreas in several ways:

- (void) resetMouseTracking
{
    [self removeTrackingArea:self.trackingArea];
    [self initializeMouseTracking];
}


- (void) scrollWheel:(NSEvent *)theEvent
{
    [self resetMouseTracking];
    [super scrollWheel:theEvent];
}


- (void) viewDidMoveToWindow:(NSWindow *)newWindow
{   
    [self resetMouseTracking];
    [super viewWillMoveToWindow:newWindow];
}


- (void) updateTrackingAreas
{
    [self resetMouseTracking];
    [super updateTrackingAreas];
}

but not only have these attempts had incomplete and buggy results, the constant recalculation of the tracking areas during scrolling (ala scrollwheel:) seems unnecessary. Instead it would be useful to have an effective way to capture the onset and offset of scrolling events (easy to do in iOS) such that I can invalidate all tracking areas during scrolling. Using an NSViewBoundsDidChangeNotification on the contentView of the scrollview tells me that scrolling is happening, but it does not indicate when it starts or stops.

Would getting scrolling start and end notifications from NSScrollView require deep subclassing or is there something else I am overlooking? It there an entirely different approach that shows more promise?

ctpenrose
  • 1,467
  • 2
  • 18
  • 28
  • I have decided to use a different solution, where I use a single tracking area on the bounds of an NSScrollView subclass which contains the NSCollectionView. Within mouseMoved: I calculate the index of the collection view item the mouse is hovering over after querying the collection view and its prototype view, and obtaining documentVisibleRect from the NSClipView. I then message the appropriate view via a collection view item. The only issue remaining is that the collection view items do not update during scrolling. But after scrolling completes, hover over works as expected. – ctpenrose Jul 19 '13 at 21:06
  • could you please tell what calculations are u doing to calculate the index of collection view item? – Ananth Kamath May 04 '22 at 13:22

2 Answers2

0

In Cocoa for the scroll view, unlike iOS everything is done on the documentView (as opposed to contentView). You probably need [scrollView documentVisibleRect].origin for the content offset.

Check out this answer for more similar comparisons for UIScrollView and NSScrollView

Community
  • 1
  • 1
Rakesh
  • 3,370
  • 2
  • 23
  • 41
  • Sorry if I was unclear in my question. I am interested in learning when scrolling starts and stops, rather than getting a content offset and reconciling it with the collection view items. I edited my question to reflect this. Also, an NSTrackingArea is initialized using an NSRect in the coordinates of the associated view; NSTrackingArea handles the relationship of a tracked view with respect to its superview internally. The problem at hand is an issue of when, rather than where. – ctpenrose Jul 18 '13 at 20:18
  • I guess you could add an observer for this property. Then implement the logic to check for last point. It would save you subclassing I guess. – Rakesh Jul 18 '13 at 20:24
-1

A different approach... an NSView inherits from NSResponder class. NSResponder has methods "mouseEntered:" and "mouseExited:". My suggestion is to use those in your NSView subclass if possible and skip the tracking areas.

regulus6633
  • 18,848
  • 5
  • 41
  • 49
  • 3
    The NSTrackingArea actually allows those two methods to fire, otherwise they will not. I did not include my overridden mouseEntered: and mouseExited: methods in my question initially. – ctpenrose Jul 18 '13 at 18:59