Question

Pourquoi Mouseexit / Mouseentered ne s'appelle pas lorsque la souris sort de NstrackingArea en faisant défiler ou en faisant l'animation?

Je crée du code comme ceci:

Mouse est entré et sorti:

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

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

Zone de suivi:

-(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];
}

Plus de détails:

J'ai ajouté NSViews en tant que sous-vues dans la vue de NSSCrollView. Chaque NSView a sa propre zone de suivi et lorsque je fais défiler mon Scrollview et qui laisse la zone de suivi "MouseExit" n'est pas appelé mais sans défiler, tout fonctionne bien. Le problème est que lorsque je fais défiler "UpdateTrackingaReas" est appelé et je pense que cela fait des problèmes.

* Même problème avec juste NSView sans l'ajouter comme SubView, ce n'est donc pas un problème.

Était-ce utile?

La solution

Comme vous l'avez noté dans le titre de la question, la souris et la moussexité ne sont appelées que lorsque la souris bouge. Pour voir pourquoi c'est le cas, examinons d'abord le processus d'ajout de nstrackingares pour la première fois.

À titre d'exemple simple, créons une vue qui dessine normalement un fond blanc, mais si l'utilisateur plane sur la vue, il dessine un fond rouge. Cet exemple utilise l'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

Il y a deux problèmes avec ce code. Premièrement, lorsque -AwakeFromnib est appelé, si la souris est déjà à l'intérieur de la vue, -Mouseentered n'est pas appelée. Cela signifie que l'arrière-plan sera toujours blanc, même si la souris est au-dessus de la vue. Ceci est en fait mentionné dans la documentation NSView pour le paramètre PUSUMEInside de -AddTrackingRect: Propriétaire: UserData: PUSUMEInSID:

Si oui, le premier événement sera généré lorsque le curseur quittera Arect, peu importe si le curseur est à l'intérieur de la circulation lorsque le rectangle de suivi est ajouté. Si non, le premier événement sera généré lorsque le curseur quitte le curseur si le curseur est initialement à l'intérieur de l'Arect, ou lorsque le curseur entre atect si le curseur est initialement à l'extérieur.

Dans les deux cas, si la souris se trouve à l'intérieur de la zone de suivi, aucun événement ne sera généré jusqu'à ce que la souris quitte la zone de suivi.

Donc, pour résoudre ce problème, lorsque nous ajoutons la zone de suivi, nous devons savoir si le curseur se trouve dans la zone de suivi. Notre méthode de CreateTrackingArea devient ainsi

- (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];
    }
}

Le deuxième problème est de faire défiler. Lors du défilement ou du déplacement d'une vue, nous devons recalculer les nstrackingares dans cette vue. Cela se fait en supprimant les zones de suivi, puis en les ajoutant. Comme vous l'avez noté, -UpdateTrackingaReas est appelé lorsque vous faites défiler la vue. C'est l'endroit où retirer et réadapter la zone.

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

Et cela devrait prendre soin de votre problème. Certes, avoir besoin de trouver l'emplacement de la souris, puis de le convertir en coordonnées à chaque fois que vous ajoutez une zone de suivi est quelque chose qui vieillit rapidement, donc je recommande de créer une catégorie sur NSView qui le gère automatiquement. Vous ne pourrez pas toujours appeler [auto-sourisé: nil] ou [self mouseexited: nil], donc vous voudrez peut-être faire accepter la catégorie quelques pâtés de maisons. Un à exécuter si la souris est dans la Nstrackingarea, et une à fonctionner si ce n'est pas le cas.

Autres conseils

@Michael offre une excellente réponse et a résolu mon problème. Mais il y a une chose,

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

j'ai trouvé CGRectContainsPoint Fonctionne dans ma boîte, pas CGPointInRect,

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top