Mobile Dev

SwiftUI TextField Sheet Keyboard Delay: Fix Multi-Taps

Fix SwiftUI TextField in sheets requiring multiple taps for keyboard due to onAppear state loading, unwanted text highlight. Use @FocusState, UIKit wrappers, pre-load state for instant iOS response.

1 answer 1 view

SwiftUI Edit Sheet TextField Keyboard Delay: Requires Multiple Taps and Unwanted Text Selection Highlight

In a SwiftUI app using SwiftData for iOS, the TextField in an edit sheet (EditPolicyView) does not respond to the first tap when the sheet loads. It requires multiple taps for the keyboard to appear, and the text field highlights for cut/copy/paste.

This occurs immediately after presenting the sheet. The form uses @State variables initialized with initial props, loaded in .onAppear with a hasLoadedInitialValues flag to load only once.

Key Code (Simplified Reproduction):

swift
struct EditPolicyFormView: View {
 // ... initial props
 @State private var name = ""
 @State private var hasLoadedInitialValues = false

 var body: some View {
 NavigationStack {
 Form {
 Section {
 TextField(nameFieldLabel, text: $name)
 } header: {
 Text(basicInfoSectionHeader)
 }
 }
 // ... toolbar
 .onAppear {
 if !hasLoadedInitialValues {
 name = initialName
 // Load other fields...
 hasLoadedInitialValues = true
 }
 }
 }
 }
}

Full Context:

  • Wrapper EditPolicyView passes EditPolicyData (non-observable struct from PolicyItem) to avoid SwiftData observation issues.
  • Attachments loading is disabled for testing.
  • Tested on iPhone (simulator/device), persists across attempts.

What causes this keyboard responsiveness issue in SwiftUI sheets with delayed state loading? How to fix TextField for immediate tap response without selection highlight?

Ever dealt with a SwiftUI TextField in a sheet that ignores your first tap, highlights the text like it’s mocking you, and only pops up the keyboard SwiftUI style after a second poke? That’s a classic gotcha in SwiftUI sheets with SwiftUI TextField—the SwiftUI onAppear loading your SwiftUI state vars triggers a view rebuild right after the sheet renders, nuking any focus and forcing that unwanted selection highlight on tap one. The fix? Swap to @FocusState with smart timing, or drop a UIKit UITextField wrapper to grab focus pre-render for instant keyboard SwiftUI without delays or multi-taps.


Contents


Why TextFields Delay in SwiftUI Sheets

Picture this: your EditPolicyFormView sheet slides up smooth as butter. You tap the SwiftUI TextField, expecting the keyboard SwiftUI magic. Nope. Text highlights for copy-paste, cursor blinks mockingly, and nothing happens until tap two. Why?

Blame the SwiftUI state dance in .onAppear. That if !hasLoadedInitialValues { name = initialName; hasLoadedInitialValues = true } block fires after the sheet’s initial render. SwiftUI rebuilds the view hierarchy—poof, any nascent focus on your SwiftUI TextField vanishes. The field acts “selected” on first tap (hence the highlight), but the keyboard waits for a real edit intent on the next one.

It’s not your SwiftData wrapper or iPhone quirks (simulator or device). This bites anyone loading SwiftUI state post-presentation. Stack Overflow threads nail it: without hacks, focus flips every other sheet open because state mutations interrupt the focus lifecycle. Reddit users gripe the same—SwiftUI blocks keyboard until the view fully settles, but your async state load prolongs that limbo.

And the highlight? That’s iOS saying “text selected, now edit me.” Frustrating for UX, especially in edit flows.


Fix with @FocusState and Timing Tricks

SwiftUI’s @FocusState is your first line of defense for SwiftUI TextField focus. Ditch pure @State reliance and wire it up like this:

swift
@FocusState private var nameFieldFocused: Bool
@State private var name = ""
@State private var hasLoadedInitialValues = false

var body: some View {
 Form {
 Section {
 TextField(nameFieldLabel, text: $name)
 .focused($nameFieldFocused)
 } header: {
 Text(basicInfoSectionHeader)
 }
 }
 .onAppear {
 if !hasLoadedInitialValues {
 name = initialName // Load here, but focus next
 hasLoadedInitialValues = true
 nameFieldFocused = true // Attempt focus post-load
 }
 }
}

Does it work? Sometimes. But if state mutation still rebuilds mid-focus, toggle fails. Hack it with a micro-delay:

swift
.onAppear {
 // Load state first
 if !hasLoadedInitialValues { /* load */ }
 // Then focus after a beat
 DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
 nameFieldFocused = true
 }
}

Paul Hudson over at Hacking with Swift pushes .defaultFocus($focusedField) for even slicker setup—set it once, and it grabs focus on sheet entry without onAppear drama. Pair with initial: true to consolidate:

swift
.focused($nameFieldFocused)
.defaultFocus($nameFieldFocused)

Pro tip: For multi-field forms, chain with .onSubmit { nameFieldFocused = false; nextFieldFocused = true }. No more tabbing woes.

But delays persist? Time to escalate.


UIKit Wrapper for Instant Keyboard

When native SwiftUI TextField flakes, bridge to UIKit. Wrap a hidden UITextField, make it first responder before your sheet paints, and boom—keyboard SwiftUI in one fluid animation, no highlight, no multi-tap.

Here’s the money code from Stack Overflow pros:

swift
struct FocusableTextField: UIViewRepresentable {
 @Binding var text: String
 @FocusState.Binding var isFocused: Bool

 func makeUIView(context: Context) -> UITextField {
 let field = UITextField()
 field.delegate = context.coordinator
 field.addTarget(context.coordinator, action: #selector(Coordinator.textChanged), for: .editingChanged)
 return field
 }

 func updateUIView(_ uiView: UITextField, context: Context) {
 uiView.text = text
 if isFocused && uiView.window != nil { uiView.becomeFirstResponder() }
 }

 func makeCoordinator() -> Coordinator { Coordinator(self) }

 class Coordinator: NSObject, UITextFieldDelegate {
 var parent: FocusableTextField
 init(_ parent: FocusableTextField) { self.parent = parent }

 @objc func textChanged(_ textField: UITextField) {
 parent.text = textField.text ?? ""
 }

 func textFieldDidBeginEditing(_ textField: UITextField) { parent.isFocused = true }
 func textFieldDidEndEditing(_ textField: UITextField) { parent.isFocused = false }
 }
}

Use it in your sheet:

swift
FocusableTextField(text: $name, isFocused: $nameFieldFocused)
 .opacity(0) // Hide the UIKit field, proxy to visible SwiftUI one

Why killer? becomeFirstResponder kicks in pre-render, syncing keyboard with sheet slide-up. This GitHub gem packages it reusable—no delays, even on iOS 17+.

Tradeoff? A smidge more code, but buttery UX. Beats async hacks.


Rethink State Loading in SwiftUI onAppear

Root fix: Don’t load in .onAppear. Pre-load SwiftUI state before presenting the sheet. Pass fully initialized data via props—no flags, no mutations.

Refactor EditPolicyView:

swift
struct EditPolicyFormView: View {
 let initialName: String // Passed ready-to-go
 @State private var name: String

 init(initialName: String) {
 self.initialName = initialName
 self._name = State(initialValue: initialName) // Init here!
 }

 var body: some View {
 Form { /* TextField($name) */ }
 // No onAppear needed
 }
}

Present like: .sheet(isPresented: $showingEdit) { EditPolicyFormView(initialName: policyItem.name) }. Boom—state settled on init, focus sticks natively.

If SwiftData forces observation, snapshot to struct pre-present. Fatbobman’s deep dive echoes: minimize post-appear mutations.


SwiftUI Sheet Testing

Test on device/sim across iOS 17-18 (2026 vibes, but behaviors stable). Toggle attachments back—shouldn’t bite. Watch for .presentationDetents or Form nesting amplifying delays.

Edge: Multiple sheets? Cascade focus with @FocusState array. Toolbar dismiss? .onDisappear { isFocused = false }.

Real talk—UIKit wins for pixel-perfect keyboard timing, but native evolves fast. Monitor WWDC for focus polish.


Sources

  1. TextField in sheet that accepts focus immediately - Stack Overflow
  2. SwiftUI - How to focus a TextField within a sheet - Stack Overflow
  3. Present sheet with TextField and keyboard in single animation - Stack Overflow
  4. How to open keyboard fast? - Reddit r/SwiftUI
  5. SwiftUI: How to make TextField become first responder - Stack Overflow
  6. How to make a TextField have default focus - Hacking with Swift
  7. SwiftUI TextField Advanced — Events, Focus, and Keyboard
  8. FocusOnAppear GitHub Repo

Conclusion

Nail SwiftUI TextField woes in SwiftUI sheets by pre-loading state or bridging UIKit for that instant keyboard SwiftUI hit—no more highlight headaches or tap marathons. Start with @FocusState tweaks, escalate to wrappers if needed, and rethink .onAppear entirely for robust edit flows. Your users will thank you with single-tap bliss.

Authors
Verified by moderation
Moderation
SwiftUI TextField Sheet Keyboard Delay: Fix Multi-Taps