I had the same question and looked around but couldn't find a gesture based solution which works in all cases, especially not with List that has selection states. I made a view modifier using an NSEvent click monitor which seems to solve it as expected for all cases I've tried:
extension View {
/// Adds a double click handler this view (macOS only)
///
/// Example
/// ```
/// Text("Hello")
/// .onDoubleClick { print("Double click detected") }
/// ```
/// - Parameters:
/// - inset: To shrink or expand the double click hit area of the view, pass positive or negative values, respectively
/// - handler: Block invoked when a double click is detected
func onDoubleClick(inset: CGSize = .zero, handler: @escaping () -> Void) -> some View {
modifier(DoubleClickHandler(inset: inset, handler: handler))
}
}
struct DoubleClickHandler: ViewModifier {
let inset: CGSize
let handler: () -> Void
@State private var monitor: Any?
private func addListener(frame: NSRect) {
let adjustedFrame = frame.insetBy(dx: inset.width, dy: inset.height)
monitor = NSEvent.addLocalMonitorForEvents(matching: .leftMouseDown) {
if $0.clickCount == 2 {
var location = $0.locationInWindow
location.y = $0.window!.frame.size.height - location.y // Flip to fix coordinate system mismatch
if adjustedFrame.contains(location) { handler() }
}
return $0
}
}
private func removeListener() {
if let monitor = monitor {
NSEvent.removeMonitor(monitor)
}
}
func body(content: Content) -> some View {
content.background {
GeometryReader { proxy in
Color.clear
.onAppear { addListener(frame: proxy.frame(in: .global)) }
.onDisappear { removeListener() }
}
}
}
}
https://gist.github.com/joelekstrom/91dad79ebdba409556dce663d28e8297
Topic:
UI Frameworks
SubTopic:
SwiftUI
Tags: