Fix onMomentumScrollBegin not dispatching from animations (#47468)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47468

Across our scroll view implementations on iOS, we fire `onMomentumScrollEnd` whenever the scroll view finishes decelerating, whether it comes from a user's touch or call to `setContentOffset` with animations. But we omit dispatching the `onMomentumScrollBegin` event in the latter cases.

This change updates both old and new architecture to dispatch `onMomentumScrollBegin` when a view-command-driven scroll occurs with animation, like `scrollTo` or `scrollToEnd`.

Changelog:
[iOS][Fixed] - Fixed `onMomentumScrollBegin` event not firing on command-driven scroll events

Reviewed By: javache

Differential Revision: D65556000

fbshipit-source-id: bc4b778c63d8a032e1d8e00b9d4d5f83a5d651d6
This commit is contained in:
Peter Abbondanzo 2024-11-11 10:12:33 -08:00 committed by Facebook GitHub Bot
parent 6295b81e79
commit 5b609cca09
3 changed files with 19 additions and 1 deletions

View File

@ -903,6 +903,8 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
// When not animated, the expected workflow in ``scrollViewDidEndScrollingAnimation`` after scrolling is not going
// to get triggered. We will need to manually execute here.
[self _handleFinishedScrolling:_scrollView];
} else if (_eventEmitter) {
static_cast<const ScrollViewEventEmitter &>(*_eventEmitter).onMomentumScrollBegin([self _scrollViewMetrics]);
}
}

View File

@ -600,6 +600,9 @@ static inline void RCTApplyTransformationAccordingLayoutDirection(
offset = CGPointMake(x, y);
}
[_scrollView setContentOffset:offset animated:animated];
if (animated) {
[self sendScrollEventWithName:@"onMomentumScrollBegin" scrollView:_scrollView userData:nil];
}
}
}
@ -622,6 +625,9 @@ static inline void RCTApplyTransformationAccordingLayoutDirection(
// Ensure at least one scroll event will fire
_allowNextScrollNoMatterWhat = YES;
[_scrollView setContentOffset:offset animated:animated];
if (animated) {
[self sendScrollEventWithName:@"onMomentumScrollBegin" scrollView:_scrollView userData:nil];
}
}
}

View File

@ -15,7 +15,7 @@ import RNTesterText from '../../components/RNTesterText';
import ScrollViewPressableStickyHeaderExample from './ScrollViewPressableStickyHeaderExample';
import nullthrows from 'nullthrows';
import * as React from 'react';
import {useCallback, useState} from 'react';
import {useCallback, useRef, useState} from 'react';
import {
Platform,
RefreshControl,
@ -855,11 +855,21 @@ const OnScrollOptions = () => {
};
const OnMomentumScroll = () => {
const ref = useRef<?React.ElementRef<typeof ScrollView>>(null);
const [scroll, setScroll] = useState('none');
return (
<View>
<RNTesterText>Scroll State: {scroll}</RNTesterText>
<Button
label="scrollTo top (animated)"
onPress={() => ref.current?.scrollTo({x: 0, y: 0, animated: true})}
/>
<Button
label="scrollTo top (not animated)"
onPress={() => ref.current?.scrollTo({x: 0, y: 0, animated: false})}
/>
<ScrollView
ref={ref}
style={[styles.scrollView, {height: 200}]}
onMomentumScrollBegin={() => setScroll('onMomentumScrollBegin')}
onMomentumScrollEnd={() => setScroll('onMomentumScrollEnd')}