【问题标题】:Hide the mouse in a Mac application after not moving it不移动鼠标后在 Mac 应用程序中隐藏鼠标
【发布时间】:2017-01-01 14:35:05
【问题描述】:

我目前正在使用 Swift 为 Mac 制作幻灯片应用程序。在这个应用程序中,我想在幻灯片运行并且鼠标有一段时间没有移动时隐藏鼠标,就像在 Quick Time Player 中所做的那样。

现在我尝试了很多使用NSCursor.hide()NSCursor.unhide() 以及NSCursor.setHiddenUntilMouseMoves() 的方法,但都没有奏效。

首先,我没有让它在我的主 ViewController 中调用 mouseMoved 函数,其次,NSCursor.setHiddenUntilMouseMoves() 似乎并不总是有效,即使我根本没有触摸我的触控板。就在更改幻灯片中图像的代码之后,我看到图像正在更改,但是使用调试器时,当光标未隐藏时,它不会在该代码行上停止。

有人能告诉我一个如何让它工作的一般方法吗?我很确定这不是一件很奇特的事情,而且有比我尝试的更简单的方法。

以下是我尝试过的:

import Cocoa

class DiashowViewController: NSViewController {

    enum DiashowState {
        case playing
        case paused
        case stopped
    }

    var files: [URL]?
    var diaTimer = Timer()
    var diashowState: DiashowState = .stopped

    var mouseTimer = Timer()

    @IBOutlet weak var diaView: NSImageView!

    override func viewDidLoad() {
        super.viewDidLoad()

    }

    override var representedObject: Any? {
        didSet {
        // Update the view, if already loaded.

        }
    }

    func playDiashow() {
        if diashowState == .paused {
            diaTimer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(self.changeDia), userInfo: nil, repeats: true)
            diashowState = .playing
            NSCursor.setHiddenUntilMouseMoves(true)
        }
    }

    func playDiashow(withFiles files: [URL]) {
        stopDiashow()

        self.files = files
        diashowState = .paused

        playDiashow()
        changeDia()
    }

    func pauseDiashow() {
        if diashowState == .playing {
            diaTimer.invalidate()
            diashowState = .paused
        }
    }

    override func mouseMoved(with event: NSEvent) {
        print("MOUSE MOVED")
    }

    func stopDiashow() {
        pauseDiashow()
        diaView.image = nil
        files = nil
        diashowState = .stopped
    }

    func changeDia() {
        if diashowState == .playing {
            let i = Int(arc4random_uniform(UInt32(files!.count)))
            let thisDiaURL = files![i]
            let thisDia = NSImage(contentsOf: thisDiaURL)
            thisDia?.size = NSSize(width: (thisDia?.representations.first?.pixelsWide)!, height: (thisDia?.representations.first?.pixelsHigh)!)
            diaView.image = thisDia
            NSCursor.setHiddenUntilMouseMoves(true)
            print("HIDE MOUSE")
        }
    }

}

提前非常感谢!

【问题讨论】:

  • 请粘贴您尝试过的代码
  • 对不起,给你!

标签: swift macos cocoa appkit nscursor


【解决方案1】:

要接收mouseMoved 事件,您需要将NSTrackingArea 添加到视图中,并且您可能已经发现setHiddenUntilMouseMoves 设置是单次触发的,并且需要在鼠标在该状态下移动后重新声明。

我没有尝试解开您的代码,而是创建了一个演示项目,在其中设置了一个带有视图和按钮的窗口。视图将颜色从红色变为绿色以显示状态。

class ViewController: NSViewController {

    @IBOutlet weak var xview: NSView!

    override func viewDidLoad() {
        super.viewDidLoad()
        xview.wantsLayer = true
    }

    //1.
    var isPresentingSlideshow = false
    @IBAction func toggle(_ sender: Any) {

        if(isPresentingSlideshow) {
            isPresentingSlideshow = false
            xview.layer?.backgroundColor = NSColor.green.cgColor
            teardownTracking()
        }
        else {
            isPresentingSlideshow = true
            xview.layer?.backgroundColor = NSColor.red.cgColor
            setupTracking()
        }
    }

    //2.
    var trackingArea:NSTrackingArea?
    func setupTracking() {
        let area = NSTrackingArea(rect: xview.bounds, options: [.activeAlways,.mouseEnteredAndExited,.mouseMoved,.inVisibleRect]  , owner: self, userInfo: nil)
        xview.addTrackingArea(area)
        trackingArea = area
    }

    //3.
    func teardownTracking() {
        if let trackingArea = trackingArea {
            xview.removeTrackingArea(trackingArea)
            self.trackingArea = nil
            NSCursor.setHiddenUntilMouseMoves(false)
        }
    }

    //4.
    var cursorHideState = false
    override func mouseMoved(with event: NSEvent) {
        super.mouseMoved(with: event)

        if !cursorHideState {
            cursorHideState = true
            //5.
            DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
                [weak self] in
                if let _ = self?.trackingArea {
                    NSCursor.setHiddenUntilMouseMoves(true)
                    self?.cursorHideState = false
                }
            }
        }
    }

}

这里发生了什么。

  1. 简单的切换操作可在播放/未播放状态之间切换并将其反映在颜色状态中。
  2. NSTrackingArea 添加到视图中。所有者是此视图控制器,因此它将接收mouseMoved: 事件。设置此选项需要.mouseMoved 选项。
  3. 在幻灯片未播放时从视图中删除跟踪区域并将setHiddenUntilMouseMoves 设置为 false。
  4. mouseMoved: 处理程序
  5. setHiddenUntilMouseMoves 在两秒后设置为真,只要跟踪区域存在且尚未等待。请注意对self 的弱引用可防止此处出现可能的保留循环。

这并不完美,因为您可能会发现光标在离开窗口后会隐藏一次,但应该会让您朝着正确的方向前进。

【讨论】:

  • 非常感谢,这正是我想要的!可以使用 Timer 代替 DispatchQueue,还是有充分的理由在 Timer 上使用它?
  • 你也可以在这里使用计时器。我更喜欢 DispatchQueue,因为保留语义在 IMO 中更清晰一些。但对每个人来说都是自己的:-)
  • 我已经尝试了你的解决方案,它似乎工作得很好,除了setHiddenUntilMouseMoves的调用似乎没有隐藏光标。我只是偶尔看到光标被隐藏。我知道该函数被调用了,因为程序在正确的时刻停止了我放置的断点,并且在它下面的printright 的调用也会在正确的时间被调用。有什么想法吗?
  • 我不知道。你确定你的鼠标在通话时没有移动/移动吗?我的测试是将鼠标移到跟踪区域,然后放手等待。
  • 是的,我是。我和你一样测试它。我只是测试了它每次鼠标移动时都会打印一些东西,并且在调用setHiddenUntilMouseMoves 后肯定没有移动,但光标仅在极少数情况下隐藏。之前每次幻灯片中的图片更改时我都隐藏光标时遇到了类似的问题。
猜你喜欢
  • 1970-01-01
  • 2018-06-04
  • 1970-01-01
  • 1970-01-01
  • 2012-10-12
  • 1970-01-01
  • 2017-06-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多