如何检测 SwiftUI 中的右键单击?
Posted
技术标签:
【中文标题】如何检测 SwiftUI 中的右键单击?【英文标题】:How can I detect a right-click in SwiftUI? 【发布时间】:2020-04-04 09:49:09 【问题描述】:我写信 a simple Mines app 是为了帮助我了解 SwiftUI。因此,我希望主要点击(通常是 LMB)来“挖掘”(显示那里是否有地雷),以及二次点击(通常是 RMB)来放置标志。
我有挖掘工作!但我不知道如何放置标志,因为我不知道如何检测二次点击。
Here's what I'm trying:
BoardSquareView(
style: self.style(for: square),
model: square
)
.gesture(TapGesture().modifiers(.control).onEnded(self.handleUserDidAltTap(square)))
.gesture(TapGesture().onEnded(self.handleUserDidTap(square)))
正如我之前所暗示的,handleUserDidTap
返回的函数在单击时会被正确调用,但handleUserDidAltTap
返回的函数只有在我按住 Control 键时才会被调用。这是有道理的,因为这就是代码所说的......但我没有看到任何可以让它注册二次点击的 API,所以我不知道还能做什么。
我也试过了,但行为似乎相同:
BoardSquareView(
style: self.style(for: square),
model: square
)
.gesture(TapGesture().modifiers(.control).onEnded(self.handleUserDidAltTap(square)))
.onTapGesture(self.handleUserDidTap(square))
【问题讨论】:
您的第一个链接已损坏。私人回购?.onTapGesture()
看看吧。
哎呀,你是对的@GilBirman!固定的;对此感到抱歉
@Raymond 我先试过了。除非我遗漏了一些重要的东西,否则它的行为似乎与 .gesture(TapGesture().onEnded(.......))
【参考方案1】:
就目前的 SwiftUI 而言,这不可能直接实现。我相信它会在未来出现,但目前,TapGesture
显然主要关注没有“右键单击”概念的 ios 用例,所以我认为这就是为什么它被忽略了.请注意,“长按”概念是 LongPressGesture
形式的一等公民,并且几乎专门用于支持该理论的 iOS 上下文中。
也就是说,我确实想出了一种方法来完成这项工作。您需要做的是回退到旧技术,并将其嵌入到您的 SwiftUI 视图中。
struct RightClickableSwiftUIView: NSViewRepresentable
func updateNSView(_ nsView: RightClickableView, context: NSViewRepresentableContext<RightClickableSwiftUIView>)
print("Update")
func makeNSView(context: Context) -> RightClickableView
RightClickableView()
class RightClickableView: NSView
override func mouseDown(with theEvent: NSEvent)
print("left mouse")
override func rightMouseDown(with theEvent: NSEvent)
print("right mouse")
我对此进行了测试,它在一个相当复杂的 SwiftUI 应用程序中对我有用。这里的基本方法是:
-
将您的监听组件创建为
NSView
。
使用实现 NSViewRepresentable
的 SwiftUI 视图包装它。
将您的实现放入您想要的 UI 中,就像您对任何其他 SwiftUI 视图所做的那样。
不是一个理想的解决方案,但现在可能已经足够了。我希望这可以解决您的问题,直到 Apple 进一步扩展 SwiftUI 的功能。
【讨论】:
谢谢。这基本上是what I was doing for now。真的很伤心,这是他们对框架的第一印象。我会将其标记为已接受,直到(希望??)有更好的解决方案出现 求助;看起来这是今天最好的答案。赏金是你的!希望有一天我们会有一个正式的解决方案。 @B.T.这仍然是推荐的解决方案还是 SwiftUI 2.0 带来了一些改进? @mica 查看官方文档,他们似乎没有为任何以鼠标样式交互表示的手势添加本机支持。它们都以“点击”和手指式交互的形式表达,这意味着很难从这种哲学中获得右键单击。文档在这里:developer.apple.com/documentation/swiftui/gestures 在这里:developer.apple.com/documentation/swiftui/eventmodifiers @B.T.你如何将 RightClickableSwiftUIView 应用到 Text() 到其他 SwiftUI-View?【参考方案2】:虽然我喜欢(并赞成)接受的答案,但我找到了一种方法来响应鼠标右键事件,而不必符合 NSViewRepresentable
。这种方法更干净地集成到了我的应用中,因此对于面临此问题的其他人来说,它可能值得考虑作为一种可能性。
我的解决方案首先是愿意接受在 macOS 中右键单击和控制左键单击在传统上被视为等效的约定。此解决方案不允许以不同于控制左键单击的方式处理控制右键单击。但是任何其他修饰符都会被处理,因为它只会将.control
添加到它们并将其转换为左键单击。
如果您使用它们,这可能会破坏 SwiftUI 上下文菜单。我还没有测试过。
所以想法是使用控制键修饰符将鼠标右键事件转换为鼠标左键事件。
为了实现这一点,我对NSHostingView
进行了子类化,并在NSEvent
上提供了一个方便的扩展
// -------------------------------------
fileprivate extension NSEvent
// -------------------------------------
var translateRightMouseButtonEvent: NSEvent
guard let cgEvent = self.cgEvent else return self
switch type
case .rightMouseDown: cgEvent.type = .leftMouseDown
case .rightMouseUp: cgEvent.type = .leftMouseUp
case .rightMouseDragged: cgEvent.type = .leftMouseDragged
default: return self
cgEvent.flags.formUnion(.maskControl)
guard let nsEvent = NSEvent(cgEvent: cgEvent) else return self
return nsEvent
// -------------------------------------
class MyHostingView<Content: View>: NSHostingView<Content>
// -------------------------------------
@objc public override func rightMouseDown(with event: NSEvent)
super.mouseDown(with: event.translateRightMouseButtonEvent)
// -------------------------------------
@objc public override func rightMouseUp(with event: NSEvent)
super.mouseUp(with: event.translateRightMouseButtonEvent)
// -------------------------------------
@objc public override func rightMouseDragged(with event: NSEvent)
super.mouseDragged(with: event.translateRightMouseButtonEvent)
然后在AppDelegate.didFinishLaunching
我改了
window.contentView = NSHostingView(rootView: contentView)
到
window.contentView = MyHostingView(rootView: contentView)
当然,必须对可能引用NSHostingView
的任何其他代码进行类似更改。 AppDelegate
中的引用通常是唯一的,但在重要的项目中可能还有其他引用。
然后,鼠标右键事件在 SwiftUI 代码中显示为带有 .control
修饰符的 TapGesture
。
Text("Right-clickable Text")
.gesture(
TapGesture().modifiers(.control)
.onEnded
_ in
print("Control-Clicked")
)
【讨论】:
谢谢。顺便说一句 - 我学到了一个艰难的方法,如果你有多个.gesture
调用附加到同一个 View
,你想把带有修饰符的那些放在第一位。我有一个首先包含一个未修改的手势,它总是被调用的那个。交换订单解决了它。【参考方案3】:
不幸的是,被接受的解决方案不适合我,因为单击NSStatusItem.button
时没有视觉反馈。对我有用的是检测按钮的触摸处理程序中的右键单击:
func setupStatusBarItem()
statusBarItem.button?.action = #selector(didPressBarItem(_:))
statusBarItem.button?.target = self
statusBarItem.button?.sendAction(on: [.leftMouseDown, .rightMouseDown])
@objc func didPressBarItem(_ sender: AnyObject?)
if let event = NSApp.currentEvent, event.isRightClick
print("right")
else
print("left")
extension NSEvent
var isRightClick: Bool
let rightClick = (self.type == .rightMouseDown)
let controlClick = self.modifierFlags.contains(.control)
return rightClick || controlClick
灵感来自article。
【讨论】:
【参考方案4】:这并不能精确解决扫雷用例,但contextMenu
是在 SwiftUI macOS 应用程序中处理右键单击的一种方法。
例如:
.contextMenu
Button(action: , label: Label("Menu title", systemImage: "icon") )
将响应在 macOS 上的右键单击和在 iOS 上的长按
【讨论】:
以上是关于如何检测 SwiftUI 中的右键单击?的主要内容,如果未能解决你的问题,请参考以下文章
显示 QMenu 以响应 QLabel 中的右键单击,可能吗?