首页 > 解决方案 > 有没有办法在用户按住鼠标按钮时接收 macOS 事件?

问题描述

拥有一个 SwiftUI slider。用户继续拖动它。在拖动的同时,用户也按下了Option键。

我想根据键盘修饰符标志更改(例如选项键)更改用户界面。但是,在拖动滑块甚至按下鼠标按钮时,似乎主事件循环被阻塞了。

当使用NSEvent.addLocalMonitorForEvents(mathing:handler)来获取Option按键通知时,处理程序甚至不会在用户拖动滑块时被调用。

有什么办法可以实现吗?

我也很想了解为什么问题首先存在。

标签: swiftmacosswiftui

解决方案


最终,我最终使用了一个计时器块来检查和发布键盘修饰符标志的更改。

import Cocoa
import Combine

/**
    Publishers for keyboard events.

    The modifiers are checked regularly by a Timer running on the Main `RunLoop` in `.common` mode
    to ensure the modifiers are detected even when having mouse button pressed down
    (which normally blocks the run loops on the main thread which are not in `common` mode).

    The `NSEvent.addLocalMonitorForEvents` cannot be used because the handler is not executed
    for nested-tracking loops, such as control tracking (e.g. when a mouse button is down).
 */
public class KeyboardEvents {
    /// Broadcasts keyboard flag changes (e.g. keys like Option, Command, Control, Shift, etc.)
    public let modifierFlagsPublisher: AnyPublisher<NSEvent.ModifierFlags?, Never>

    private let modifierFlagsSubject = PassthroughSubject<NSEvent.ModifierFlags?, Never>()
    private var timer: Timer!

    public init() {
        modifierFlagsPublisher = modifierFlagsSubject.removeDuplicates().eraseToAnyPublisher()

        timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [unowned self] _ in
            modifierFlagsSubject.send(NSApp.currentEvent?.modifierFlags)
        }

        RunLoop.current.add(timer, forMode: .common)
    }

    deinit {
        timer.invalidate()
    }
}

推荐阅读