diff --git a/js/ViewPager.js b/js/ViewPager.js index d9cf0953..93c7c9af 100644 --- a/js/ViewPager.js +++ b/js/ViewPager.js @@ -83,6 +83,8 @@ function getViewManagerConfig(viewManagerName) { */ class ViewPager extends React.Component { + isScrolling = false; + componentDidMount() { // On iOS we do it directly on the native side if (Platform.OS === 'android') { @@ -114,6 +116,7 @@ class ViewPager extends React.Component { if (this.props.onPageScrollStateChanged) { this.props.onPageScrollStateChanged(e); } + this.isScrolling = e.nativeEvent.pageScrollState === 'dragging'; }; _onPageSelected = (e: PageSelectedEvent) => { @@ -159,6 +162,13 @@ class ViewPager extends React.Component { ); }; + _onMoveShouldSetResponderCapture = () => { + if (Platform.OS === 'ios') { + return this.isScrolling; + } + return false; + }; + render() { return ( { onPageScroll={this._onPageScroll} onPageScrollStateChanged={this._onPageScrollStateChanged} onPageSelected={this._onPageSelected} + onMoveShouldSetResponderCapture={this._onMoveShouldSetResponderCapture} children={childrenWithOverriddenStyle(this.props.children)} /> ); diff --git a/js/types.js b/js/types.js index 4d519796..77f7fe9a 100644 --- a/js/types.js +++ b/js/types.js @@ -7,11 +7,18 @@ * @format * @flow strict-local */ - +import * as React from 'react'; +import {View} from 'react-native'; import type {Node} from 'react'; import type {SyntheticEvent} from 'react-native/Libraries/Types/CoreEventTypes'; import type {ViewStyleProp} from 'react-native/Libraries/StyleSheet/StyleSheet'; +type ViewProps = React.ElementProps; +type ResponderCaptureType = $PropertyType< + ViewProps, + 'onMoveShouldSetResponderCapture', +>; + export type PageScrollState = 'idle' | 'dragging' | 'settling'; export type PageScrollEvent = SyntheticEvent< @@ -95,6 +102,17 @@ export type ViewPagerProps = $ReadOnly<{| style?: ?ViewStyleProp, + /** + * If a parent `View` wants to prevent a child `View` from becoming responder + * on a move, it should have this handler which returns `true`. + * + * `View.props.onMoveShouldSetResponderCapture: (event) => [true | false]`, + * where `event` is a synthetic touch event as described above. + * + * See http://facebook.github.io/react-native/docs/view.html#onMoveShouldsetrespondercapture + */ + onMoveShouldSetResponderCapture?: ?ResponderCaptureType, + /** * iOS only */ diff --git a/typings/index.d.ts b/typings/index.d.ts index cd05b38d..ba9bfbce 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -10,6 +10,10 @@ export interface ViewPagerOnPageSelectedEventData { position: number; } +export interface PageScrollStateChangedEvent { + pageScrollState: 'idle' | 'dragging' | 'settling'; +} + export interface ViewPagerProps extends ReactNative.ViewProps { /** * Index of initial page that should be selected. Use `setPage` method to @@ -50,7 +54,7 @@ export interface ViewPagerProps extends ReactNative.ViewProps { * - settling, meaning that there was an interaction with the page scroller, and the * page scroller is now finishing it's closing or opening animation */ - onPageScrollStateChanged?: (state: "Idle" | "Dragging" | "Settling") => void; + onPageScrollStateChanged?: (event: ReactNative.NativeSyntheticEvent) => void; /** * Determines whether the keyboard gets dismissed in response to a drag. @@ -64,6 +68,8 @@ export interface ViewPagerProps extends ReactNative.ViewProps { * edge-to-edge. */ pageMargin?: number; + + onMoveShouldSetResponderCapture?: (event: any) => boolean; /** * iOS only