【问题标题】:How can I detect a right-click in SwiftUI?如何检测 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(.......))

标签: swift event-handling mouseevent swiftui right-click


【解决方案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 应用程序中对我有用。这里的基本方法是:

  1. 将您的监听组件创建为NSView
  2. 使用实现 NSViewRepresentable 的 SwiftUI 视图包装它。
  3. 将您的实现放入您想要的 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 上的长按

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-08-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-30
      • 2022-12-23
      • 1970-01-01
      相关资源
      最近更新 更多