Como ha indicado en el título de la pregunta, mouseEntered y mouseExited solo se invocan cuando se mueve el mouse. Para ver por qué este es el caso, veamos primero el proceso de agregar NSTrackingAreas por primera vez.
Como un ejemplo simple, creemos una vista que normalmente dibuja un fondo blanco, pero si el usuario se desplaza sobre la vista, dibuja un fondo rojo. Este ejemplo usa 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
Existen dos problemas con este código. Primero, cuando se llama a -awakeFromNib, si el mouse ya está dentro de la vista, -mouseEntered no se llama. Esto significa que el fondo seguirá siendo blanco, aunque el mouse esté sobre la vista. Esto es en realidad mencionado en la documentación para el parámetro NSView assumeInside de -addTrackingRect: propietario: userData: assumeInside:
En caso afirmativo, se generará el primer evento cuando el cursor deja aRect, sin tener en cuenta si el cursor está dentro de aRect cuando se agrega el rectángulo de seguimiento. Si NO, el primer evento se generará cuando el cursor salga a Rect si el cursor está inicialmente dentro de aRect, o cuando el cursor ingrese aRect si el cursor está inicialmente fuera de aRect.
En ambos casos, si el mouse está dentro del área de seguimiento, no se generarán eventos hasta que el mouse abandone el área de seguimiento.
Para solucionar esto, cuando agreguemos el área de seguimiento, debemos averiguar si el cursor está dentro del área de seguimiento. Nuestro método -createTrackingArea se convierte así en
- (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];
}
}
El segundo problema es el desplazamiento. Al desplazar o mover una vista, debemos volver a calcular las NSTrackingAreas en esa vista. Esto se hace eliminando las áreas de seguimiento y luego volviéndolas a agregar. Como anotó, se llama a -updateTrackingAreas cuando se desplaza la vista. Este es el lugar para eliminar y volver a agregar el área.
- (void) updateTrackingAreas
{
[self removeTrackingArea:trackingArea];
[self createTrackingArea];
[super updateTrackingAreas]; // Needed, according to the NSView documentation
}
Y eso debería encargarse de su problema.Es cierto que la necesidad de encontrar la ubicación del mouse y luego convertirla para ver las coordenadas cada vez que agrega un área de seguimiento es algo que envejece rápidamente, por lo que recomendaría crear una categoría en NSView que maneje esto automáticamente. No siempre podrá llamar a [self mouseEntered: nil] o [self mouseExited: nil], por lo que es posible que desee que la categoría acepte un par de bloques. Uno para ejecutar si el mouse está en NSTrackingArea, y uno para ejecutar si no lo está.
Hay algunas cosas a tener en cuenta. ¿Cuál es la superclase? ¿Sobreescribes cualquier método de superclase sin enviar super? Entonces, aquí hay opciones que siempre paso al trackingArea para asegurarme de que el mouse siempre se rastrea: NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveInKeyWindow – Dimillian
@ Dimillian77 Cambié a "NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveInKeyWindow" pero eso no ayudó ... mismo problema . Y sin "NSTrackingActiveAlways" no funcionó en absoluto ... Actualicé mi pregunta para que sea más clara. –
necesita llamar '[super updateTrackingAreas]'. ¿Y este código está dentro de las subclases NSViews o NSScrollView? –