特定于 MacOS,但也许与 iOS 相关的回复也可以让我走上正轨:
当应用程序失去焦点时,我需要确定应用程序范围内是否有任何文本输入(swiftUI 文本字段、NSTextField)处于活动状态。这意味着用户要么最小化我的应用程序的窗口,要么单击另一个应用程序的窗口。 我可以使用 NSApplication (或一般的 AppKit)API 来实现此目的吗? 在幕后肯定存在某些东西,因为当应用程序重新获得焦点时,应用程序上次获得焦点时处于活动状态的字段将再次处于活动状态。不幸的是,我认为无论使用什么 api 来实现这一目标都没有公开。
这是我的需求的代码示例:
struct FooView: View {
@Environment(\.controlActiveState) private var controlActiveState
var body: some View {
Text("Hello World")
.onChange(of: controlActiveState) { newState in
guard !(newState == .inactive) else { return }
let wasAnyTextInputActive: Bool = false // <- need to determine if any textfield was active app wide here
guard wasAnyTextInputActive else { return }
print("Should only be reached when the app was backgrounded with an active text input")
}
}
}
谢谢!
应用程序中接收键盘事件的
NSWindow
称为“按键窗口”,您可以在NSApplication
keyWindow
属性中找到它。
当应用程序未激活时,AppKit 将
keyWindow
设置为零。当应用程序再次激活时,AppKit 会恢复 keyWindow
。
在 AppKit(和 UIKit)中,键盘事件沿着称为“响应者链”的结构进行路由。链中的第一响应者称为“第一响应者”。您可以在 其 firstResponder
属性中找到窗口当前的第一响应者。
(请注意,
UIWindow
不会公开 firstResponder
属性,尽管它内部有一个属性。)因此,当您的应用程序变得不活动时,您可以检查它是否有关键窗口,并(如果有)询问该关键窗口中的第一响应者是什么。
在键盘系统设置中,有一个“键盘导航”选项,允许非文本控件(如按钮)成为第一响应者。这允许用户完全通过键盘操纵更多的用户界面。因此,您可能还想检查第一响应者是否是
NSTextView
。请注意,在 AppKit 中,
NSTextField
通常不能成为第一响应者。当你尝试聚焦 NSTextField
时,它会偷偷地给自己一个 NSTextView
子视图,称为“字段编辑器”,字段编辑器成为第一响应者。这个例子应该可以帮助您入门:
struct ContentView: View {
@State var text = "hello"
@FocusState var fieldIsFocused
var body: some View {
VStack {
TextField("Text", text: $text)
.focused($fieldIsFocused)
Text(fieldIsFocused ? "Field is focused" : "Field is not focused")
Button("Unfocus") {
fieldIsFocused = false
}
}
.padding()
.onReceive(
NotificationCenter.default
.publisher(for: NSApplication.willResignActiveNotification)
) { _ in
print("key window: \(NSApplication.shared.keyWindow)")
print("key window's first responder: \(NSApplication.shared.keyWindow?.firstResponder)")
print("key window's first responder is NSTextView: \(NSApplication.shared.keyWindow?.firstResponder is NSTextView)")
}
}
}