Automatic keyboard avoidance seems to work fine if it’s a regular TextField
(i.e. one that doesn’t expand on an axis
), whether or not it is contained in a ScrollView
Keyboard avoidance also seems to work with the new TextField(_:text:axis)
introduced in iOS 16 if it’s simply placed in a VStack
without being wrapped in a ScrollView
. It will even continue to avoid the keyboard correctly as the height expands with more text.
But I can’t seem to get keyboard avoidance to work with TextField(_:text:axis)
if it is placed inside a ScrollView
I can employ the hacky method of using a ScrollViewReader
combined with DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(400))
to wrap the proxy.scrollTo()
when the TextField
is focused. This sort of works when you first focus the field, but I can’t seem to get the ScrollView
to continue to adjust its position as the TextField
expands.
Here is an example:
struct KeyboardAvoidingView: View {
@State var text = ""
var body: some View {
ScrollViewReader { proxy in
ScrollView {
VStack {
Color.red
.frame(height: 400)
Color.blue
.frame(height: 400)
TextField("Name", text: $text, axis: .vertical)
.padding(.vertical)
.onTapGesture {
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(400)) {
withAnimation(.default) {
proxy.scrollTo(0)
}
}
}
.onChange(of: text) { newValue in
proxy.scrollTo(0) // This doesn't seem to do anything
}
Spacer()
.frame(height: 0)
.id(0)
}
}
}
}
}
I guess I’m wondering whether this is expected behavior, or a bug. And regardless if it’s one or the other, I’m wondering if I can have an auto-expanding text field inside a scroll view that I can make avoid the keyboard even as the height of the field expands?
UPDATE: It turns out, the issue was with placing the TextField
inside a VStack
instead of a LazyVStack
. I assume ScrollView
doesn’t know what to do with just a regular VStack
in certain situations. If I replace the VStack
with a LazyVStack
in my example, everything works as expected!
2
Answers
I answered the question with the update posted above. The issue was with using
VStack
instead ofLazyVStack
This is a long time known bug in the
TextField
component, but you may achieve the desired behavior by using ananchor: .bottom
in theproxy.scrollTo
call of youronChange
.it’ll look like this:
You may need some additional work to handle the editing of upper parts of the text editor when it’s taller than your screen