I’m running into an issue with TextField focus behavior in SwiftUI.
By default, when I set focus to a TextField programmatically (using @FocusState), SwiftUI behaves like AppKit — the entire contents of the text field are selected. This is causing problems for my use case, because I want the caret placed at the end of the text without selecting everything.
How I solved this in AppKit
In AppKit, I worked around this by subclassing NSTextField and
overriding becomeFirstResponder to
adjust the editor’s selection:
override func becomeFirstResponder() -> Bool {
let responderStatus = super.becomeFirstResponder()
// Ensure caret is placed at the end, no text selected
if let editor = self.currentEditor() {
let selectedRange = editor.selectedRange
editor.selectedRange = NSRange(location: selectedRange.length, length: 0)
}
return responderStatus
}
This successfully prevented AppKit from auto-selecting the entire string when focus changed.
The problem in SwiftUI
Now I see the same auto-select behavior in SwiftUI when I toggle focus with @FocusState. But unlike AppKit, SwiftUI doesn’t expose the underlying NSTextView or UITextField APIs, so I can’t directly adjust the selection or caret position.
Questions:
Is there a way in SwiftUI to control the caret/selection behavior when a TextField becomes focused? Is there a built-in modifier or @FocusState trick I’m missing?
Has anyone found a reliable SwiftUI-idiomatic approach to ensure the caret is placed at the end of the text instead of selecting all text?
update:
adding my swiftUI code below:
struct TextFieldUI: View {
@ObservedObject var pModel:TextFieldModel
@FocusState private var pIsFocusedState: Bool
var body: some View {
VStack(spacing: 20) {
TextField(pModel.placeholder, text: $pModel.text)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
.focused($pIsFocusedState)
.onChange(of: pModel.isFocused) { old, newValue in
pIsFocusedState = newValue
}
.onChange(of: pIsFocusedState) { old, newValue in
pModel.isFocused = newValue
}
.onAppear {
pIsFocusedState = pModel.isFocused
}
Toggle("Secure Mode", isOn: $pModel.isSecure)
.padding()
}
.padding()
}
}