在 MacOS 上的 SwiftUI 中查找点击位置
Posted
技术标签:
【中文标题】在 MacOS 上的 SwiftUI 中查找点击位置【英文标题】:Finding click location in SwiftUI on MacOS 【发布时间】:2020-11-28 05:46:24 【问题描述】:我尝试使用 NSViewRepresentable 而不是 UIViewRepresentable 将我在 ios 应用程序上使用的解决方案调整到 macOS。
下面是我的“可点击视图”。我的问题是,当我尝试使用此视图时,我收到错误 Cannot find "TappableView" in scope
。
谢谢。
(使用 Xcode 版本 12.0 beta 4)
import Foundation
import SwiftUI
struct TappableView: NSViewRepresentable
var tappedCallback: ((CGPoint, Int) -> Void)
func makeNSView(context: NSViewRepresentableContext<TappableView>) -> NSView
let v = UIView(frame: .zero)
let gesture = NSClickGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.tapped))
gesture.numberOfTapsRequired = 1
let gesture2 = NSClickGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.doubleTapped))
gesture2.numberOfTapsRequired = 2
gesture.require(toFail: gesture2)
v.addGestureRecognizer(gesture)
v.addGestureRecognizer(gesture2)
return v
class Coordinator: NSObject
var tappedCallback: ((CGPoint, Int) -> Void)
init(tappedCallback: @escaping ((CGPoint, Int) -> Void))
self.tappedCallback = tappedCallback
@objc func tapped(gesture:NSClickGestureRecognizer)
let point = gesture.location(in: gesture.view)
self.tappedCallback(point, 1)
@objc func doubleTapped(gesture:NSClickGestureRecognizer)
let point = gesture.location(in: gesture.view)
self.tappedCallback(point, 2)
func makeCoordinator() -> TappableView.Coordinator
return Coordinator(tappedCallback:self.tappedCallback)
func updateNSView(_ nsView: NSView, context: NSViewRepresentableContext<TappableView>)
【问题讨论】:
【参考方案1】:更好的解决方案是使用 SwiftUI 手势组合而不是 NSViewRepresentable
解决方案,因为它可以更好地与 SwiftUI API 集成。提出的其他解决方案的问题是,如果您想编写复杂的手势,则不能使用.highPriorityGesture()
或任何其他Gesture
API。
这是一个有效的实现:
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>
SimultaneousGesture(
TapGesture(count: count),
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 location = value.second?.startLocation else return
guard let endLocation = value.second?.location else return
guard ((location.x-1)...(location.x+1)).contains(endLocation.x),
((location.y-1)...(location.y+1)).contains(endLocation.y) else
return
action(location)
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, coordinateSpace: .local, perform: action)
您可以像任何其他 SwiftUI 手势一样通过便捷的 API .onClickGesture(count:perform:)
使用它。
【讨论】:
【参考方案2】:这是工作变体
struct TappableView: NSViewRepresentable
var tappedCallback: ((CGPoint, Int) -> Void)
func makeNSView(context: NSViewRepresentableContext<TappableView>) -> NSView
let v = NSView(frame: .zero)
context.coordinator.configure(view: v)
return v
class Coordinator: NSObject, NSGestureRecognizerDelegate
var tappedCallback: ((CGPoint, Int) -> Void)
private var gesture: NSClickGestureRecognizer!
private var gesture2: NSClickGestureRecognizer!
init(tappedCallback: @escaping ((CGPoint, Int) -> Void))
self.tappedCallback = tappedCallback
func configure(view: NSView)
gesture = NSClickGestureRecognizer(target: self, action: #selector(Coordinator.tapped))
gesture.delegate = self
gesture.numberOfClicksRequired = 1
gesture2 = NSClickGestureRecognizer(target: self, action: #selector(Coordinator.doubleTapped))
gesture2.delegate = self
gesture2.numberOfClicksRequired = 2
view.addGestureRecognizer(gesture)
view.addGestureRecognizer(gesture2)
@objc func tapped(gesture:NSClickGestureRecognizer)
let point = gesture.location(in: gesture.view)
self.tappedCallback(point, 1)
@objc func doubleTapped(gesture:NSClickGestureRecognizer)
let point = gesture.location(in: gesture.view)
self.tappedCallback(point, 2)
func gestureRecognizer(_ gestureRecognizer: NSGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: NSGestureRecognizer) -> Bool
return gestureRecognizer === gesture && otherGestureRecognizer === gesture2
func makeCoordinator() -> TappableView.Coordinator
return Coordinator(tappedCallback:self.tappedCallback)
func updateNSView(_ nsView: NSView, context: NSViewRepresentableContext<TappableView>)
【讨论】:
以上是关于在 MacOS 上的 SwiftUI 中查找点击位置的主要内容,如果未能解决你的问题,请参考以下文章
在 macOS 上的 SwiftUI 列表视图中选择和删除核心数据实体
macOS 上的 SwiftUI:如何为 onDelete 启用 UI(从列表中删除)