0

From a UX point, the goal is to commit the input when people click anywhere outside the TextField.
For example, if the input is for renaming an item, then when clicked outside, we store the input as the new item name, and replace the TextField with a Text to show the new item name.

I assume it is an expected standard behavior, but please let me know if it is against Apple's MacOS standards.

Is there a standard / conventional way to achieve it with SwiftUI, or with some AppKit workaround?

As I see, onCommit is only triggered when we hit the Return key, not when we click outside the TextField. So what I think what I need to figure out is how to detect clicking outside.

What I considered:

  1. Some built-in view modifier on the TextField which activates this behavior, but I couldn't find any.
  2. Detect focus loss or editingChanged of the TextField, but by default, when we click on the background / a button / a text, the TextField doesn't lose focus, nor is the editingChanged event triggered.
  3. If the TextField is focused, add an overlay on the ContentView with an onTapGesture modifier, but then it would take two taps to trigger a button when a TextField is focused.
  4. Add an onTapGesture modifier on the ContentView, which calls a focusOut method, but it doesn't receive the event when people tap on a child view that also has an onTapGesture on it.
  5. Improving on 4, also call focusOut from the onTapGesture callback of all child views. So far this is the only viable option I see, but I'm not sure it is a good pattern to put extra code in all onTapGestures, just in order to customize TextField behavior.

Example code:

import SwiftUI

@main
struct app: App {
    @State private var inputText: String = ""

    var body: some Scene {
        WindowGroup {
            ZStack {
                Color.secondary.onTapGesture { print("Background tapped") }
                VStack {
                    Text("Some label")
                    Button("Some button") { print("Button clicked") }
                    TextField(
                        "Item rename input",
                        text: $inputText,
                        onCommit: { print("Item rename commit") }
                    )
                }
            }
        }
    }
}
bzyr
  • 289
  • 1
  • 11
  • Does this answer your question https://stackoverflow.com/a/58988238/12299030? – Asperi Mar 14 '22 at 12:58
  • @Asperi Not entirely. As I understand, your code answers how to programatically take focus from the TextField. But my question is also about when/where to call your code - please see options 3-5 which I listed. – bzyr Mar 14 '22 at 13:25

0 Answers0