在macOS应用中获得右键单击的位置

问题描述

我正在使用SwiftUI开发macOS应用程序,该应用程序应该能够在屏幕上创建,排列和删除几何形状。使用上下文菜单可以很好地创建和拖动形状。

import SwiftUI

class Canvas: ObservableObject {
    
    @Published var nodes: [Node] = []
    
    func addNode(position: CGPoint) -> Void {
        nodes.append(Node(id: UUID(),position: position))
    }
}

struct CanvasView: View {
    
    @Observedobject var canvas = Canvas()
    
    var body: some View {
        ZStack {
            Color(red: 0.9,green: 0.9,blue: 0.8)
                .contextMenu {
                    Button( action: {
                        self.canvas.addNode(position: CGPoint(x: 400,y: 400))
                        
                    } )
                        { Text("Add Node ...") }
                }
            ForEach(canvas.nodes) {node in
                NodeView(node: node)
            }
        }
    }
}

class Node: Identifiable,ObservableObject {
    @Published var id: UUID
    @Published var position: CGPoint
    @Published var positionProxy: CGPoint
    
    init (id: UUID,position: CGPoint) {
        self.id = id
        self.position = position
        self.positionProxy = position
    }
}

struct NodeView: View {
    
    @Observedobject var node: Node
    
    init(node: Node) {
        self.node = node
    }
    
    var draggingNode: some Gesture {
        DragGesture(coordinateSpace: .global)
            .onChanged { value in
                self.node.position.x = value.translation.width + self.node.positionProxy.x;
                self.node.position.y = -value.translation.height + self.node.positionProxy.y
            }
            .onEnded { value in
                self.node.position.x = value.translation.width + self.node.positionProxy.x;
                self.node.position.y = -value.translation.height + self.node.positionProxy.y;
                self.node.positionProxy = self.node.position
            }
    }
    
    var body: some View {
        RoundedRectangle(cornerRadius: 20)
            .fill(Color.white)
            .frame(width: 100,height: 100)
            .overlay(
                RoundedRectangle(cornerRadius: 20)
                    .stroke(linewidth: 1)
                    .fill(Color.gray)
            )
            .position(node.position)
            .gesture(draggingNode)
    }
}

我的问题是所有创建的形状都出现在相同的预定义位置

position: CGPoint(x: 400,y: 400)

在屏幕上,我必须手动将它们移动到预定位置。我正在寻找一种在右键单击期间或上下文菜单位置中跟踪光标位置的方法,以将其用作节点位置并能够编写类似

内容
self.canvas.addNode(position: cursorPosition)

代替

self.canvas.addNode(position: CGPoint(x: 400,y: 400))

Swift中是否有任何功能,最好是在SwiftUI中可以解决我的问题?

解决方法

目前无法在 SwiftUI 中检测鼠标点击位置。您的问题的解决方法可能是使用 SwiftUI Gesture 的组合。

以下是可用于检测(简单或双击)左键单击位置的代码:

import SwiftUI

struct ClickGesture: Gesture {
    let count: Int
    let coordinateSpace: CoordinateSpace
    
    typealias Value = SimultaneousGesture<TapGesture,DragGesture>.Value
    
    init(count: Int = 1,coordinateSpace: CoordinateSpace = .local) {
        precondition(count > 0,"Count must be greater than or equal to 1.")
        self.count = count
        self.coordinateSpace = coordinateSpace
    }
    
    var body: SimultaneousGesture<TapGesture,DragGesture> {
        TapGesture(count: count)
            .simultaneously(with: DragGesture(minimumDistance: 0,coordinateSpace: coordinateSpace))
    }
    
    func onEnded(perform action: @escaping (CGPoint) -> Void) -> some Gesture {
        ClickGesture(count: count,coordinateSpace: coordinateSpace)
            .onEnded { (value: Value) -> Void in
                guard value.first != nil else { return }
                guard let startLocation = value.second?.startLocation else { return }
                guard let endLocation = value.second?.location else { return }
                guard ((startLocation.x-1)...(startLocation.x+1)).contains(endLocation.x),((startLocation.y-1)...(startLocation.y+1)).contains(endLocation.y) else { return }
                
                action(startLocation)
            }
    }
}

extension View {
    func onClickGesture(
        count: Int,coordinateSpace: CoordinateSpace = .local,perform action: @escaping (CGPoint) -> Void
    ) -> some View {
        gesture(ClickGesture(count: count,coordinateSpace: coordinateSpace)
            .onEnded(perform: action)
        )
    }
    
    func onClickGesture(
        count: Int,perform action: @escaping (CGPoint) -> Void
    ) -> some View {
        onClickGesture(count: count,coordinateSpace: .local,perform: action)
    }
    
    func onClickGesture(
        perform action: @escaping (CGPoint) -> Void
    ) -> some View {
        onClickGesture(count: 1,perform: action)
    }
}

您可以以与 onTapGesture()TapGesture 非常相似的方式使用它:

struct MyView: View {
  var body: some View {
    Rectangle()
      .frame(width: 600,height: 400)
      .onClickGesture(count: 2) { location in 
        print("Double tap at location \(location)")
      }
  }
}

您可以另外指定一个 CoordinateSpace。