Remove faulty logic in ScrollView #1510
Merged
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.
Please select one of the following
Summary
RCTScrollView forwards some of its methods to its underlying
_scrollView
of type RCTCustomScrollView. Some of these always return YES, instead of properly implementing the logic. Let's remove the faulty forwarding, so that instead inherited superclass implementation (RCTView) is used.Background
Normally, you expect a 1:1 mapping between the JS and native component (e.g:
<View>
maps toRCTView
). There are notable exceptions, like<ScrollView>
.<ScrollView>
maps toRCTScrollView
which is a subclass ofRCTView
, notUIScrollView / NSScrollView
. So how doesRCTScrollView
do its scrolling logic if the underlying component is just a subclass ofRCTView
? It turns out,RCTScrollView
contains a member variable_scrollView
(the real NSScrollView / UIScrollView). It then forwards appropriate methods / variables to the underlying "real"_scrollView
.(Side note): That's a little simplified. The real inheritance / ownership relationship looks like:
The issue is that in the case of accepting/forwarding first responder status, this didn't work. RCTScrollView would forward the calls to
canBecomeFirstResponder / becomeFirstResponder / resignFirstResponder
to it's underlying_scrollView
, which were overridden to always YES. This is bad because:The
_scrollView
always accepted first responder status, even when its outer RCTScrollView didn't want it (e.g:focusable={false}
). This blocked FocusZone (macOS): move focus between first responders instead of key views fluentui-react-native#2329 , because the_scrollView
would receive focus even when JS told it to not to.Even though RCTScrollView forwards the call to
becomeFirstResponder
to its_scrollView
, that didn't actually cause_scrollView
to become first responder. It would just returnYES
, and Appkit would proceed with making the outer RCTScrollView first responder anyway.I see an issue that
_scrollView
never actually becomes first responder, but instead anNSView
wrapping it does. However, It think that's a much bigger fix and, the outer view managing first responder status has worked in production for a while. Let's just remove the faulty logic implying the inner_scrollView
does get first responder status for now.Changelog
[macOS] [Fixed] - Don't override responder methods in ScrollView
Test Plan
I tested the ScrollViewSimpleExample, ScrollView, and Flatlist test pages to make sure that the key view loop was the same and that you could still tab to a ScrollView and scroll it with PageUp/Down.
Video of Flatlist test page:
Screen.Recording.2022-11-14.at.3.39.59.PM.mov