Simple input accessory view on UIViewController
I want to have an input accessory view like you see in iMessage. The bar is always visible at the bottom of the screen. When the TextField gets focus, the keyboard jumps in and the bar automatically moves up, while sticking at the top of the keyboard.
Input accessory must be visible even if no TextInput has focus
In UIKit the ViewController can override inputAccessory and become the first Responder. So the Input accessory is permanentely visible. I could not find a way to do this in SwiftUI.
Keyboard and accessory must shrink the scrollview
When the keyboard pops up, the scroll view which holds the messages and fills the screen must shrink accordingly. The same applies when dragging the scrollview down und moving the keyboard interactively. The view has to be resized while dragging.
Most important: must work when dragging a scrollview down and moving the keyboard interactively
The most important requirement is that this must work smoothly when dragging then scrollview which moves the keyboard down interactively. It's the same behavior in iMessage or WhatsApp. The input accessory must move while dragging and also the scrollview must be resized. After the keyboard is dismissed the toolbar / input accessory must stay visible at bottom.
Not a duplicate
I read InputAccessoryView / View Pinned to Keyboard with SwiftUI, but this is not similar to my question. I need the toolbar to be visible, even if the keyboard is not shown. Also the accepted answer in that question does not work properly with iOS 15. Dragging to dismiss the keyboard leaves a white gap.
UIKit works
In the UIKit implementation the holding view is an UITableViewController with keyboard dismiss interactive set. That means, when the keyboard is shown, you can drag the view and in the same move drag the keyboard and the accessory view down until the keyboard dissappears. The accessory view still remains at bottom though.
Implementing in UIKit is straight forward and looks like this. Everything comes right out of the box.
class ChatTableViewController: UITableViewController {
...
override var inputAccessoryView: UIView? {
return inputBar
}
override var canBecomeFirstResponder: Bool {
return true
}
...
}
UIKit implementation looks fine
Need help with SwiftUI (Xcode 13 beta 5, iOS 15)
For SwiftUI I already found out that in iOS 15 there is a placement: .keyboard parameter, that allows putting a toolbar on top of the keyboard. The problem is, that this only works if a TextField is focussed. If no TextField is focussed, i.e. the keyboard is not visible or dismissed, the toolbar is not visible.
struct Chat: View {
@State private var text: String = ""
var body: some View {
ZStack {
ScrollView {
ScrollViewReader { value in
LazyVStack(spacing: 15) {
ForEach((1...10), id: \.self) { x in
let isOwner = x % 2 == 0;
ChatMessage(isOwner: isOwner)
.padding(.leading, isOwner ? 90 : 0)
.padding(.trailing, isOwner ? 0 : 90)
}
}
.padding(.horizontal)
.padding(.top, 10)
.onAppear {
value.scrollTo(10, anchor: .bottomTrailing)
}
}
}
.background(Color.red)
.ignoresSafeArea(.keyboard)
VStack {
// Toolbar only visible if this box has focus
TextField("", text: $text)
.padding(10)
.background(Color(.systemGray6))
.cornerRadius(5)
}
}
.navigationBarTitle("test")
.navigationBarTitleDisplayMode(.inline)
.onAppear {
UIScrollView.appearance().keyboardDismissMode = .interactive
}
.toolbar {
ToolbarItemGroup(placement: .keyboard) {
HStack {
TextField("", text: $text)
.padding(10)
.background(Color(.systemGray6))
.cornerRadius(5)
Text("Hello")
}
.padding(.vertical, 5)
.padding(.horizontal)
.background(Color.blue)
}
}
}
}