响应 SwiftUI 中的按键事件
Posted
技术标签:
【中文标题】响应 SwiftUI 中的按键事件【英文标题】:Respond to key press events in SwiftUI 【发布时间】:2020-04-25 17:57:53 【问题描述】:我想响应按键,例如 macOS/OSX 上的 esc
键,以及在 iPad 上使用外接键盘时。我该怎么做?
我曾想过将 @available
/#available
与 SwiftUI 的 onExitCommand
一起使用,这看起来很有希望,但不幸的是,这只适用于 macOS/OSX。如何响应 SwiftUI 中的按键而不仅仅是 macOS/OSX?
【问题讨论】:
目前还没有通用的方法。UIKeyCommand
用于 ios,NSEvent
用于 macOS。关注SwiftUI iOS - how to capture hardware key events 和 How to detect keyboard events in SwiftUI on macOS? 可能会有所帮助
@Asperi 很不幸。感谢这些链接,我会得到它的工作!
【参考方案1】:
更新:SwiftUI 2 现在有.keyboardShortcut(_:modifiers:)
。
旧答案:
感谢@Asperi 为我指明了正确的方向,我现在已经成功地完成了这项工作。
解决方案是使用UIKeyCommand
。这是我所做的,但您可以根据自己的情况进行不同的调整。
我有一个名为AppState
的@EnvironmentObject
,它可以帮助我设置委托,因此他们的键盘输入可以根据当前显示的视图而有所不同。
protocol KeyInput
func onKeyPress(_ key: String)
class KeyInputController<Content: View>: UIHostingController<Content>
private let state: AppState
init(rootView: Content, state: AppState)
self.state = state
super.init(rootView: rootView)
@objc required dynamic init?(coder aDecoder: NSCoder)
fatalError("init(coder:) has not been implemented")
override func becomeFirstResponder() -> Bool
true
override var keyCommands: [UIKeyCommand]?
switch state.current
case .usingApp:
return [
UIKeyCommand(input: UIKeyCommand.inputEscape, modifierFlags: [], action: #selector(keyPressed(_:)))
]
default:
return nil
@objc private func keyPressed(_ sender: UIKeyCommand)
guard let key = sender.input else return
state.delegate?.onKeyPress(key)
AppState (@EnvironmentObject
):
class AppState: ObservableObject
var delegate: KeyInput?
/* ... */
场景委托看起来像:
let stateObject = AppState()
let contentView = ContentView()
.environmentObject(stateObject)
// Use a UIHostingController as window root view controller.
if let windowScene = scene as? UIWindowScene
let window = UIWindow(windowScene: windowScene)
window.rootViewController = KeyInputController(rootView: contentView, state: stateObject)
/* ... */
这使得现在根据按下的键添加功能变得非常容易。
符合KeyInput
,例如:
struct ContentView: View, KeyInput
/* ... */
var body: some View
Text("Hello world!")
.onAppear
self.state.delegate = self
func onKeyPress(_ key: String)
print(key)
guard key == UIKeyCommand.inputEscape else return
// esc key was pressed
/* ... */
【讨论】:
【参考方案2】:在 macOS 和 tvOS 上,有一个 onExitCommand(perform:)
修饰符用于查看。
来自 Apple 的documentation:
设置在视图获得焦点时响应接收到退出命令而触发的操作。
用户通过按 tvOS 上的菜单按钮或 macOS 上的退出键来生成退出命令。
例如:
struct ContentView: View
var body: some View
VStack
TextField("Top", text: .constant(""))
.onExitCommand(perform:
print("Exit from top text field")
)
TextField("Bottom", text: .constant(""))
.onExitCommand(perform:
print("Exit from bottom text field")
)
.padding()
【讨论】:
上述示例在 macOS 上运行良好,但是,如果 TextField 位于 ScrollView 而不是 VStack 中,则不会执行 TextField 的 onExitCommand 操作。向 ScrollView 添加 onExitCommand 修饰符将捕获转义,但不会直接告诉您哪个(如果有)有焦点。【参考方案3】:更简单的解决方案是 .onExitCommand 修饰符。 正如@Chuck H 评论的那样,如果文本字段在 ScrollView 内或在我的情况下为 Section 内,则这不起作用。 但我发现,只需将 TextField 及其 .onExitCommand 嵌入到 HStack 或 VStack 中,.onExitCommand 就可以正常工作。
HStack // This HStack is for .onExitCommand to work
TextField("Nombre del proyecto", text: $texto)
.onExitCommand(perform:
print("Cancelando edición....")
)
.textFieldStyle(PlainTextFieldStyle())
.lineLimit(1)
【讨论】:
非常感谢!!!节省了我的时间,您的解决方案也适用于List
!你摇滚!
只是想知道有没有办法检测 TextField 何时失去焦点,不幸的是 @FocusedState didSet 没有检测到它
.onChange(of: isNewFolderInFocus) newValue in
似乎是检测所有值变化的方法。检测用户何时在文本字段外点击很有用以上是关于响应 SwiftUI 中的按键事件的主要内容,如果未能解决你的问题,请参考以下文章