Goal: A ChatGPT-style composer fixed at the bottom, always above the keyboard, supports interactive dismissal, growing text, and iPad undocked/split keyboards.
Tried:
-
Pure SwiftUI: .safeAreaInset(.bottom) + .scrollDismissesKeyboard(.interactively) → during interactive drag the panel sometimes lags/briefly shows a gap; growing text can cause small jumps.
-
.toolbar(.keyboard) → not persistent when there’s no focus.
-
UIKit wrapper with inputAccessoryView via UIViewControllerRepresentable → smooth, but only visible while a field is first responder.
Minimal repro (SwiftUI):
import SwiftUI
struct ChatView: View {
@State private var text = ""
var body: some View {
List(0..<40, id: \.self) { i in Text("Message \(i)") }
.safeAreaInset(edge: .bottom) {
HStack {
TextField("Message", text: $text, axis: .vertical)
.lineLimit(1...6)
.textFieldStyle(.roundedBorder)
Button("Send") {}
}
.padding()
.background(.ultraThinMaterial)
}
.scrollDismissesKeyboard(.interactively)
}
}
Observed: On iPhone (iOS 17/18) there’s a brief 2–8 px gap/lag vs. keyboard during interactive dismissal; on iPad with undocked keyboard the panel may not “follow” the keyboard.
Question: In 2025, is there a pure SwiftUI pattern that reliably matches inputAccessoryView behavior (including interactive drag and undocked keyboards), or is the recommended approach still to embed a UIKit controller and use inputAccessoryView? Are there any Apple docs or sample code available for this?
Environment: Xcode 15/16, Swift 5.9/5.10, iOS 17