diff --git a/example/src/App.tsx b/example/src/App.tsx index eb33e443..a7bb584e 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -36,6 +36,7 @@ import { MaterialTopBarExample } from './MaterialTopTabExample'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; import { SafeAreaProvider } from 'react-native-safe-area-context'; import { PagerHookExample } from './PagerHookExample'; +import { NestedHorizontalScrollViewExample } from './NestedHorizontalScrollViewExample'; const examples = [ { component: BasicPagerViewExample, name: 'Basic Example' }, @@ -50,7 +51,10 @@ const examples = [ component: ScrollablePagerViewExample, name: 'Scrollable PagerView Example', }, - { component: TabViewInsideScrollViewExample, name: 'TabView inside ScrollView Example' }, + { + component: TabViewInsideScrollViewExample, + name: 'TabView inside ScrollView Example', + }, { component: ScrollViewInsideExample, name: 'ScrollView inside PagerView Example', @@ -60,6 +64,10 @@ const examples = [ name: 'Nest PagerView Example', }, { component: ScrollableTabBarExample, name: 'ScrollableTabBarExample' }, + { + component: NestedHorizontalScrollViewExample, + name: 'NestedHorizontalScrollViewExample', + }, { component: AutoWidthTabBarExample, name: 'AutoWidthTabBarExample' }, { component: TabBarIconExample, name: 'TabBarIconExample' }, { component: CustomIndicatorExample, name: 'CustomIndicatorExample' }, diff --git a/example/src/NestedHorizontalScrollViewExample.tsx b/example/src/NestedHorizontalScrollViewExample.tsx new file mode 100644 index 00000000..daf1c8fb --- /dev/null +++ b/example/src/NestedHorizontalScrollViewExample.tsx @@ -0,0 +1,105 @@ +import React, { useMemo } from 'react'; +import { + StyleSheet, + View, + SafeAreaView, + Animated, + Text, + ScrollView, +} from 'react-native'; + +import PagerView from 'react-native-pager-view'; + +import { LikeCount } from './component/LikeCount'; +import { NavigationPanel } from './component/NavigationPanel'; +import { useNavigationPanel } from './hook/useNavigationPanel'; + +const AnimatedPagerView = Animated.createAnimatedComponent(PagerView); + +/** + * When dragging the horizontal ScrollView inside the PagerView, it can slightly push pager view into a different page. + * This is because scrollview in the same direction overdrags the outer scrollview (pager view). + * This example reproduces this behavior. + * Go to the second page and try to drag the horizontal scrollview. + */ +export function NestedHorizontalScrollViewExample() { + const { ref, ...navigationPanel } = useNavigationPanel(3); + + return ( + + + {useMemo( + () => + navigationPanel.pages.map((page, index) => { + if (index === 1) { + return ( + + + {`Scroll View page number ${index}`} + + + {`Scroll View page number ${index}`} + + + ); + } + + return ( + + + {`page number ${index}`} + + ); + }), + [navigationPanel.pages] + )} + + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: 'white', + }, + image: { + width: 300, + height: 200, + padding: 20, + }, + PagerView: { + flex: 1, + }, + box: { + width: 300, + height: '100%', + backgroundColor: 'lightgreen', + padding: 20, + }, +}); diff --git a/ios/RNCPagerViewComponentView.mm b/ios/RNCPagerViewComponentView.mm index c6a150b0..ccc2595b 100644 --- a/ios/RNCPagerViewComponentView.mm +++ b/ios/RNCPagerViewComponentView.mm @@ -296,53 +296,51 @@ - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { - (void)scrollViewDidScroll:(UIScrollView *)scrollView { - CGFloat contentOffset = [self isHorizontal] ? scrollView.contentOffset.x : scrollView.contentOffset.y; - CGFloat frameSize = [self isHorizontal] ? scrollView.frame.size.width : scrollView.frame.size.height; - + BOOL isHorizontal = [self isHorizontal]; + CGFloat contentOffset = isHorizontal ? scrollView.contentOffset.x : scrollView.contentOffset.y; + CGFloat frameSize = isHorizontal ? scrollView.frame.size.width : scrollView.frame.size.height; + if (frameSize == 0) { return; } - float offset = (contentOffset - frameSize)/frameSize; - + float offset = (contentOffset - frameSize) / frameSize; float absoluteOffset = fabs(offset); - NSInteger position = _currentIndex; BOOL isHorizontalRtl = [self isHorizontalRtlLayout]; BOOL isAnimatingBackwards = isHorizontalRtl ? offset > 0.05f : offset < 0; - - if (scrollView.isDragging) { + BOOL isBeingMovedByNestedScrollView = !scrollView.isDragging && !scrollView.isTracking; + if (scrollView.isDragging || isBeingMovedByNestedScrollView) { _destinationIndex = isAnimatingBackwards ? _currentIndex - 1 : _currentIndex + 1; } if (isAnimatingBackwards) { - position = _destinationIndex; - absoluteOffset = fmax(0, 1 - absoluteOffset); + position = _destinationIndex; + absoluteOffset = fmax(0, 1 - absoluteOffset); } if (!_overdrag) { NSInteger maxIndex = _nativeChildrenViewControllers.count - 1; - NSInteger firstPageIndex = !isHorizontalRtl ? 0 : maxIndex; - NSInteger lastPageIndex = !isHorizontalRtl ? maxIndex : 0; + NSInteger firstPageIndex = isHorizontalRtl ? maxIndex : 0; + NSInteger lastPageIndex = isHorizontalRtl ? 0 : maxIndex; BOOL isFirstPage = _currentIndex == firstPageIndex; BOOL isLastPage = _currentIndex == lastPageIndex; - CGFloat contentOffset =[self isHorizontal] ? scrollView.contentOffset.x : scrollView.contentOffset.y; - CGFloat topBound = [self isHorizontal] ? scrollView.bounds.size.width : scrollView.bounds.size.height; + CGFloat topBound = isHorizontal ? scrollView.bounds.size.width : scrollView.bounds.size.height; if ((isFirstPage && contentOffset <= topBound) || (isLastPage && contentOffset >= topBound)) { - CGPoint croppedOffset = [self isHorizontal] ? CGPointMake(topBound, 0) : CGPointMake(0, topBound); + CGPoint croppedOffset = isHorizontal ? CGPointMake(topBound, 0) : CGPointMake(0, topBound); scrollView.contentOffset = croppedOffset; - absoluteOffset=0; + absoluteOffset = 0; position = isLastPage ? lastPageIndex : firstPageIndex; } } float interpolatedOffset = absoluteOffset * labs(_destinationIndex - _currentIndex); - [self sendScrollEventsForPosition:position offset:interpolatedOffset]; } + #pragma mark - UIPageViewControllerDelegate - (void)pageViewController:(UIPageViewController *)pageViewController @@ -356,7 +354,6 @@ - (void)pageViewController:(UIPageViewController *)pageViewController int position = (int) currentIndex; const auto eventEmitter = [self pagerEventEmitter]; eventEmitter->onPageSelected(RNCViewPagerEventEmitter::OnPageSelected{.position = static_cast(position)}); - eventEmitter->onPageScroll(RNCViewPagerEventEmitter::OnPageScroll{.position = static_cast(position), .offset = 0.0}); } }