如何在 SwiftUI macOS 项目中使用 NSNotification 观察按下的修饰键(例如 option、shift)?

问题描述 投票:0回答:2

我想要一个 Bool 属性,代表按下了选项键

@Publised var isOptionPressed = false
我会用它来更改 SwiftUI 视图。

为此,我认为我应该使用Combine 来观察按键压力。

我试图找到该事件的 NSNotification,但在我看来,没有任何 NSNotification,这可能对我有用。

macos swiftui combine appkit nsnotifications
2个回答
3
投票

好的,我找到了解决问题的简单方法:

class KeyPressedController: ObservableObject {
    @Published var isOptionPressed = false
    
    init() {
        NSEvent.addLocalMonitorForEvents(matching: .flagsChanged) { [weak self] event -> NSEvent? in
            if event.modifierFlags.contains(.option) {
                self?.isOptionPressed = true
            } else {
                self?.isOptionPressed = false
            }
            return event
        }
    }
}

1
投票

由于您正在使用 SwiftUI 进行工作,因此我建议您除了观察发布者之外,还可以将修改器标志的状态放入 SwiftUI 中

Environment
。我认为它非常适合 SwiftUI 的声明性语法。

我有另一个实现,但采用了您找到的解决方案并进行了调整。


import Cocoa
import SwiftUI
import Combine

struct KeyModifierFlags: EnvironmentKey {
    static let defaultValue = NSEvent.ModifierFlags([])
}

extension EnvironmentValues {
    var keyModifierFlags: NSEvent.ModifierFlags {
        get { self[KeyModifierFlags.self] }
        set { self[KeyModifierFlags.self] = newValue }
    }
}

struct ModifierFlagEnvironment<Content>: View where Content:View {
    @StateObject var flagState = ModifierFlags()
    let content: Content;

    init(@ViewBuilder content: () -> Content) {
        self.content = content();
    }

    var body: some View {
        content
            .environment(\.keyModifierFlags, flagState.modifierFlags)
    }
}

final class ModifierFlags: ObservableObject {
    @Published var modifierFlags = NSEvent.ModifierFlags([])

    init() {
        NSEvent.addLocalMonitorForEvents(matching: .flagsChanged) { [weak self] event in
            self?.modifierFlags = event.modifierFlags
            return event;
        }
    }
}

请注意,我的事件闭包正在返回传入的事件。如果您返回

nil
,您将阻止事件进一步发展,并且系统中的其他人可能想看到它。

结构体

KeyModifierFlags
设置要添加到视图
Environment
的新项目。
EnvironmentValues
的扩展让我们可以存储和 从环境中检索当前标志。

最后是

ModifierFlagEnvironment
视图。它没有自己的内容 - 在
@ViewBuilder
函数中传递给初始化器。它所做的就是提供包含状态监视器的
StateObject
,并将修改器标志的当前值传递到内容的
Environment

要使用

ModifierFlagEnvironment
,您可以用它在层次结构中包装一个顶级视图。在从默认 Xcode 模板构建的简单 Cocoa 应用程序中,我将应用程序 SwiftUI 内容更改为:

struct KeyWatcherApp: App {
    var body: some Scene {
        WindowGroup {
            ModifierFlagEnvironment {
                ContentView()
            }
        }
    }
}

因此应用程序中的所有视图都可以观看这些标志。

然后要使用它,你可以这样做:

struct ContentView: View {
    @Environment(\.keyModifierFlags) var modifierFlags: NSEvent.ModifierFlags

    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundColor(.accentColor)
            if(modifierFlags.contains(.option)) {
                Text("Option is pressed")
            } else {
                Text("Option is up")
            }
        }
        .padding()
    }
}

这里内容视图监视环境中的标志,并且视图使用当前修饰符决定显示什么。

© www.soinside.com 2019 - 2024. All rights reserved.