react-native
react-native copied to clipboard
Feature: Improvements to `automaticallyAdjustKeyboardInsets`
Summary
This PR builds on top of #31402, which introduced the automaticallyAdjustsScrollIndicatorInsets functionality. It aims to fix one of RN's longstanding pain point regarding the keyboard.
The changes provide a better way of handling the ScrollView offset when a keyboard opens. Currently, when a keyboard opens we apply an offset to the Scrollview that matches the size of the keyboard. This approach is great if we are using an InputAccessoryView but if we have multiple TextInputs in a ScrollView; offsetting the content by the size of the keyboard doesn't yield the best user experience.
Changelog
[General] [Changed] - Changes offset behaviour when keyboard opens in ScrollView with automaticallyAdjustsScrollIndicatorInsets
Test Plan
The videos below compare the current and proposed behaviors for the following code:
<ScrollView
automaticallyAdjustKeyboardInsets
keyboardDismissMode="interactive">
{[...Array(10).keys()].map(item => (
<CustomTextInput placeholder={item.toString()} key={item} />
))}
</ScrollView>
| Current behavior | Proposal |
|---|---|
As can be seen in the video, the current behavior applies an offset to the ScrollView content regardless of where the TextInput sits on the screen.
The proposal checks if the TextInput will be covered by the keyboard, and only then applies an offset. The offset applied is not the full size of the keyboard but instead only the required amount so that the TextInput is a specific distance above the top of the keyboard. This achieves a less "jumpy" experience for the user.
Open question: Should the distance between the top of the keyboard and the bottom of the TextInput be customizable with a prop?
CGFloat textFieldBottom = textFieldFrame.origin.y + textFieldFrame.size.height + 32; // What should be the default? Should we allow a prop to be passed?
CGFloat contentDiff = textFieldBottom - endFrame.origin.y;
The proposal doesn't change the behavior of the ScrollView offset when an InputAccessory view is used, since it checks if the TextField that triggered the keyboard is a descendant of the ScrollView or not.
Why not use other existing solutions?
RN ecosystem offers other alternatives for dealing with a keyboard inside a ScrollView, such as a KeyboardAvoidingView or using third party libraries like react-native-keyboard-aware-scroll-view. But as shown in the recordings below, these solutions don't provide the smoothness or behavior that can be achieved with automaticallyAdjustsScrollIndicatorInsets.
| KeyboardAvoidingView | rn-keyboard-aware-scroll-view |
|---|---|
As shown in the videos, the TextInput is hidden by the keyboard for a split second before becoming visible.
Code for the videos above:
// KeyboardAvoidingView
<KeyboardAvoidingView
style={{flex: 1, flexDirection: 'column', justifyContent: 'center'}}
behavior="padding"
enabled>
<ScrollView>
{[...Array(10).keys()].map(item => (
<CustomTextInput placeholder={item.toString()} key={item} />
))}
</ScrollView>
</KeyboardAvoidingView>
// rn-keyboard-aware-scroll-view
<KeyboardAwareScrollView>
{[...Array(10).keys()].map(item => (
<CustomTextInput placeholder={item.toString()} key={item} />
))}
</KeyboardAwareScrollView>
| Platform | Engine | Arch | Size (bytes) | Diff |
|---|---|---|---|---|
| android | hermes | arm64-v8a | 6,995,258 | -107,005 |
| android | hermes | armeabi-v7a | 6,372,106 | -99,338 |
| android | hermes | x86 | 7,408,196 | -111,403 |
| android | hermes | x86_64 | 7,272,145 | -105,881 |
| android | jsc | arm64-v8a | 8,861,159 | -105,874 |
| android | jsc | armeabi-v7a | 7,599,671 | -98,206 |
| android | jsc | x86 | 8,918,904 | -110,291 |
| android | jsc | x86_64 | 9,402,268 | -104,742 |
Base commit: 04c286c3c203ffeca414ecbc5f3e1d017520aefa Branch: main
| Platform | Engine | Arch | Size (bytes) | Diff |
|---|---|---|---|---|
| ios | - | universal | n/a | -- |
Base commit: 04c286c3c203ffeca414ecbc5f3e1d017520aefa Branch: main
PR build artifact for 6cd100a99bf97c3b1a4c7dc008236f5af34123dd is ready.
To use, download tarball from "Artifacts" tab in this CircleCI job then run yarn add <path to tarball> in your React Native project.
PR build artifact for 6cd100a99bf97c3b1a4c7dc008236f5af34123dd is ready.
To use, download tarball from "Artifacts" tab in this CircleCI job then run yarn add <path to tarball> in your React Native project.
For android (Java/Kotlin) we can use;
WindowInsetsapi to get the height of the soft. keyboard
UIKit seems to have api's like view.keyboardLayoutGuide and, UIKeyboardLayoutGuide
PS: Providing prop to adjust it manually seems nice customization but, using native api's to calculate the height would be more appropriate in this case.
- hardcoding the height will impact the UX
For android (Java/Kotlin) we can use;
WindowInsetsapi to get the height of the soft. keyboardUIKit seems to have api's like view.keyboardLayoutGuide and, UIKeyboardLayoutGuide
PS: Providing prop to adjust it manually seems nice customization but, using native api's to calculate the height would be more appropriate in this case.
- hardcoding the height will impact the UX
Hi, the hardcoded value represents the distance between the bottom of the TextInput and the top of the Keyboard. Not the size of the keyboard
Please see the screenshot below for reference. IMO the default value should be 0 and we should allow the user to customize this distance.
Hi, the hardcoded value represents the distance between the bottom of the TextInput and the top of the Keyboard. Not the size of the keyboard
Got it.. Thanks. 😄
Then adding a prop for it and keeping its default value resonable; makes sense..
I've just patched it into 0.68.5, works as a charm. I had to hard code the distance between the input and the keyboard because I was using an absolute positioned element at the bottom of the screen so I believe a prop would be great to adjust it.
I've just patched it into 0.68.5, works as a charm. I had to hard code the distance between the input and the keyboard because I was using an absolute positioned element at the bottom of the screen so I believe a prop would be great to adjust it.
Hey, glad to hear it worked! Do you have any suggestions regarding what this prop should be called? Looking for ideas :)
bottomOffset, bottomKeyboardOffset , bottomOffsetPadding 🤔
PR build artifact for 24ce8b590789805679632d474ed59b1b821862ce is ready.
To use, download tarball from "Artifacts" tab in this CircleCI job then run yarn add <path to tarball> in your React Native project.
PR build artifact for 24ce8b590789805679632d474ed59b1b821862ce is ready.
To use, download tarball from "Artifacts" tab in this CircleCI job then run yarn add <path to tarball> in your React Native project.
bottomOffset,bottomKeyboardOffset,bottomOffsetPadding🤔
Added the prop and named it bottomKeyboardOffset. Thank you for the suggestions! @efstathiosntonas
PR build artifact for 42088b6e7c7b74f292ee35df1c24642b711006dd is ready.
To use, download tarball from "Artifacts" tab in this CircleCI job then run yarn add <path to tarball> in your React Native project.
PR build artifact for 42088b6e7c7b74f292ee35df1c24642b711006dd is ready.
To use, download tarball from "Artifacts" tab in this CircleCI job then run yarn add <path to tarball> in your React Native project.
PR build artifact for e2891a7eafe4ba4d447322ca0cca19814a6a0d18 is ready.
To use, download tarball from "Artifacts" tab in this CircleCI job then run yarn add <path to tarball> in your React Native project.
PR build artifact for e2891a7eafe4ba4d447322ca0cca19814a6a0d18 is ready.
To use, download tarball from "Artifacts" tab in this CircleCI job then run yarn add <path to tarball> in your React Native project.
@isidoro98 why did you close this ?
This is super! Why is this closed?