滚动时鼠标离开trackingArea时不调用mouseExited
-
12-11-2019 - |
题
当鼠标通过滚动或做动画从NStrackingArea退出时,为什么不调用mouseExited/mouseEntered?
我创建这样的代码:
鼠标进出:
-(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];
}
更多详情:
我已经在NSScrollView的视图中添加了NSViews作为子视图。每个NSView都有自己的跟踪区域,当我滚动我的scrollView并离开跟踪区域时,不调用"mouseExited",但不滚动一切正常。问题是,当我滚动"updateTrackingAreas"被调用,我认为这会产生问题。
* 同样的问题只是NSView而没有将其添加为子视图,所以这不是问题。
解决方案
当您在问题的标题中注意到,鼠标移动时只调用mouseEneed和mousexited。要了解为什么是这种情况,让我们首先查看第一次添加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时,如果鼠标已在视图中,则不会调用-mouseentered。这意味着即使鼠标在视图上,背景仍将是白色的。这实际上在-addtrackingrect的idultInside参数的NSView文档中提到了:Owner:UserData:authutsIne:
如果是,则当光标叶叶时,将生成第一个事件,无论在添加跟踪矩形时是否在内部时都会产生。如果如果光标最初在ATECt中,则在光标叶子时,如果光标在光标最初在外部时,光标进入时,如果光标进入时,则不会生成第一个事件。 在这两种情况下,如果鼠标位于跟踪区域内,则不会在鼠标离开跟踪区域之前生成事件。
因此修复此项,当我们添加跟踪区域时,我们需要查明跟踪区域内的光标是否在。我们的-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。这是通过删除跟踪区域来完成的,然后将它们添加回来完成。当您注明的时,滚动视图时调用-UpdateTrackAxingAis。这是删除和重新添加该区域的地方。
.- (void) updateTrackingAreas { [self removeTrackingArea:trackingArea]; [self createTrackingArea]; [super updateTrackingAreas]; // Needed, according to the NSView documentation }
并且应该照顾好你的问题。不可否认,需要找到鼠标位置,然后每次添加跟踪区域时将其转换为查看坐标是一个旧的东西,所以我建议在NSView上创建一个自动处理的类别。您将永远不会能够调用[自我mouseEnEntered:nil]或[自我鼠标尖叫:nil],因此您可能希望使该类别接受耦合块。如果鼠标位于nstrackingarea中,则为一个运行,如果不是,则为一个运行。
其他提示
@Michael提供了一个很好的答案,并解决了我的问题。但有一件事,
if (CGRectContainsPoint([self bounds], mouseLocation))
{
[self mouseEntered: nil];
}
else
{
[self mouseExited: nil];
}
我发现 CGRectContainsPoint
在我的盒子里工作,而不是 CGPointInRect
,