-
Notifications
You must be signed in to change notification settings - Fork 24.4k
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: ScrollView automaticallyAdjustKeyboardInsets
#31402
Feature: ScrollView automaticallyAdjustKeyboardInsets
#31402
Conversation
Sets the [isModalInPresentation](https://developer.apple.com/documentation/uikit/uiviewcontroller/3229894-ismodalinpresentation) property to `NO` if the Presentation Style is formSheet or pageSheet, otherwise `YES`
Controls whether the ScrollView should automatically adjust it's contentInset and scrollViewInsets when the Keyboard changes it's size. The default value is false.
Sets the [isModalInPresentation](https://developer.apple.com/documentation/uikit/uiviewcontroller/3229894-ismodalinpresentation) property to `NO` if the Presentation Style is formSheet or pageSheet, otherwise `YES`
This reverts commit b9b7d42.
Base commit: b494ae0 |
Base commit: b494ae0 |
Btw sorry for the commit history I just noticed that I had some old stuff on my fork's master 🙈 |
Why are the tests failing? |
I have also considered putting all this logic inside |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has been a longstanding limitation of React Native for iOS. Would be amazing if it were finally resolved.
Code looks good to me but admittedly I don't have a whole lot of context on RCTScrollView
.
Co-authored-by: Bartosz Kaszubowski <gosimek@gmail.com>
The Man! Cool feature! |
Hooohaay! You're a hero man! Everyone had this problem and no one solved, ultil now! |
While this feature works fine, I have noticed something strange when navigating: RPReplay_Final1619439728.MP4I have traced this back to find out where it actually updates the So in general, everytime this UIViewController blurs, the keyboard notification gets invoked with a keyboard frame (which is fully dismissed), and somehow it interactively slides down the ScrollView. That is, when another page gets pushed, when this page gets popped, and it happens interactively when you use the pop to dismiss gesture for the UIViewController. This is obviously only the case when a Keyboard is extended, but since an react-native/React/Views/ScrollView/RCTScrollView.m Lines 301 to 342 in 4bc8ce8
Does anyone have an idea why that's happening? Since it is sliding down interactively, there surely must be a way to disable that behaviour.. 🤔 EditI think that's iOS' CoreAnimation behaviour. Not sure if this is also reproduceable with non-native screens (react-navigation), but I'm pretty sure it's not caused by my PR. |
Any time estimate when this is going to get merged? |
This was re-merged in 49a1460 (please see the react-native-bot comment on December 6th, 2021) |
Nevermind, I see it has |
Tried it now in React native 0.68.0, looks good <3 |
My bad, I was still in school |
Thank you @mrousavy 🙏 Will this new |
Can someone tell me how to add this PR in my version 0.66.4 |
I am almost certainly naive to how these different elements act/interact, but I am curious as to why this type of functionality is not applied to KeyboardAvoidingView as well? I have a situation where I have a simple screen with a search input at the top, a scroll view in the middle (of search results), and action buttons on the bottom. For the life of me, I cannot figure out how to get the UX to play nice with keyboardDismissMode interactive. You would think that this fix in conjunction with wrapping the actions in an InputAccessoryView would be sufficient, but due to where the input field is in the layout, the InputAccessoryView does not remain "docked" to the bottom of the screen when the keyboard is dismissed / the input loses focus. Ideally, KeyboardAvoidingView worked properly when the keyboard is dismissed interactively (in which case there would be no need for an InputAccessoryView at all as the buttons could just live in a KeyboardAvoidingView. If someone has any insight into the correct way to keep an InputAccessoryView docked to the bottom for all inputs in a view (in my case a single TextInput), please save me from my misery. All that said, from someone who has been building social chat applications in react native for the past 4 years, huge kudos to @mrousavy for fixing an an incredibly frustrating issue that has plagued making a basic chat implementation feel native on iOS. |
@cenzovit you probably set |
I believe that only works if the TextInput is a child of the InputAccessoryView. In my case, the text input is at the top of the page and is a sibling to the action buttons I would like to dock to the bottom. So, if I exclude the nativeID, although the InputAccessoryView renders initially, when the input is focused, the InputAccessoryView does not mount to the keyboard. |
There seems to be some strange behavior occurring between automaticallyAdjustKeyboardInsets and react-native-screens where swiping back is triggering scrollview insets to change interactively with the swipe even though the keyboard (and/or InputAccessoryView) is not being dismissed. I am not sure if it is related, but I have a keyboard will change frame listener and it would appear that the native change frame events are firing even though the keyboard is not actually changing frames. I have a hack in my listener to ignore events with a duration of 350ms because that appears to be the event that is being triggered erroneously on the transition away from the screen. |
@cenzovit I've noticed that as well, maybe this is a RN Screens issue? |
When I did my custom implementation of |
This works perfectly fine in normal usage, but when using it in a nested list (ScrollView parent set to horizontal inside it a ScrollView with |
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
Hey guys, |
Hello, I have contributed to this project for 4 years and I have 58 pull requests. I can start working on this PR, fix the issue with nested scrollview and release it with the npm package react-native-improved in 7-8 days. Could you let me know if you are still interested in this issue? Thanks |
@ServiceTouchDevelopers @Yassir4 @savelichalex @cenzovit @jamninetyfive @Bardiamist @mattgabor @jonitef Still interested in having this implemented in react-native? We have 2000 issues, so I would need to know if you are still interested in this PR. |
Summary
Currently, ScrollViews provide the prop
keyboardDismissMode
which lets you choose"interactive"
. However when the keyboard is shown, it will be rendered above the ScrollView, potentially blocking content.With the
automaticallyAdjustKeyboardInsets
prop the ScrollView will automatically adjust it'scontentInset
,scrollIndicatorInsets
andcontentOffset
(scroll Y) props to push the content up so nothing gets blocked.<InputAccessoryView>
smaintainVisibleContentPosition
andautoscrollToTopThreshold
.maintainVisibleContentPosition
(autoscrollToTopThreshold
) prop(s), so they behave more reliably whencontentInset
s are applied. (This makes automatically scrolling to new items fully compatible withautomaticallyAdjustKeyboardInsets
)Changelog
automaticallyAdjustKeyboardInsets
prop: Automatically animatecontentInset
,scrollIndicatorInsets
andcontentOffset
(scroll Y) to avoid the Keyboard. (respecting absolute position on screen and safe-areas)contentInset
when animating new items withautoscrollToTopThreshold
, makeautomaticallyAdjustKeyboardInsets
work withautoscrollToTopThreshold
(includes vertical, vertical-inverted, horizontal and horizontal-inverted ScrollViews)Test Plan
before_tap.MP4
after_tap.MP4
"Why not just use
<KeyboardAvoidingView>
?"<KeyboardAvoidingView>
)automaticallyAdjustKeyboardInsets
)before_kav.MP4
after_interactive.MP4
Usage
Related Issues