fix(iOS): fire onMomentumScrollEnd when UIScrollView is removed from window (#46277)

Summary:
Solves this issue: https://github.com/facebook/react-native/issues/46276

## Changelog:

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
-->

[IOS] [ADDED] - fire onMomentumScrollEnd when UIScrollView is removed from window

**Why the issue is happening?**
The `onMomentumScrollEnd` event is typically triggered by the `UIScrollView` delegate methods `scrollViewDidEndDecelerating` and `scrollViewDidEndScrollingAnimation`. However, if the scroll view is removed from the window while navigating away, these delegate methods are not called, resulting in the event not being dispatched.

This behaviour was particularly problematic in scenarios where a scroll view is in motion, and the user navigates away from the screen before the scrolling completes. In such cases, the `onMomentumScrollEnd` event would never fire, which further make scroll area un touchable or un responsive.

**What we changed?**
In the didMoveToWindow method, we added logic to handle the scenario where the UIScrollView is being removed from the window (i.e., when the component is unmounted or the user navigates away). Here’s a breakdown of the changes:

- **Added a Check for Scroll State:** We check if the UIScrollView was decelerating or had stopped tracking (_scrollView.isDecelerating || _scrollView.isTracking == NO).

- **Manually Triggered onMomentumScrollEnd:** If the scroll view was in motion and is being removed from the window, we manually trigger the `onMomentumScrollEnd` event to ensure that the final scroll state is captured.

**_I had fixed this issue on both Old and New arch._**

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

Test Plan:
Attaching a video with working solution:

https://github.com/user-attachments/assets/1a1f3765-3f11-46c3-af18-330c88478db8

Reviewed By: andrewdacenko

Differential Revision: D62374798

Pulled By: cipolleschi

fbshipit-source-id: 014be8d313bab0257459dc4e53f5b0386a39d5e0
This commit is contained in:
shubhamguptadream11
2024-09-13 06:33:30 -07:00
committed by Facebook GitHub Bot
parent f8287e25e1
commit b98b9f1fa7
2 changed files with 36 additions and 0 deletions
@@ -721,6 +721,29 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
[self _handleFinishedScrolling:scrollView];
}
- (void)didMoveToWindow
{
[super didMoveToWindow];
if (!self.window) {
// The view is being removed, ensure that the scroll end event is dispatched
[self _handleScrollEndIfNeeded];
}
}
- (void)_handleScrollEndIfNeeded
{
if (_scrollView.isDecelerating || !_scrollView.isTracking) {
if (!_eventEmitter) {
return;
}
static_cast<const ScrollViewEventEmitter &>(*_eventEmitter).onMomentumScrollEnd([self _scrollViewMetrics]);
[self _updateStateWithContentOffset];
_isUserTriggeredScrolling = NO;
}
}
- (void)_handleFinishedScrolling:(UIScrollView *)scrollView
{
[self _forceDispatchNextScrollEvent];
@@ -848,6 +848,19 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidScrollToTop, onScrollToTop)
RCT_FORWARD_SCROLL_EVENT(scrollViewDidEndZooming : scrollView withView : view atScale : scale);
}
- (void)didMoveToWindow
{
[super didMoveToWindow];
if (self.window == nil) {
// Check if the ScrollView was in motion
if (_scrollView.isDecelerating || !_scrollView.isTracking) {
// Trigger the onMomentumScrollEnd event manually
RCT_SEND_SCROLL_EVENT(onMomentumScrollEnd, nil);
RCT_FORWARD_SCROLL_EVENT(scrollViewDidEndDecelerating : _scrollView);
}
}
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
// Fire a final scroll event