鼠标事件响应
NSResponder提供了鼠标响应事件,而所有的视图都继承自NSResponder,所以我们可以很方便的处理鼠标事件。
鼠标一般事件
鼠标一般事件有按下和松开,又有左右键和其它键之分(本文只讲述左右键),对应的消息如下:
// 按下鼠标左键
- (void)mouseDown:(NSEvent *)theEvent;
// 松开鼠标左键
- (void)mouseUp:(NSEvent *)theEvent;
// 按下鼠标右键
- (void)rightMouseDown:(NSEvent *)theEvent;
// 松开鼠标右键
- (void)rightMouseUp:(NSEvent *)theEvent;
鼠标点击后,NSEvent捕获到的坐标是基于Window的,所以需要经过转换才能使用。
- (void)mouseDown:(NSEvent *)theEvent {
/**
* 将mouseDown消息传递给父类
* 注意,如果写了这句的话,mouseUp事件将不会触发
*/
[super mouseDown:theEvent];
// 将鼠标点击位置的坐标从Window转换为当前View,fromView设为nil表示从Window转换
NSPoint location = [self convertPoint:theEvent.locationInWindow fromView:nil];
// doing something
}
鼠标跟踪事件
鼠标跟踪是通过定义一个矩形区域,捕获在这个区域内鼠标的进出和移动事件。这个监视区可以在awakeFromNib中用 addTrackingArea:方法注册。
- (void)awakeFromNib {
// 创建监视区
NSTrackingArea *trackingArea = [[NSTrackingArea alloc] initWithRect:self.bounds options:
NSTrackingMouseMoved |
NSTrackingMouseEnteredAndExited |
NSTrackingActiveAlways owner:self userInfo:nil];
// 添加到View中
[self addTrackingArea:trackingArea];
}
// 鼠标进入监视区
- (void)mouseEntered:(NSEvent *)theEvent;
// 鼠标在监视区内移动
- (void)mouseMoved:(NSEvent *)theEvent
// 鼠标推出监视区
- (void)mouseExited:(NSEvent *)theEvent;
然而很多时候我们的View的frame或者bounds是会变化的,变化后在awakeFromNib中注册的监视区就与View不重合了,就会出现事件不会触发或者坐标偏差等现象。这个问题,我一般是通过重写updateTrackingAreas方法来解决,updateTrackingAreas在控件大小或者坐标发生变化的时候会被触发。
// 定义一个变量来存储监视区
NSTrackingArea *_trackingArea;
- (void)updateTrackingAreas {
[super updateTrackingAreas];
if (_trackingArea) // 移除久的监视区
[self removeTrackingArea:_trackingArea];
// 创建新的监视区
_trackingArea = [[NSTrackingArea alloc] initWithRect:self.bounds options:
NSTrackingMouseMoved |
NSTrackingMouseEnteredAndExited |
NSTrackingActiveAlways owner:self userInfo:nil];
// 添加到View中
[self addTrackingArea:_trackingArea];
}
鼠标拖拽事件
鼠标拖拽顾名思义就是鼠标按下后不松开,然后移动鼠标,到目标位置后才松开,这三步完成了一次完整的鼠标拖拽动作。
// 鼠标左键拖拽
- (void)mouseDragged:(NSEvent *)theEvent;
// 鼠标右键拖拽
- (void)rightMouseDragged:(NSEvent *)theEvent;
一次完整的拖拽动作代码是这样的:
BOOL isDragged = NO;
- (void)mouseDown:(NSEvent *)theEvent {
// 鼠标按下,开始拖拽
isDragged = YES;
}
- (void)mouseDragged:(NSEvent *)theEvent {
if (isDragged) { // 拖拽中
// 这里不转换坐标的原因是setFrameOrigin需要用的就是在Window中的坐标
NSPoint location = theEvent.locationInWindow;
// 改变控件在Window中的位置,当然这只是简单的写法,效果并不是特别好
[self setFrameOrigin:location];
}
}
- (void)mouseUp:(NSEvent *)theEvent {
// 鼠标松开,结束拖拽
isDragged = NO;
}
上面的代码需要三个方法来解决一个问题,还需要外部状态码来控制,有没有更优雅的写法呢?查找苹果文档,发现可以在mouseDown里面监听后面的鼠标事件。
- (void)mouseDown:(NSEvent *)theEvent {
BOOL keepOn = YES;
while (keepOn) {
theEvent = [self.window nextEventMatchingMask:NSLeftMouseUpMask | NSLeftMouseDraggedMask];
NSPoint location = [self convertPoint:theEvent.locationInWindow fromView:nil];
BOOL isInside = [self mouse:location inRect:self.bounds];
switch (theEvent.type) {
case NSLeftMouseUp:
keepOn = NO;
// doing something
if (isInside)
[super mouseDown:theEvent];
break;
case NSLeftMouseDragged:
// doing something
break;
default:
break;
}
}
}
原文地址:http://blog.youkuaiyun.com/shalyf/article/details/50002453