V8tr / KeyboardAvoidanceSwiftUI

How to move SwiftUI view up when keyboard appears https://www.vadimbulavin.com/how-to-move-swiftui-view-when-keyboard-covers-text-field/
https://www.vadimbulavin.com
The Unlicense
327 stars 36 forks source link

Tapping between TextFields breaks keyboard avoidance #1

Open bennokress opened 4 years ago

bennokress commented 4 years ago

To reproduce the issue, use 2 TextFields in need of keyboard awareness and tap the lower one. The scroll here will be correct, but tap the upper TextField while the keyboard is still showing and the bottom padding will be too little resulting in a (partially) hidden TextField. This issue seems to be caused by focusedTextInputBottom in the ViewModifier which is calculated based on the already scrolled View.

If I find the time I will look into this further myself. My current solution is taking the bottomPadding at the time of the calculation into account (in the ViewModifier):

var focusedTextInputBottom = UIResponder.currentFirstResponder?.globalFrame?.maxY ?? 0
focusedTextInputBottom` += self.bottomPadding

It seems to fix the problem, but I'm not sure about possible side effects and if this calculation is always right.

V8tr commented 4 years ago

Thanks for reporting the problem! I am afraid, I also don't have a solution yet.

I was able to reproduce the issue for multiple text fields inside ScrollView. The bottom padding is calculated correctly inside the view modifier, but the scroll view seem to behave oddly when we apply the padding. I am going to give it another try and hopefully find out the root cause.

wearhere commented 4 years ago

This fix makes general sense to me. Another way to reproduce the problem here is to switch between the English and Emoji keyboards: you will see the view oscillate up and down as (I think) it applies too little padding, then corrects, then applies too little padding again. You can see this especially clearly if you hardcode a tall keyboard height e.g. always return 500 here.

However, I'm not sure that adding self.bottomPadding is the exact right offset to use. In my return-500 example above, if I += self.bottomPadding, the view scoots upward indefinitely, a bit more each time I toggle keyboards. This is fixed (the view remains stable when I toggle keyboards) if I += self.bottomPadding / 2 instead. This is my basic view setup I'm testing that with:

VStack {
    VStack(alignment: .leading, spacing: 10) {
        Text("Tap and hold the ") + Text("Globe").bold() + Text(" icon")
        Text("Tap ") + Text("Searchboard").bold() + Text(" to switch keyboards")
    }
    TextField("", isEditing: $text)
}
.keyboardAdaptive()
wearhere commented 4 years ago

Corroborating the += self.bottomPadding / 2 bit above: I find that if I log

UIResponder.currentFirstResponder?.globalFrame?.maxY

a couple of seconds after updating the bottom padding, it's only translated upward by half self.bottomPadding. So, I now set self.bottomPadding = 2 * <the current calculation>, and then divide by 2 when preparing to set the padding to a new value (my previous comment), that's consistent.

Not sure why padding would only translate by half… maybe my view is trying to recenter itself somehow, like distribute the padding to the top too.