diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm index d029337fb0d4..9b4e6aad2ec7 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm @@ -89,6 +89,7 @@ @interface RCTScrollViewComponentView () < @implementation RCTScrollViewComponentView { ScrollViewShadowNode::ConcreteState::Shared _state; + LayoutMetrics _lastContentContainerLayoutMetrics; CGSize _contentSize; NSTimeInterval _lastScrollEventDispatchTime; NSTimeInterval _scrollEventThrottle; @@ -132,7 +133,7 @@ - (instancetype)initWithFrame:(CGRect)frame _automaticallyAdjustKeyboardInsets = NO; [self addSubview:_scrollView]; - _containerView = [[UIView alloc] initWithFrame:CGRectZero]; + _containerView = [[RCTViewComponentView alloc] initWithFrame:CGRectZero]; [_scrollView addSubview:_containerView]; [self.scrollViewDelegateSplitter addDelegate:self]; @@ -468,7 +469,11 @@ - (void)updateState:(const State::Shared &)state oldState:(const State::Shared & } _contentSize = contentSize; - _containerView.frame = CGRect{RCTCGPointFromPoint(data.contentBoundingRect.origin), contentSize}; + LayoutMetrics newContentContainerLayoutMetrics = LayoutMetrics{ + .frame = {.origin = data.contentBoundingRect.origin, .size = data.getContentSize()}, .overflowInset = {.top = 1}}; + [_containerView updateLayoutMetrics:newContentContainerLayoutMetrics + oldLayoutMetrics:_lastContentContainerLayoutMetrics]; + _lastContentContainerLayoutMetrics = newContentContainerLayoutMetrics; [self _preserveContentOffsetIfNeededWithBlock:^{ self->_scrollView.contentSize = contentSize; diff --git a/packages/rn-tester/js/examples/ScrollView/ScrollViewExample.js b/packages/rn-tester/js/examples/ScrollView/ScrollViewExample.js index 6ec79751e73d..c0d44afaa774 100644 --- a/packages/rn-tester/js/examples/ScrollView/ScrollViewExample.js +++ b/packages/rn-tester/js/examples/ScrollView/ScrollViewExample.js @@ -457,6 +457,16 @@ const examples: Array = [ return ; }, }, + { + name: 'touchableChildrenOverflowingContainerHorizontal', + title: + ' touchable children overflow content container (horizontal = true)\n', + description: + "Children that overflow ScrollView's content container should still receive touch events", + render() { + return ; + }, + }, ]; if (Platform.OS === 'ios') { @@ -1352,6 +1362,56 @@ function ClippingExampleHorizontal() { ); } +function TouchableItem({index}: {index: number}) { + const [pressed, setPressed] = useState(false); + + return ( + setPressed(p => !p)} + testID={`touchable_item_${index}`} + style={{ + position: 'relative', + flexDirection: 'row', + justifyContent: 'center', + alignItems: 'center', + flexGrow: 1, + flexShrink: 1, + flexBasis: '25%', + margin: 5, + backgroundColor: pressed ? 'gray' : 'lightgray', + }}> + Item {index} + + ); +} + +function ChildrenWithTouchEventsOverflowingContainerHorizontal() { + return ( + + + + + + + + ); +} + class Item extends React.PureComponent<{ msg?: string, style?: ViewStyleProp,