Fix KeyboardAvoidingView behavior for multi-line TextInputs on iOS - #58436
Open
jasozh wants to merge 4 commits into
Open
Fix KeyboardAvoidingView behavior for multi-line TextInputs on iOS#58436jasozh wants to merge 4 commits into
jasozh wants to merge 4 commits into
Conversation
Summary:
In the RNTester KeyboardAvoidingView example, fix minor spacing issues and add descriptions for "Keyboard Avoiding View with enabled={false}" and "Keyboard Avoiding View with contentContainerStyle".
Changelog:
[Internal]
Differential Revision: D119204437
Summary: The RNTester KeyboardAvoidingView example had alignment issues with the close button on top of the example form. When KeyboardAvoidingView's behavior was toggled to "position", a small amount of right padding is added to the close button, making it appear non-flush with the right edge of the form. This impacted both the "Keyboard Avoiding View with different behaviors" screen and the "Keyboard Avoiding View with contentContainerStyle" screen. Changelog: [Internal] Differential Revision: D119204438
Summary: Add a new RNTester example in KeyboardAvoidingView for using the component with a ScrollView. This reproduces the issue raised in [react#16826](react#16826) where keyboard avoiding behavior fails for multi-line text fields in a ScrollView on iOS. As this is a common use case, the example acts as a reference to show intended behavior and prevent future regressions. Changelog: [Internal] Differential Revision: D119204439
Summary: Fixes the issue described in [react#16826](react#16826), where KeyboardAvoidingView does not reveal multi-line TextInputs when used with an enclosing ScrollView on iOS. ### Root Cause When a text input is focused on iOS, UIKit reveals the input by applying a scroll operation on the innermost ScrollView. When a single-line UITextField is inside a ScrollView, this scrolls the ScrollView itself, revealing the input correctly. However, the multi-line UITextView itself extends UIScrollView. When a UITextView is inside a ScrollView, UIKit applies the scroll operation only on the input and not the surrounding ScrollView. If the input is in a position where it is covered by the keyboard, it will remain covered afterwards. Community members have found a workaround by setting `scrollEnabled={false}` on multi-line inputs. This disables scrolling on the UITextView, so the scroll operation is correctly applied on the surrounding ScrollView. However, this workaround comes with obvious downsides, and solutions offered by third-party libraries are often used instead. ### This PR Modify RCTUITextView to override the `scrollRectToVisible(_:animated:)` method which is what UIKit uses to implicitly reveal the text view. The scrolling operation is performed on the text view like before, but we then forward the same operation to the nearest scrollable ancestor (such as a surrounding ScrollView). This ensures that the text view itself is always scrolled into view as scrolling is always applied to the parent. ### Limitations The main limitation is that we rely on UIKit to reveal the text input, since KeyboardAvoidingView itself implements no scrolling logic. This also leads to some discrepancy in behavior with Android. On Android, the entire text box is revealed on focus, whereas on iOS with this fix, only the caret line is revealed. We can introduce additional logic in RCTUITextView to reveal the entire text view, but this won't apply to text views with `scrollEnabled={false}` which will still only reveal the caret line using UIKit's default behavior. Native approaches on iOS generally involve manually handling the keyboard avoiding behavior by adding keyboard event listeners and manually triggering `scrollView.scrollRectToVisible()` when receiving the `keyboardDidShow()` notification. We can do something similar in `RCTScrollViewComponentView`, but this explicitly violates the contract there which prohibits behavior that contradicts a standard UIScrollView. In contrast, this approach aligns with what is already being used to reveal both UITextField and UITextView with `scrollEnabled={false}`. Changelog: [iOS][Fixed] Reveal multi-line text inputs when using KeyboardAvoidingView with a ScrollView Differential Revision: D119204440
|
@jasozh has exported this pull request. If you are a Meta employee, you can view the originating Diff in D119204440. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary:
Fixes the issue described in #16826, where KeyboardAvoidingView does not reveal multi-line TextInputs when used with an enclosing ScrollView on iOS.
Root Cause
When a text input is focused on iOS, UIKit reveals the input by applying a scroll operation on the innermost ScrollView. When a single-line UITextField is inside a ScrollView, this scrolls the ScrollView itself, revealing the input correctly.
However, the multi-line UITextView itself extends UIScrollView. When a UITextView is inside a ScrollView, UIKit applies the scroll operation only on the input and not the surrounding ScrollView. If the input is in a position where it is covered by the keyboard, it will remain covered afterwards.
Community members have found a workaround by setting
scrollEnabled={false}on multi-line inputs. This disables scrolling on the UITextView, so the scroll operation is correctly applied on the surrounding ScrollView. However, this workaround comes with obvious downsides, and solutions offered by third-party libraries are often used instead.This PR
Modify RCTUITextView to override the
scrollRectToVisible(_:animated:)method which is what UIKit uses to implicitly reveal the text view. The scrolling operation is performed on the text view like before, but we then forward the same operation to the nearest scrollable ancestor (such as a surrounding ScrollView). This ensures that the text view itself is always scrolled into view as scrolling is always applied to the parent.Limitations
The main limitation is that we rely on UIKit to reveal the text input, since KeyboardAvoidingView itself implements no scrolling logic. This also leads to some discrepancy in behavior with Android. On Android, the entire text box is revealed on focus, whereas on iOS with this fix, only the caret line is revealed.
We can introduce additional logic in RCTUITextView to reveal the entire text view, but this won't apply to text views with
scrollEnabled={false}which will still only reveal the caret line using UIKit's default behavior.Native approaches on iOS generally involve manually handling the keyboard avoiding behavior by adding keyboard event listeners and manually triggering
scrollView.scrollRectToVisible()when receiving thekeyboardDidShow()notification. We can do something similar inRCTScrollViewComponentView, but this explicitly violates the contract there which prohibits behavior that contradicts a standard UIScrollView.In contrast, this approach aligns with what is already being used to reveal both UITextField and UITextView with
scrollEnabled={false}.Changelog: [iOS][Fixed] Reveal multi-line text inputs when using KeyboardAvoidingView with a ScrollView
Differential Revision: D119204440