Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.swmansion.rnscreens

import android.annotation.SuppressLint
import com.facebook.react.bridge.ReactContext
import com.facebook.react.views.view.ReactViewGroup

@SuppressLint("ViewConstructor")
class ContentScrollViewDetector(
val reactContext: ReactContext,
) : ReactViewGroup(reactContext) {
override fun onAttachedToWindow() {
super.onAttachedToWindow()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.swmansion.rnscreens

import com.facebook.react.module.annotations.ReactModule
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.ViewGroupManager
import com.facebook.react.uimanager.ViewManagerDelegate
import com.facebook.react.viewmanagers.RNSContentScrollViewDetectorManagerDelegate
import com.facebook.react.viewmanagers.RNSContentScrollViewDetectorManagerInterface

@ReactModule(name = ContentScrollViewDetectorViewManager.REACT_CLASS)
open class ContentScrollViewDetectorViewManager :
ViewGroupManager<ContentScrollViewDetector>(),
RNSContentScrollViewDetectorManagerInterface<ContentScrollViewDetector> {
private val delegate: ViewManagerDelegate<ContentScrollViewDetector>

init {
delegate =
RNSContentScrollViewDetectorManagerDelegate<ContentScrollViewDetector, ContentScrollViewDetectorViewManager>(
this,
)
}

override fun getName() = REACT_CLASS

override fun createViewInstance(reactContext: ThemedReactContext) = ContentScrollViewDetector(reactContext)

override fun getDelegate(): ViewManagerDelegate<ContentScrollViewDetector> = delegate

companion object Companion {
const val REACT_CLASS = "RNSContentScrollViewDetector"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class RNScreensPackage : BaseReactPackage() {
TabsHostViewManager(),
TabScreenViewManager(),
SafeAreaViewManager(),
ContentScrollViewDetectorViewManager(),
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
*
* Do not edit this file as changes may cause incorrect behavior and will be lost
* once the code is regenerated.
*
* @generated by codegen project: GeneratePropsJavaDelegate.js
*/

package com.facebook.react.viewmanagers;

import android.view.View;
import androidx.annotation.Nullable;
import com.facebook.react.uimanager.BaseViewManager;
import com.facebook.react.uimanager.BaseViewManagerDelegate;
import com.facebook.react.uimanager.LayoutShadowNode;

@SuppressWarnings("deprecation")
public class RNSContentScrollViewDetectorManagerDelegate<T extends View, U extends BaseViewManager<T, ? extends LayoutShadowNode> & RNSContentScrollViewDetectorManagerInterface<T>> extends BaseViewManagerDelegate<T, U> {
public RNSContentScrollViewDetectorManagerDelegate(U viewManager) {
super(viewManager);
}
@Override
public void setProperty(T view, String propName, @Nullable Object value) {
super.setProperty(view, propName, value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
*
* Do not edit this file as changes may cause incorrect behavior and will be lost
* once the code is regenerated.
*
* @generated by codegen project: GeneratePropsJavaInterface.js
*/

package com.facebook.react.viewmanagers;

import android.view.View;
import com.facebook.react.uimanager.ViewManagerWithGeneratedInterface;

public interface RNSContentScrollViewDetectorManagerInterface<T extends View> extends ViewManagerWithGeneratedInterface {
// No props
}
35 changes: 35 additions & 0 deletions ios/RNSContentScrollViewDetector.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#pragma once

#import "ContentScrollViewConsumer.h"
#import "RNSReactBaseView.h"
#import "RNSScrollViewFinder.h"

#if !RCT_NEW_ARCH_ENABLED
#import <React/RCTViewManager.h>
#endif // !RCT_NEW_ARCH_ENABLED

NS_ASSUME_NONNULL_BEGIN

@interface RNSContentScrollViewDetector :
#ifdef RCT_NEW_ARCH_ENABLED
RCTViewComponentView
#else
UIView
#endif

- (UIScrollView *)findContentScrollView;

- (void)registerContentScrollViewInHierarchy;

- (void)unregisterContentScrollViewInHierarchy;

@end

#if !RCT_NEW_ARCH_ENABLED

@interface RNSContentScrollViewDetectorViewManager : RCTViewManager
@end

#endif // !RCT_NEW_ARCH_ENABLED

NS_ASSUME_NONNULL_END
101 changes: 101 additions & 0 deletions ios/RNSContentScrollViewDetector.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#import "RNSContentScrollViewDetector.h"
#import <React/RCTLog.h>
#import <react/renderer/components/rnscreens/ComponentDescriptors.h>

namespace react = facebook::react;

@implementation RNSContentScrollViewDetector {
UIScrollView *_scrollView;
UIView *_linkedView;
}

- (void)didMoveToWindow
{
if (self.window == nil) {
[self unregisterContentScrollViewInHierarchy];
} else {
if ((_scrollView = [self findContentScrollView])) {
[self registerContentScrollViewInHierarchy];
}
}
}

- (UIScrollView *)findContentScrollView
{
auto scrollView = [RNSScrollViewFinder findScrollViewInFirstDescendantChainFrom:self];
if (scrollView) {
RCTLogInfo(@"Found content ScrollView");
return scrollView;
}

return nullptr;
}

- (void)registerContentScrollViewInHierarchy
{
if (_linkedView != nullptr) {
RCTLogWarn(
@"Content ScrollView has already been registered. Make sure to only have one ScrollViewWrapper for content ScrollView.");
[self unregisterContentScrollViewInHierarchy];
}

UIView *parent = self.superview;
while (parent) {
if ([parent respondsToSelector:@selector(findContentScrollView)]) {
RCTLogWarn(
@"Nested ScrollViewWrapper detected. Make sure to only have one ScrollViewWrapper for content ScrollView.");
}

if ([parent respondsToSelector:@selector(registerContentScrollView:)]) {
[(id<ContentScrollViewConsumer>)parent registerContentScrollView:_scrollView];
_linkedView = parent;
RCTLogInfo(@"registered content ScrollView for: %@", parent);
break;
}

parent = parent.superview;
}
}

- (void)unregisterContentScrollViewInHierarchy
{
if (_linkedView == nullptr) {
return;
}

[(id<ContentScrollViewConsumer>)_linkedView unregisterContentScrollView:_scrollView];

RCTLogInfo(@"unregistered content ScrollView for: %@", _linkedView);
_linkedView = nullptr;
}

#pragma mark - RCTViewComponentViewProtocol

+ (react::ComponentDescriptorProvider)componentDescriptorProvider
{
return react::concreteComponentDescriptorProvider<react::RNSContentScrollViewDetectorComponentDescriptor>();
}

@end

#if RCT_NEW_ARCH_ENABLED
Class<RCTComponentViewProtocol> RNSContentScrollViewDetectorCls(void)
{
return RNSContentScrollViewDetector.class;
}
#endif // RCT_NEW_ARCH_ENABLED

#if !RCT_NEW_ARCH_ENABLED

@implementation RNSContentScrollViewDetectorViewManager

RCT_EXPORT_MODULE(RNSContentScrollViewDetector);

- (UIView *)view
{
return [[RNSContentScrollViewDetector alloc] init];
}

@end

#endif // !RCT_NEW_ARCH_ENABLED
9 changes: 9 additions & 0 deletions ios/helpers/scroll-view/ContentScrollViewConsumer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#pragma once

@protocol ContentScrollViewConsumer

- (void)registerContentScrollView:(UIScrollView *)scrollView;

- (void)unregisterContentScrollView:(UIScrollView *)scrollView;

@end
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,8 @@
"RNSSearchBar": "RNSSearchBar",
"RNSSplitViewHost": "RNSSplitViewHostComponentView",
"RNSSplitViewScreen": "RNSSplitViewScreenComponentView",
"RNSSafeAreaView": "RNSSafeAreaViewComponentView"
"RNSSafeAreaView": "RNSSafeAreaViewComponentView",
"RNSContentScrollViewDetector": "RNSContentScrollViewDetector"
},
"components": {
"RNSFullWindowOverlay": {
Expand Down Expand Up @@ -245,6 +246,9 @@
},
"RNSSafeAreaView": {
"className": "RNSSafeAreaViewComponentView"
},
"RNSContentScrollViewDetector": {
"className": "RNSContentScrollViewDetector"
}
}
}
Expand Down
18 changes: 18 additions & 0 deletions src/components/ContentScrollViewDetector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
'use client';

import React from 'react';
import ContentScrollViewDetectorNativeComponent from '../fabric/ContentScrollViewDetectorNativeComponent';

function ContentScrollViewDetector({
children,
}: {
children?: React.ReactNode;
}) {
return (
<ContentScrollViewDetectorNativeComponent>
{children}
</ContentScrollViewDetectorNativeComponent>
);
}

export default ContentScrollViewDetector;
9 changes: 9 additions & 0 deletions src/fabric/ContentScrollViewDetectorNativeComponent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { codegenNativeComponent } from 'react-native';
import type { ViewProps } from 'react-native';

export interface NativeProps extends ViewProps {}

export default codegenNativeComponent<NativeProps>(
'RNSContentScrollViewDetector',
{},
);
Loading