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 #37766

Conversation

adamaveray
Copy link
Contributor

@adamaveray adamaveray commented Jun 8, 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

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 behaviour Proposal
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

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

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

isidoro98 and others added 7 commits November 6, 2022 20:07
* main: (1824 commits)
  Convert FallbackJSBundleLoaderTest to Kotlin (facebook#37750)
  Migrate JSPackagerClientTest to Kotlin (facebook#37747)
  (refactor): kotlin-ify `ShareModuleTest.java` (facebook#37746)
  Upgrade `@react-native/codegen-typescript-test`'s Jest dependency to Jest 29 (facebook#37745)
  Move flow-typed definitions to repo root (facebook#37636)
  Convert InterpolatorTypeTest to Kotlin (facebook#37724)
  Update documentation of ReactHost.reload method (facebook#37691)
  Reduce visibility of ReactHost.destroy() method (facebook#37693)
  Reduce visibility in React Context (facebook#37695)
  Remove InstanceHandleHelper as unused (facebook#37740)
  Convert CompositeReactPackageTest to Kotlin (facebook#37734)
  Add license header to SetSpanOperation.java
  Convert FakeEventDispatcher to kotlin (facebook#37739)
  Convert FakeRCTEventEmitter to Kotlin (facebook#37733)
  Convert InteropModuleRegistryTest to Kotlin (facebook#37735)
  Bump `autorebase.yml` to `v1.8` (facebook#37584)
  Convert StackTraceHelperTest to Kotlin (facebook#37741)
  Convert BlobModuleTest class to Kotlin (facebook#37719)
  Update prettier to v2.8.8 (facebook#37738)
  Introduce BoltsFutureTask class to avoid leaking bolts.Task into ReactHost API (facebook#37744)
  ...

# Conflicts:
#	BUCK
#	packages/react-native/React/Views/UIResponder+FirstResponder.h
#	packages/react-native/React/Views/UIResponder+FirstResponder.m
@facebook-github-bot
Copy link
Contributor

Hi @adamaveray!

Thank you for your pull request and welcome to our community.

Action Required

In order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you.

Process

In order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.

Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks!

@facebook-github-bot
Copy link
Contributor

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks!

@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 Jun 8, 2023
@javache
Copy link
Member

javache commented Jun 8, 2023

Why can't bottomKeyboardOffset be implemented using a marginBottom on the TextInput component?

@adamaveray
Copy link
Contributor Author

@javache Thanks for reviewing and for your feedback. This PR was initially put together by another contributor so I wasn't aware of the private API usage. I've overhauled how it's all implemented now to avoid that and to remove the niche bottomKeyboardOffset value. It now replicates native iOS focus behaviour as closely as I could get it, which can be tested in the updated RN Tester ScrollViewKeyboardInsets screen. Please let me know if you have any feedback on the changes!

* main: (135 commits)
  translation auto-update for i18n/twilight.config.json on master
  Interop: Introduce Bridge proxy
  Remove okhttp internal util usage (facebook#37843)
  Update debian to fix CI while updating Node (facebook#37841)
  fix: foreground ripple crash on api < 23 (facebook#37901)
  Re-add the top level LICENSE file (facebook#37916)
  Deploy 0.209.0 to xplat (facebook#37921)
  Re-enable direct debugging with JSC on iOS 16.4+ (facebook#37914)
  add emitObjectProp in parser primitives (facebook#37904)
  Make React-utils its own pod (facebook#37659)
  feat: allow custom assignment of rootView to rootViewController (facebook#37873)
  Switch xplat prettier config to hermes plugin (facebook#37915)
  Set iOS AppState to inactive when app is launching (facebook#37690)
  Use `fileExists` in replace_hermes script (facebook#37911)
  (docs): fix license url (facebook#37909)
  Revert D46719890: Re-enable direct debugging with JSC on iOS 16.4+
  Re-enable direct debugging with JSC on iOS 16.4+ (facebook#37874)
  Fix component type references in xplat (facebook#37903)
  Remove usage of passthroughAnimatedPropExplicitValues in ScrollViewStickyHeader (facebook#37867)
  test runtime lifecycle callback (facebook#37897)
  ...
@analysis-bot
Copy link

analysis-bot commented Jun 16, 2023

Platform Engine Arch Size (bytes) Diff
android hermes arm64-v8a 8,969,902 +8
android hermes armeabi-v7a n/a --
android hermes x86 n/a --
android hermes x86_64 n/a --
android jsc arm64-v8a 9,563,165 +7
android jsc armeabi-v7a n/a --
android jsc x86 n/a --
android jsc x86_64 n/a --

Base commit: 9faf256
Branch: main

@adamaveray adamaveray requested a review from javache June 16, 2023 05:46
@javache javache requested a review from philIip June 19, 2023 09:47
Copy link
Member

@javache javache left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@philIip Any concerns with this? You mentioned some changes coming to keyboard layout events with iOS17.

@adamaveray
Copy link
Contributor Author

@javache Thanks for the additional review; I've made the requested changes and also done some additional testing & updates to more closely replicate the native bottom keyboard offset amount (which I initially thought was due to an empty selection but have since found it's actually single line aka UITextField vs multiline aka UITextView). Please let me know if you have any more feedback.

@adamaveray
Copy link
Contributor Author

@javache Just checking in to see if there are any other issues you can see with this PR that are standing in the way of it potentially being merged?

@javache
Copy link
Member

javache commented Jul 14, 2023

Thanks for adding the example (and sorry for the delay)! This mostly looks good to me, but would like to clarify what the coordinates used are relative to.

@adamaveray
Copy link
Contributor Author

Thanks for following up! I've implemented those requests & suggestions, let me know if you have any other feedback 🙌

@facebook-github-bot facebook-github-bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Sep 9, 2023
@javache
Copy link
Member

javache commented Sep 13, 2023

This looks good to merge now, but there are conflicts with main in RCTBaseTextInputView.m. Could you resolve those?

* main: (1012 commits)
  Add simple constructor for JSError (facebook#39415)
  Breaking: per-node pointScaleFactor (facebook#39403)
  Implement "tickleJS" for Hermes (facebook#39289)
  Add thread idle indicator (facebook#39206)
  Unblock `yarn android` on main (facebook#39413)
  Remove Codegen buck-oss scripts as they're unused (facebook#39422)
  Immediately dispatch events to the shared C++ infrastructure to support interruptability (facebook#39380)
  Fix race condition in Binding::reportMount (facebook#39420)
  Clone free state progression (facebook#39357)
  fix: return the correct default trait collection (facebook#38214)
  Read the React Native version and set the new arch flag properly (facebook#39388)
  Deprecate default_flags in Podfile (facebook#39389)
  Create Helper to read the package.json from Cocoapods (facebook#39390)
  Create helper to enforce the New Arch enabled for double released (facebook#39387)
  Remove layoutContext Drilling (facebook#39401)
  Remove JNI Binding usage of layoutContext (facebook#39402)
  Extract isBaselineLayout() (facebook#39400)
  Refactor and separate layout affected nodes react marker (facebook#39249)
  Bump AGP to 8.1.1 (facebook#39392)
  Fix broken Gradle Sync when opening the project with Android Studio (facebook#39412)
  ...
@adamaveray
Copy link
Contributor Author

@javache The merge conflicts should be resolved now. Let me know if anything else needs updating!

@facebook-github-bot
Copy link
Contributor

@javache has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@facebook-github-bot facebook-github-bot added the Merged This PR has been merged. label Sep 15, 2023
@facebook-github-bot
Copy link
Contributor

@javache merged this pull request in 9ca1660.

@javache
Copy link
Member

javache commented Sep 15, 2023

Thanks for contributing! It'd be great if you could make this feature compatible with the new architecture too.

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. Merged This PR has been merged. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants