Ideal Cross-Platform Return Key Behavior with SwiftUI's TextField
June 6, 2024
I’ve recently been working on a cross-platform chat application for macOS and iOS written in SwiftUI. One of the challenges I ran into was getting the return key to behave as expected on all platforms in a multi-line TextField
. The behavior I wanted was for the return key (↩) to send the message and for option return (⌥↩) to insert a newline. This is the behavior of a chat application like iMessage, and it’s what I wanted to replicate.
I started with something like this:
struct EntryField: View {
let titleKey: LocalizedStringKey
@Binding var text: String
let onSend: (String) -> Void
var body: some View {
TextField(titleKey, text: _text, axis: .vertical)
.onSubmit {
onSend(text)
}
}
}
If using an AppKit macOS application, you get the desired return key behavior for free. You also get it with iOS / iPadOS and a connected hardware keyboard. However, when using a Catalyst macOS app target (at least on Sonoma), the return key always inserts a return character and never triggers the onSubmit
closure. 🤔
So as a workaround, you can add an invisible Button
that responds to the return and performs the passed in closure.
TextField(titleKey, text: _text, axis: .vertical)
.onSubmit {
onSend(text)
}
.background {
Button {
onSend(text)
} label: {
EmptyView()
}
.keyboardShortcut(.defaultAction)
.opacity(0)
}
Now the field behaves as expected on all platforms. The return key sends the message, and option return inserts a newline. This is a simple solution (hack?) that works well for my little app, but if you have a better solution I’d love to hear it!