SwiftUI在macOS中监听键盘事件,在TextEditor中实现Enter提交,Enter+Option换行

SwiftUI监听键盘事件

SwiftUI中的TextEditor没有类似于TextField的onCommit()或onSubmit()方法,所以无法直接使用Enter键做出此动作。

下面是一个变通方法,使用NSEvent监听实现Enter提交,Enter+Option换行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import SwiftUI

struct TestView: View {
@State private var text = ""
var body: some View {
TextEditor(text: $text)
/*
此处的onChange()是确保按下Enter后能清空TextEditor
SwiftUI有Bug,text = ""并不能完全清空TextEditor,它可能存在换行行为
异步操作模拟按下"delete键",保证TextEditor完全清空
*/
.onChange(of: message.messages.count, perform: { _ in
text = ""
DispatchQueue.main.asyncAfter(deadline: .now() + 0.001) {
NSApplication.shared.sendAction(#selector(NSText.deleteBackward(_:)), to: nil, from: nil)
}
})
.font(.title2)
.padding(5)
.focused($isFocused)
.frame(minHeight: 15, maxHeight: 180)
.fixedSize(horizontal: false, vertical: true)//使其支持动态调整高度
.scrollContentBackground(.hidden)//去除白色的背景,使其透明
.overlay(
RoundedRectangle(cornerRadius: 15)
.stroke(isFocused == true ? Color.blue : Color.secondary, lineWidth: isFocused == true ? 2 : 0.3)
)//圆角遮罩

/*
实现Enter提交,Enter+Option换行
注意不能将下面的代码放到NavigationStack里面,否则会提交两次
也就是说"Enter提交"会重复两次,这是不符合预期的
*/
.onAppear {
NSEvent.addLocalMonitorForEvents(matching: [.keyDown]) { nsevent in
if nsevent.keyCode == 36 && !nsevent.modifierFlags.contains(.option) {
if text.last != nil {
if !text.isEmpty, !text.allSatisfy({ $0.isNewline }) {
print("Enter提交")
}
}
}
return nsevent
}
}
}
}