【问题标题】:NSView does not receive mouseUp: event when mouse button is released outside of viewNSView 没有收到 mouseUp: 鼠标按钮在视图外释放时的事件
【发布时间】:2016-11-22 14:13:54
【问题描述】:

我有一个自定义的NSView 子类(例如)以下方法:

override func mouseDown(with event: NSEvent) { Swift.print("mouseDown") }
override func mouseDragged(with event: NSEvent) { Swift.print("mouseDragged") }
override func mouseUp(with event: NSEvent) { Swift.print("mouseUp") }

只要在视图内按下、拖动和释放鼠标(按钮),就可以正常工作。但是,当鼠标在视图内按下,移到视图外,然后才释放时,我从未收到mouseUp 事件。

P.S.:调用 super 实现没有帮助。

【问题讨论】:

  • 我添加了这个来获取事件:NSEvent.addLocalMonitorForEvents(matching: .leftMouseUp)
  • @HeestandXYZ 不是应用中发生的 所有 鼠标上移事件的通用监视器吗?问题是关于检测这些事件本身的特定视图。
  • 啊是的,没错,抱歉在我的用例中我只有一个视图。

标签: cocoa event-handling nsview appkit mouseup


【解决方案1】:

Apple 鼠标事件文档的 Handling Mouse Dragging Operations 部分提供了一个解决方案:显然,在使用鼠标跟踪循环跟踪事件时,我们确实收到了 mouseUp 事件。

这是文档中示例代码的变体,适用于 Swift 3:

override func mouseDown(with event: NSEvent) {
    var keepOn = true

    mouseDownImpl(with: event)

    // We need to use a mouse-tracking loop as otherwise mouseUp events are not delivered when the mouse button is
    // released outside the view.
    while true {
        guard let nextEvent = self.window?.nextEvent(matching: [.leftMouseUp, .leftMouseDragged]) else { continue }
        let mouseLocation = self.convert(nextEvent.locationInWindow, from: nil)
        let isInside = self.bounds.contains(mouseLocation)

        switch nextEvent.type {
        case .leftMouseDragged:
            if isInside {
                mouseDraggedImpl(with: nextEvent)
            }

        case .leftMouseUp:
            mouseUpImpl(with: nextEvent)
            return

        default: break
        }
    }
}

func mouseDownImpl(with event: NSEvent) { Swift.print("mouseDown") }
func mouseDraggedImpl(with event: NSEvent) { Swift.print("mouseDragged") }
func mouseUpImpl(with event: NSEvent) { Swift.print("mouseUp") }

【讨论】:

  • 这是错误的。引用的 Apple 文档将“鼠标跟踪循环”作为鼠标拖动的一种可能方法,而在“三方法方法”中,“mouseUp”事件是单独处理的。在同一文档的“处理鼠标单击”一章中也独立处理。这表明实际上应该接收 mouseUp ,除非存在 Apple 错误。事实上,当指针在外面时,mouseUp 不应该被发送,因此必须捕获 mouseDragged 以取消鼠标按下。
  • 我只是将Apple的示例代码转换为Swift,无需冒犯。此外,他们的跟踪循环方法特别有代码来仔细检查鼠标向上事件是否在视图边界内。如果没有发送mouseUp 事件,我们怎么知道结束跟踪循环?
  • @cyanide 这是正确的方法。 mouseDragged 不能用于取消鼠标按下,因为用户可以从那里拖回视图和mouseUp。您希望能够明确地单独检测 mouseUp - 即使它是在视图之外释放的。上面的方法是唯一正确的方法。
  • 花了几个小时在谷歌上搜索这个。这是我发现 NSTableView 不使用 mouseDragged 或 mouseUp 事件的唯一方法。
【解决方案2】:

我将此作为对我需要知道用户已停止使用滑块的类似问题的答案发布。我需要从 NSSlider 或实际上是 NSView 捕获 mouseUp 事件。为我解决的解决方案是简单地捕获 mouseDown 事件并在它退出时添加一些代码并完成我需要的工作。希望这对需要做类似事情的其他人有用。使用 XCode 11.3.1 Swift 5 编写的代码

import Cocoa

class SMSlider: NSSlider {

    var calledOnExit:(()->())?

    override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)
    }

    override func mouseDown(with event: NSEvent) {
        super.mouseDown(with: event)
        if self.calledOnExit != nil {
            self.calledOnExit!()
        }
    }    
}

// In my main swift app
func sliderStopped() {
    print("Slider stopped moving")
}

//...
if slider == nil {
    slider = SMSlider()
}
slider?.isContinuous = true
slider?.target = self
slider?.calledOnExit = sliderStopped
//...

【讨论】:

  • 这是不正确的,假设鼠标在按下的那一刻被释放(并因此被“点击”)并不总是可取的,特别是如果用户按住点击,然后拖出按钮放手(并取消点击)。
猜你喜欢
  • 1970-01-01
  • 2018-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多