Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Improvements to automaticallyAdjustKeyboardInsets #35224

Closed
wants to merge 5 commits into from
Closed

Feature: Improvements to automaticallyAdjustKeyboardInsets #35224

wants to merge 5 commits into from

Conversation

isidoro98
Copy link

@isidoro98 isidoro98 commented Nov 6, 2022

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
current.mov
proposal.mov

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 (customizable using the new bottomKeyboardOffset prop). This achieves a less "jumpy" experience for the user.

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
KeyboardAvoidingView.mov
rn-keyboard-aware-scroll-view.mov

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>

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Nov 6, 2022
@analysis-bot
Copy link

analysis-bot commented Nov 6, 2022

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: 04c286c
Branch: main

@analysis-bot
Copy link

analysis-bot commented Nov 6, 2022

Platform Engine Arch Size (bytes) Diff
ios - universal n/a --

Base commit: 04c286c
Branch: main

@pull-bot
Copy link

pull-bot commented Nov 6, 2022

PR build artifact for 6cd100a 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.

@pull-bot
Copy link

pull-bot commented Nov 6, 2022

PR build artifact for 6cd100a 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.

Comment on lines 332 to 334
// [32] is a placeholder. It's the distance between the bottom of the textfield and the top of the keyboard
// What value should be used? Should it be customizable with a prop?
CGFloat textFieldBottom = textFieldFrame.origin.y + textFieldFrame.size.height + 32;

This comment was marked as resolved.

@Pranav-yadav

This comment was marked as resolved.

@isidoro98
Copy link
Author

For android (Java/Kotlin) we can use; WindowInsets api 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

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.

@Pranav-yadav
Copy link
Contributor

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..

@efstathiosntonas
Copy link

efstathiosntonas commented Nov 9, 2022

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.

@isidoro98
Copy link
Author

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 :)

@efstathiosntonas
Copy link

efstathiosntonas commented Nov 11, 2022

bottomOffset, bottomKeyboardOffset , bottomOffsetPadding 🤔

@pull-bot
Copy link

PR build artifact for 24ce8b5 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.

@pull-bot
Copy link

PR build artifact for 24ce8b5 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
Copy link
Author

isidoro98 commented Nov 17, 2022

bottomOffset, bottomKeyboardOffset , bottomOffsetPadding 🤔

Added the prop and named it bottomKeyboardOffset. Thank you for the suggestions! @efstathiosntonas

@pull-bot
Copy link

PR build artifact for 42088b6 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.

@pull-bot
Copy link

PR build artifact for 42088b6 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 isidoro98 closed this Nov 17, 2022
@pull-bot
Copy link

PR build artifact for e2891a7 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.

@pull-bot
Copy link

PR build artifact for e2891a7 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.

@aliraza-noon
Copy link

@isidoro98 why did you close this ?

@casperstr
Copy link

This is super! Why is this closed?

facebook-github-bot pushed a commit that referenced this pull request Sep 15, 2023
Summary:
This is a reopened version of #35224 by isidoro98 which was closed without explanation, updated to resolve new merge conflicts and now includes an example in the RN-Tester app. Aside from that it is unchanged. Here is isidoro98's description from their original PR:

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:

[iOS] [Changed] - Scroll `ScrollView` text fields into view with `automaticallyAdjustsScrollIndicatorInsets`

Pull Request resolved: #37766

Test Plan:
The videos below compare the current and proposed behaviors for the following code:

```js
<ScrollView
  automaticallyAdjustKeyboardInsets
  keyboardDismissMode="interactive">
  {[...Array(10).keys()].map(item => (
    <CustomTextInput placeholder={item.toString()} key={item} />
  ))}
</ScrollView>
```

| Current behaviour | Proposal |
|-|-|
| ![https://user-images.githubusercontent.com/25139053/200194972-1ac5f1cd-2d61-4118-ad77-95c04d30c98d.mov](https://user-images.githubusercontent.com/25139053/200194972-1ac5f1cd-2d61-4118-ad77-95c04d30c98d.mov) | ![https://user-images.githubusercontent.com/25139053/200194990-53f28296-be11-4a47-be70-cec917d7deb1.mov](https://user-images.githubusercontent.com/25139053/200194990-53f28296-be11-4a47-be70-cec917d7deb1.mov) |

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 (customizable using the new `bottomKeyboardOffset` prop). This achieves a less "jumpy" experience for the user.

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 |
|-|-|
| ![https://user-images.githubusercontent.com/25139053/200195145-de742f0a-6913-4099-83c4-7693448a8933.mov](https://user-images.githubusercontent.com/25139053/200195145-de742f0a-6913-4099-83c4-7693448a8933.mov) | ![https://user-images.githubusercontent.com/25139053/200195151-80745533-16b5-4aa0-b6cd-d01041dbd001.mov](https://user-images.githubusercontent.com/25139053/200195151-80745533-16b5-4aa0-b6cd-d01041dbd001.mov) |

As shown in the videos, the `TextInput` is hidden by the keyboard for a split second before becoming visible.

Code for the videos above:

```js
// 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>
```

 ```js
// rn-keyboard-aware-scroll-view
<KeyboardAwareScrollView>
  {[...Array(10).keys()].map(item => (
    <CustomTextInput placeholder={item.toString()} key={item} />
  ))}
</KeyboardAwareScrollView>
```

Reviewed By: sammy-SC

Differential Revision: D49269426

Pulled By: javache

fbshipit-source-id: 6ec2e7b45f6854dd34b9dbb06ab77053b6419733
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants