在Cocoa OSX中捕获双击事件

19

NSResponder似乎没有鼠标双击事件。有没有简单的方法来捕获双击?

NSResponder似乎缺少鼠标双击事件。是否存在一种简单的方式来捕捉双击事件?
7个回答

34

mouseDown:mouseUp:方法需要一个NSEvent对象作为参数,该对象包含有关点击的信息,包括clickCount(点击次数)。


它起作用了。但我们应该考虑到它破坏了几个组件的选择流程 - 例如 NSTextView。 - WINSergey

9

普通的clickCount解决方案存在的问题是双击被视为两次单击。我的意思是,您仍然会得到单击。如果您想对该单击有不同的反应,则需要在纯粹的计数基础上添加一些东西。以下是我最终采用的(使用Swift):

private var _doubleClickTimer: NSTimer?

// a way to ignore first click when listening for double click
override func mouseDown(theEvent: NSEvent) {
    if theEvent.clickCount > 1 {
        _doubleClickTimer!.invalidate()
        onDoubleClick(theEvent)
    } else if theEvent.clickCount == 1 { // can be 0 - if delay was big between down and up
        _doubleClickTimer = NSTimer.scheduledTimerWithTimeInterval(
            0.3, // NSEvent.doubleClickInterval() - too long
            target: self,
            selector: "onDoubleClickTimeout:",
            userInfo: theEvent,
            repeats: false
        )
    }
}


func onDoubleClickTimeout(timer: NSTimer) {
    onClick(timer.userInfo as! NSEvent)
}


func onClick(theEvent: NSEvent) {
    println("single")
}


func onDoubleClick(theEvent: NSEvent) {
    println("double")
}

9

mouseDown:mouseUp:生成的NSEvent具有一个名为clickCount的属性。检查它是否为2,以确定是否发生了双击。

示例实现:

- (void)mouseDown:(NSEvent *)event {
    if (event.clickCount == 2) {
        NSLog(@"Double click!");
    }
}

只需将此放置在您的NSResponder(例如NSView)子类中即可。


如果您需要处理单击和双击,您通常会怎么做呢?显然,如果最终是双击,您将不得不以某种方式忽略单击。 - jayarjo
@jayarjo - 从我的经验来看,我想不出任何一个单击操作不能在需要双击时执行的地方。在文本字段中,单击将光标放置在特定位置。双击会突出显示整个单词。因此,在单击上移动光标是无害的。在 RTS 游戏中(至少是暴雪的游戏),单击选择单个单位,而双击选择该类型的所有单位。如果您希望双击执行与单击完全不同的操作,则可能需要重新考虑整个用户体验。 - ArtOfWarfare

8
通常应用程序通过在-[mouseUp:]上观察clickCount == 2来确定双击。
一个改进是在-[mouseDown:]上跟踪鼠标单击的位置,并查看鼠标抬起位置的增量很小(在x和y方向上都小于5个点)。

1
在我看来,这是最有用的答案,因为它还回答了一个隐含的问题,即是否在第二次 mouseDown 或第二次 mouseUp 上做出反应。当然,在某些情况下,从标准中偏离可能是有意义的。不幸的是,苹果公司自己并没有完全遵守一个标准(Finder 和 Mail 在 up 上,Calendar 在 down 上)... - fzwo

2

我更喜欢的一种替代 mouseDown: + NSTimer 方法是使用 NSClickGestureRecognizer

    let doubleClickGestureRecognizer = NSClickGestureRecognizer(target: self, action: #selector(self.myCustomMethod))
    doubleClickGestureRecognizer.numberOfClicksRequired = 2

    self.myView.addGestureRecognizer(doubleClickGestureRecognizer)

这与mouseDown的确切样本问题相同:如果您有两个手势识别器用于单击和双击,则在双击的情况下也会调用单击选择器。 - Stefano Bider
这对于这个问题来说过于复杂,也不是正确的方法。Chuck的解决方案是正确的:在-mouseUp:或-mouseDown:中获取NSEvent的clickCount。 - Bryan

1

我实现了类似@jayarjo的东西,但这个更加模块化,你可以将其用于任何NSView或其子类。这是一个自定义手势识别器,它将识别单击和双击操作,但在双击阈值过去之前不会识别单击:

//
//  DoubleClickGestureRecognizer.swift
//

import Foundation
/// gesture recognizer to detect two clicks and one click without having a massive delay or having to implement all this annoying `requireFailureOf` boilerplate code
final class DoubleClickGestureRecognizer: NSClickGestureRecognizer {

    private let _action: Selector
    private let _doubleAction: Selector
    private var _clickCount: Int = 0

    override var action: Selector? {
        get {
            return nil /// prevent base class from performing any actions
        } set {
            if newValue != nil { // if they are trying to assign an actual action
                fatalError("Only use init(target:action:doubleAction) for assigning actions")
            }
        }
    }

    required init(target: AnyObject, action: Selector, doubleAction: Selector) {
        _action = action
        _doubleAction = doubleAction
        super.init(target: target, action: nil)
    }

    required init?(coder: NSCoder) {
        fatalError("init(target:action:doubleAction) is only support atm")
    }

    override func mouseDown(with event: NSEvent) {
        super.mouseDown(with: event)
        _clickCount += 1
        let delayThreshold = 0.15 // fine tune this as needed
        perform(#selector(_resetAndPerformActionIfNecessary), with: nil, afterDelay: delayThreshold)        
        if _clickCount == 2 {
            _ = target?.perform(_doubleAction)
        }
    }

    @objc private func _resetAndPerformActionIfNecessary() {
        if _clickCount == 1 {
            _ = target?.perform(_action)
        }
        _clickCount = 0
    }
}

使用方法:

let gesture = DoubleClickGestureRecognizer(target: self, action: #selector(mySingleAction), doubleAction: #selector(myDoubleAction))
button.addGestureRecognizer(gesture)

@objc func mySingleAction() {
 //  ... single click handling code here
}

@objc func myDoubleAction() {
 // ... double click handling code here
 }

0

就我个人而言,我会检查双击鼠标松开函数:

- (void)mouseUp:(NSEvent *)theEvent
{

    if ([theEvent clickCount] == 2)
    {

        CGPoint point = [theEvent locationInWindow];
        NSLog(@"Double click on: %f, %f", point.x, point.y);

     }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接