mouseExited не вызывается, когда мышь покидает область отслеживания во время прокрутки

StackOverflow https://stackoverflow.com/questions/8979639

Вопрос

Почему mouseExited/mouseEntered не вызывается, когда мышь выходит из NStrackingArea путем прокрутки или выполнения анимации?

Я создаю такой код:

Мышь входила и выходила:

-(void)mouseEntered:(NSEvent *)theEvent {
    NSLog(@"Mouse entered");
}

-(void)mouseExited:(NSEvent *)theEvent
{
    NSLog(@"Mouse exited");
}

Зона отслеживания:

-(void)updateTrackingAreas
{ 
    if(trackingArea != nil) {
        [self removeTrackingArea:trackingArea];
        [trackingArea release];
    }

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

Более подробная информация:

Я добавил NSViews в качестве подпредставлений в представлении NSScrollView.У каждого NSView есть своя собственная область отслеживания, и когда я прокручиваю свой ScrollView и покидаю область отслеживания, «mouseExited» не вызывается, но без прокрутки все работает нормально.Проблема в том, что при прокрутке вызывается «updateTrackingAreas», и я думаю, что это создает проблемы.

* Та же проблема только с NSView без добавления его в качестве подпредставления, так что это не проблема.

Это было полезно?

Решение

Как вы отмечали в названии вопроса, мыши, мыши, и мыши называются только при перемещении мыши. Чтобы увидеть, почему это так, давайте сначала посмотрим на процесс добавления NStrackingareas в первый раз.

как простой пример, давайте создадим представление, которое обычно рисует белый фон, но если пользователь навещает вид на вид, он рисует красный фон. Этот пример использует дугу.

@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
.

Есть две проблемы с этим кодом. Во-первых, когда-awakefromnib называется, если мышь уже внутри вида, -messEdented не вызывается. Это означает, что фон все еще будет белым, даже если мышь сверх виде. Это на самом деле упомянуто в документации NSView для параметра AssumeInside of -adtrackingRect: владелец: userdata: assumeinside:

Если да, первое событие будет сгенерировано, когда выходит из листьев курсора, независимо от того, если курсор внутри возникает, когда добавляется прямоугольник отслеживания. Если нет первое событие, будет сгенерировано при выходе из курсора, если курсор изначально находится внутри арека, или когда курсор входит в исход, если курсор изначально снаружи Arect.

В обоих случаях, если мышь находится внутри области отслеживания, никакие события не будут сгенерированы, пока мышь не покидает область слежения.

Так, чтобы исправить это, когда мы добавляем область слежения, нам нужно выяснить, находится ли курсор внутри области отслеживания. Таким образом, наша метод -createTrackingArea становится

- (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 (NSPointInRect(mouseLocation, [self bounds]))
    {
        [self mouseEntered: nil];
    }
    else
    {
        [self mouseExited: nil];
    }
}
.

Вторая проблема прокручивает. При прокрутке или перемещении вида нам нужно пересчитать NStrackingAreas в этом представлении. Это делается путем удаления зон отслеживания, а затем добавляя их обратно. Как вы уже отмечали, -udatetrackingingareas вызывается при прокручении вида. Это место для удаления и восстановления области.

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

И это должно позаботиться о вашей проблеме. По общему признанию, нужно найти место нахождения мыши, а затем преобразовать его, чтобы просмотреть координаты каждый раз, когда вы добавляете область отслеживания - это то, что быстро становится старым, поэтому я бы порекомендовал создать категорию на NsView, которая автоматически обрабатывает это. Вы не будете всегда сможете назвать [SAVE NOMMENTEDEDEDED: NIL] или [SEA TOUSEXITED: NIL], так что вы можете захотеть в категорию принимать пару блоков. Один, чтобы запустить, если мышь находится в NStrackingArea, и один, чтобы запустить, если это нет.

Другие советы

@Майкл предлагает отличный ответ и решил мою проблему.Но есть одна вещь,

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

я нашел CGRectContainsPoint работает в моей коробке, нет CGPointInRect,

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top