NSResponder seems to have no mouse double click event. Is there an easy way to catch a double click?
7 Answers
The mouseDown:
and mouseUp:
methods take an NSEvent object as an argument with information about the clicks, including the clickCount
.

- 234,037
- 30
- 302
- 389
-
It worked. But we should take into account that it broke selecting flow of the several components - NSTextView for example. – WINSergey Mar 27 '19 at 10:26
The NSEvent
s generated for mouseDown:
and mouseUp:
have a property called clickCount
. Check if it's two to determine if a double click has occurred.
Sample implementation:
- (void)mouseDown:(NSEvent *)event {
if (event.clickCount == 2) {
NSLog(@"Double click!");
}
}
Just place that in your NSResponder
(such as an NSView
) subclass.

- 20,617
- 19
- 137
- 193
-
And what would you typically do if you need to handle both - single-click and double-click. Obviously you will have to ignore single-click somehow if in the end it turns out to be double-click? – jayarjo Aug 05 '15 at 12:35
-
@jayarjo - Off the top of my head, I can't think of any place where the action done by a single click shouldn't be done if it turns out that a double click was necessary. In text fields, a single click puts your cursor at a specific spot. Double clicking highlights that entire word. So moving the cursor on the single click was harmless. In RTS games (at least Blizzard's) a single click selects a single unit while double clicking selects all units of that type. If you want the double click to do something entirely different from what a single click does, you may want to rethink your whole UX. – ArtOfWarfare Aug 05 '15 at 14:46
The problem with plain clickCount
solution is that double click is considered simply as two single clicks. I mean you still get the single click. And if you want to react differently to that single click, you need something on top of mere click counting. Here's what I've ended up with (in 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")
}

- 16,124
- 24
- 94
- 138
Generally applications look at clickCount == 2 on -[mouseUp:] to determine a double-click.
One refinement is to keep track of the location of the mouse click on the -[mouseDown:] and see that the delta on the mouse up location is small (5 points or less in both the x and the y).

- 91
- 1
- 2
-
1IMHO this is the most useful answer as it also answers the implicit question of whether to react on second mouseDown or second mouseUp. Of course, in certain situations it might make sense to deviate from the standard. Unfortunately, Apple themselves don't quite adhere to one standard (Finder and Mail on up, Calendar on down)… – fzwo May 06 '14 at 10:34
An alternative to the mouseDown:
+ NSTimer
method that I prefer is NSClickGestureRecognizer
.
let doubleClickGestureRecognizer = NSClickGestureRecognizer(target: self, action: #selector(self.myCustomMethod))
doubleClickGestureRecognizer.numberOfClicksRequired = 2
self.myView.addGestureRecognizer(doubleClickGestureRecognizer)

- 764
- 1
- 18
- 34
-
this presents the exact sample issue as with mouseDown: if you have two gesture recognizers for single and double click, the single click selector is called also in case of double click – Stefano Bider Sep 06 '19 at 09:45
-
This is overkill for this problem and is not the right approach. Chuck's solution is correct: get the clickCount of the NSEvent in -mouseUp: or -mouseDown: – Bryan Sep 29 '19 at 05:38
I implemented something similar to @jayarjo except this is a bit more modular in that you could use it for any NSView or a subclass of it. This is a custom gesture recognizer that will recognize both click and double actions but not single clicks until the double click threshold has passed:
//
// 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
}
}
USAGE :
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
}

- 2,184
- 25
- 18
Personally, I check the double click into mouseUp functions:
- (void)mouseUp:(NSEvent *)theEvent
{
if ([theEvent clickCount] == 2)
{
CGPoint point = [theEvent locationInWindow];
NSLog(@"Double click on: %f, %f", point.x, point.y);
}
}

- 2,509
- 28
- 21