10

Consider this view in SwiftUI:

struct MyView: View {
    var body: some View {
        Rectangle()
            .fill(Color.blue)
            .frame(width: 200, height: 200)
            .onTapGesture {
                print("single clicked")
            }
    }
}

Now we're handling single-click. Say you wanna handle double-click too, but with a separate callback.

You have 2 options:

  • Add double click handler after single click - this doesn't work at all
  • Add double click handler before single click handler, this kinda works:
struct MyView: View {
    var body: some View {
        Rectangle()
            .fill(Color.blue)
            .frame(width: 200, height: 200)
            .onTapGesture(count: 2) {
                print("double clicked")
            }
            .onTapGesture {
                print("single clicked")
            }
    }
}

Double-click handler is called properly, but single-click handler is called after a delay of about 250ms.

Any ideas how to resolve this?

2
  • another approach with registering right tap gesture, here: stackoverflow.com/a/66432412/14998134 Commented Mar 2, 2021 at 3:44
  • In Monterey this means I get either a single or a double-click, which is exactly what I wanted. Commented Apr 29, 2022 at 8:46

2 Answers 2

24

Here is possible approach (tested with Xcode 11.2 / macOS 10.15)

struct MyView: View {
    var body: some View {
        Rectangle()
            .fill(Color.blue)
            .frame(width: 200, height: 200)
            .gesture(TapGesture(count: 2).onEnded {
                print("double clicked")
            })
            .simultaneousGesture(TapGesture().onEnded {
                print("single clicked")
            })
    }
}
Sign up to request clarification or add additional context in comments.

6 Comments

Hmm... It seems that you will get both called when you double-tap the rectangle.
Works as expected on macOS 11.2 (Big Sur).
Doesn't seem to work under macCatalyst though
@ElTomato: there is a possible salvation for registering right tap gesture, here: stackoverflow.com/a/66432412/14998134
Works on Big Sur, 11.4
|
8

The gesture-based solutions don't work correctly when selection is enabled on the List, since the first tap will delay the actual selection. I have made a double click modifier which works on any view and seems to solve it the way I expect in all cases:

extension View {
    /// Adds a double click handler this view (macOS only)
    ///
    /// Example
    /// ```
    /// Text("Hello")
    ///     .onDoubleClick { print("Double click detected") }
    /// ```
    /// - Parameters:
    ///   - handler: Block invoked when a double click is detected
    func onDoubleClick(handler: @escaping () -> Void) -> some View {
        modifier(DoubleClickHandler(handler: handler))
    }
}

struct DoubleClickHandler: ViewModifier {
    let handler: () -> Void
    func body(content: Content) -> some View {
        content.overlay {
            DoubleClickListeningViewRepresentable(handler: handler)
        }
    }
}

struct DoubleClickListeningViewRepresentable: NSViewRepresentable {
    let handler: () -> Void
    func makeNSView(context: Context) -> DoubleClickListeningView {
        DoubleClickListeningView(handler: handler)
    }
    func updateNSView(_ nsView: DoubleClickListeningView, context: Context) {}
}

class DoubleClickListeningView: NSView {
    let handler: () -> Void

    init(handler: @escaping () -> Void) {
        self.handler = handler
        super.init(frame: .zero)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func mouseDown(with event: NSEvent) {
        super.mouseDown(with: event)
        if event.clickCount == 2 {
            handler()
        }
    }
}

https://gist.github.com/joelekstrom/91dad79ebdba409556dce663d28e8297

1 Comment

This is the only way that I've managed to have a $selection and double click working. Kudos sir!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.