Cocoa 鼠标事件

Cocoa中的NSResponder类提供了鼠标事件响应能力,所有视图都继承自它,因此可以方便地处理鼠标按下和松开事件。文章详细介绍了鼠标一般事件、鼠标跟踪事件以及鼠标拖拽事件的处理方式,并探讨了在视图尺寸变化时如何更新跟踪区域以避免事件触发问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

鼠标事件响应

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值