Come indicato nel titolo della domanda, mouseEntered e mouseExited vengono chiamati solo quando il mouse si sposta. Per capire perché questo è il caso, diamo prima un'occhiata al processo di aggiunta di NSTrackingAreas per la prima volta.
Come semplice esempio, creiamo una vista che normalmente disegna uno sfondo bianco, ma se l'utente passa sopra la vista, disegna uno sfondo rosso. Questo esempio utilizza 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
Ci sono due problemi con questo codice. Innanzitutto, quando viene chiamato -wakeFromNib, se il mouse è già all'interno della vista, -mouseEntered non viene chiamato. Ciò significa che lo sfondo sarà ancora bianco, anche se il mouse è sopra la vista. Questo in realtà è menzionato nella documentazione NSView per il parametro assumeInside di -addTrackingRect: proprietario: userData: assumeInside:
Se SI, il primo evento verrà generato quando il cursore lascia aRect, a prescindere se il cursore si trova all'interno aRect quando viene aggiunto il rettangolo di tracciamento. Se NO il primo evento verrà generato quando il cursore lascia aRect se il cursore è inizialmente all'interno diRect, o quando il cursore entra inRect se il cursore è inizialmente all'esterno diRect.
In entrambi i casi, se il mouse si trova all'interno dell'area di tracciamento, non verranno generati eventi fino a quando il mouse non lascia l'area di tracciamento.
Quindi per risolvere questo problema, quando aggiungiamo l'area di rilevamento, dobbiamo scoprire se il cursore si trova all'interno dell'area di tracciamento. Il nostro metodo -createTrackingArea diventa quindi
- (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];
}
}
Il secondo problema è lo scorrimento. Quando si scorre o si sposta una vista, è necessario ricalcolare NSTrackingAreas in quella vista. Questo viene fatto rimuovendo le aree di tracciamento e quindi aggiungendole di nuovo. Come hai notato, -updateTrackingAreas viene chiamato quando si scorre la vista. Questo è il posto dove rimuovere e aggiungere nuovamente l'area.
- (void) updateTrackingAreas
{
[self removeTrackingArea:trackingArea];
[self createTrackingArea];
[super updateTrackingAreas]; // Needed, according to the NSView documentation
}
E che dovrebbe prendersi cura del vostro problema.Certo, il bisogno di trovare la posizione del mouse e quindi convertirlo per visualizzare le coordinate ogni volta che aggiungi un'area di tracciamento è qualcosa che invecchia rapidamente, quindi ti consiglio di creare una categoria su NSView che la gestisca automaticamente. Non sarai sempre in grado di chiamare [self mouseEntered: nil] o [self mouseExited: nil], quindi potresti voler fare in modo che la categoria accetti un paio di blocchi. Uno da eseguire se il mouse si trova in NSTrackingArea e uno da eseguire se non lo è.
C'è alcune cose da prendere in considerazione. Qual è la superclasse? Sovrascrivi qualsiasi metodo di superclasse senza inviare super? Quindi, ecco le opzioni che passo sempre al trackingArea per essere sicuro che il mouse sia sempre tracciato: NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveInKeyWindow – Dimillian
@ Dimillian77 Ho cambiato in "NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveInKeyWindow" ma non ha aiutato .. lo stesso problema . E senza "NSTrackingActiveAlways" non ha funzionato affatto .. Ho aggiornato la mia domanda che sia più chiara. –
è necessario chiamare '[super updateTrackingAreas]'. E questo codice si trova all'interno delle sottoclassi NSViews o NSScrollView? –