现在的位置: 首页 > 综合 > 正文

当滚动鼠标滚轮离开tracking区域的时候,mouseExited不被调用的问题

2013年10月14日 ⁄ 综合 ⁄ 共 3935字 ⁄ 字号 评论关闭

摘译自: http://stackoverflow.com/questions/8979639/mouseexited-isnt-called-when-mouse-leaves-trackingarea-while-scrolling

当滚动鼠标滚轮离开tracking区域的时候,mouseExited不被调用,这时候需要在自定义的视图中实现这个函数updateTrackingAreas:

- (void) updateTrackingAreas
{
    [self removeTrackingArea:trackingArea];
    [self createTrackingArea];
    [super updateTrackingAreas]; // Needed, according to the NSView documentation
}

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

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

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

英文原文:

问:

Why mouseExited/mouseEntered isn't called when mouse exits from NStrackingArea by scrolling or doing animation?

答:

As you noted in the title of the question, mouseEntered and mouseExited are only called when the mouse moves. To see why this is the case, let's first look at the process of adding NSTrackingAreas for the first time.

As a simple example, let's create a view that normally draws a white background, but if the user hovers over the view, it draws a red background. This example uses ARC.

@interface ExampleView

- (void) createTrackingArea

@property (nonatomic, retain) backgroundColor;
@property (nonatomic, retain) trackingArea;

@end

@implementation ExampleView

@synthesize backgroundColor;
@synthesize trackingArea

- (id) awakeFromNib
{
    [self setBackgroundColor: [NSColor whiteColor]];
    [self createTrackingArea];
}

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

- (void) drawRect: (NSRect) rect
{
    [[self backgroundColor] set];
    NSRectFill(rect);
}

- (void) mouseEntered: (NSEvent*) theEvent
{
    [self setBackgroundColor: [NSColor redColor]];
}

- (void) mouseEntered: (NSEvent*) theEvent
{
    [self setBackgroundColor: [NSColor whiteColor]];
}

@end

There are two problems with this code. First, when -awakeFromNib is called, if the mouse is already inside the view, -mouseEntered is not called. This means that the background will still be white, even though the mouse is over the view. This is actually mentioned
in the NSView documentation for the assumeInside parameter of -addTrackingRect:owner:userData:assumeInside:

If YES, the first event will be generated when the cursor leaves aRect, regardless if the cursor is inside aRect when the tracking rectangle is added. If NO the first event will be generated when the cursor leaves aRect if the cursor is initially inside aRect,
or when the cursor enters aRect if the cursor is initially outside aRect.

In both cases, if the mouse is inside the tracking area, no events will be generated until the mouse leaves the tracking area.

So to fix this, when we add the tracking area, we need to find out if the cursor is within in the tracking area. Our -createTrackingArea method thus becomes

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

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

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

The second problem is scrolling. When scrolling or moving a view, we need to recalculate the NSTrackingAreas in that view. This is done by removing the tracking areas and then adding them back in. As you noted, -updateTrackingAreas is called when you scroll
the view. This is the place to remove and re-add the area.

- (void) updateTrackingAreas
{
   
[self removeTrackingArea:trackingArea];
   
[self createTrackingArea];
   
【上篇】
【下篇】

抱歉!评论已关闭.