Implementing markdown text editor.
Each line of text is a TextField
.
Need to switch line on 🔼 and 🔽
First attempt:
TextField(...)
.onKeyPress(.downArrow) {
print("down")
return .handled
}
This event is never fired
Second attempt:
import SwiftUI
class CustomTextField: NSTextField {
override public var acceptsFirstResponder: Bool { true }
override func keyDown(with event: NSEvent) {
print("key down with character: \(String(describing: event.characters)) " )
super.keyDown(with: event)
}
}
struct LineEditor: NSViewRepresentable {
@Binding var text: String
init(_ text: Binding<String>) {
self._text = text
}
typealias NSViewType = CustomTextField
func makeNSView(context: Context) -> CustomTextField {
let view = CustomTextField(string: text)
view.delegate = context.coordinator
return view
}
func updateNSView(_ nsView: CustomTextField, context: Context) {
nsView.stringValue = text
}
class Coordinator: NSObject, NSTextFieldDelegate {
var parent: LineEditor
init(_ parent: LineEditor) {
self.parent = parent
}
func controlTextDidChange(_ obj: Notification) {
let field = obj.object as! NSTextField
self.parent.text = field.stringValue
}
}
func makeCoordinator() -> Coordinator {
return Coordinator(self)
}
}
keyDown
is never called either.
Question: how to track key press events properly in modern SwiftUI
?
For multiline texts you need to use TextEditor
.
But in case you still need to use set of TextFields like this you need sth like the following code.
(Just copy-paste the code. All works with any count of pre-defined TextFields :)
import Combine
import SwiftUI
struct ContentView: View {
@State var text1: String = "1234"
@State var text2: String = "2345"
@State var text3: String = "3456"
@State var text4: String = "4567"
@FocusState private var focusedField: Field?
var body: some View {
VStack {
TextField("", text: $text1)
.focused($focusedField, equals: .f1)
TextField("", text: $text2)
.focused($focusedField, equals: .f2)
TextField("", text: $text3)
.focused($focusedField, equals: .f3)
TextField("", text: $text4)
.focused($focusedField, equals: .f4)
}
.onAppear { keyboardUpDownSubscribe() }
}
}
extension ContentView {
func keyboardUpDownSubscribe() {
NSEvent.addLocalMonitorForEvents(matching: .keyDown) { (aEvent) -> NSEvent? in
print(aEvent.keyCode)
guard focusedField != nil else { return nil }
guard aEvent.specialKey != nil else { return aEvent }
switch aEvent.specialKey! {
case .downArrow:
focusNextField()
case .upArrow:
focusPreviousField()
default:
break
}
return aEvent
}
}
private enum Field: Int, CaseIterable {
case f1, f2, f3, f4
}
private func focusPreviousField() {
focusedField = focusedField.map {
Field(rawValue: $0.rawValue - 1) ?? Field.allCases.first!
}
}
private func focusNextField() {
focusedField = focusedField.map {
Field(rawValue: $0.rawValue + 1) ?? Field.allCases.last!
}
}
}