first commit

This commit is contained in:
2026-03-10 16:18:05 +00:00
commit 11f9c069b5
31635 changed files with 3187747 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
#pragma once
@protocol RNSContentScrollViewProviding
/**
* Finds content ScrollView within provider's hierarchy. The content ScrollView serves as a main interaction on the
* given screen. Implementations may use `RNSScrollViewFinder` to continue the search however they see fit.
*/
- (nullable UIScrollView *)findContentScrollView;
@end

View File

@@ -0,0 +1,14 @@
#pragma once
#import "RNSEnums.h"
@protocol RNSScrollEdgeEffectProviding
- (RNSScrollEdgeEffect)bottomScrollEdgeEffect;
- (RNSScrollEdgeEffect)leftScrollEdgeEffect;
- (RNSScrollEdgeEffect)rightScrollEdgeEffect;
- (RNSScrollEdgeEffect)topScrollEdgeEffect;
@end
@interface RNSScrollEdgeEffectApplicator : NSObject
+ (void)applyToScrollView:(UIScrollView *)scrollView withProvider:(id<RNSScrollEdgeEffectProviding>)provider;
@end

View File

@@ -0,0 +1,52 @@
#import "RNSScrollEdgeEffectApplicator.h"
#import <React/RCTLog.h>
#import "RNSDefines.h"
#import "RNSEnums.h"
@implementation RNSScrollEdgeEffectApplicator
+ (void)applyToScrollView:(UIScrollView *)scrollView withProvider:(id<RNSScrollEdgeEffectProviding>)provider
{
#if RNS_IPHONE_OS_VERSION_AVAILABLE(26_0)
if (@available(iOS 26, *)) {
[RNSScrollEdgeEffectApplicator configureEffect:scrollView.bottomEdgeEffect
withEnum:[provider bottomScrollEdgeEffect]];
[RNSScrollEdgeEffectApplicator configureEffect:scrollView.leftEdgeEffect withEnum:[provider leftScrollEdgeEffect]];
[RNSScrollEdgeEffectApplicator configureEffect:scrollView.rightEdgeEffect
withEnum:[provider rightScrollEdgeEffect]];
[RNSScrollEdgeEffectApplicator configureEffect:scrollView.topEdgeEffect withEnum:[provider topScrollEdgeEffect]];
}
#endif
}
#if RNS_IPHONE_OS_VERSION_AVAILABLE(26_0)
+ (void)configureEffect:(UIScrollEdgeEffect *)edgeEffect
withEnum:(RNSScrollEdgeEffect)effectEnum API_AVAILABLE(ios(26.0))
{
if (@available(iOS 26, *)) {
switch (effectEnum) {
case RNSScrollEdgeEffectAutomatic:
edgeEffect.hidden = false;
edgeEffect.style = UIScrollEdgeEffectStyle.automaticStyle;
break;
case RNSScrollEdgeEffectHard:
edgeEffect.hidden = false;
edgeEffect.style = UIScrollEdgeEffectStyle.hardStyle;
break;
case RNSScrollEdgeEffectSoft:
edgeEffect.hidden = false;
edgeEffect.style = UIScrollEdgeEffectStyle.softStyle;
break;
case RNSScrollEdgeEffectHidden:
edgeEffect.hidden = true;
edgeEffect.style = UIScrollEdgeEffectStyle.automaticStyle;
break;
default:
RCTLogError(@"[RNScreens] unsupported edge effect");
break;
}
}
}
#endif
@end

View File

@@ -0,0 +1,28 @@
#pragma once
#import <UIKit/UIKit.h>
#import "RNSContentScrollViewProviding.h"
@interface RNSScrollViewFinder : NSObject
/**
* Searches for content ScrollView by traversing down the hierarchy using first subview, similar to UIKit behavior.
* It will fail if:
* - UIScrollView is not a first subview of view or one of its descendants in the hierarchy,
* - if UIScrollView's parent is not yet attached.
*
* When `view == nil`, it should also return `nil`.
*/
+ (nullable UIScrollView *)findScrollViewInFirstDescendantChainFrom:(nullable UIView *)view;
/**
* Looks for UIScrollView in a similar way to `findScrollViewInFirstDescendantChainFrom`, until it finds
* `RNSContentScrollViewProviding`. Then, it delegates the task to the provider, and returns the results. This can
* overcome the problems of subviews' children not being mounted yet, or ScrollView being mounted at index different
* than 0.
*
* When `view == nil`, it should also return `nil`.
*/
+ (nullable UIScrollView *)findContentScrollViewWithDelegatingToProvider:(nullable UIView *)view;
@end

View File

@@ -0,0 +1,44 @@
#import "RNSScrollViewFinder.h"
@implementation RNSScrollViewFinder
+ (UIScrollView *)findScrollViewInFirstDescendantChainFrom:(UIView *)view
{
UIView *currentView = view;
while (currentView != nil) {
if ([currentView isKindOfClass:UIScrollView.class]) {
return static_cast<UIScrollView *>(currentView);
} else if ([currentView.subviews count] > 0) {
currentView = currentView.subviews[0];
} else {
break;
}
}
return nil;
}
+ (nullable UIScrollView *)findContentScrollViewWithDelegatingToProvider:(nullable UIView *)view
{
UIView *currentView = view;
while (currentView != nil) {
if ([currentView isKindOfClass:UIScrollView.class]) {
return static_cast<UIScrollView *>(currentView);
} else if ([currentView respondsToSelector:@selector(findContentScrollView)]) {
// When traversing the hierarchy, we don't check for conformance to protocol,
// but whether the view responds to `RNSContentScrollViewProviding.findContentScrollView`.
// This doesn't place locks and is faster.
return [static_cast<id<RNSContentScrollViewProviding>>(currentView) findContentScrollView];
} else if ([currentView.subviews count] > 0) {
currentView = currentView.subviews[0];
} else {
break;
}
}
return nil;
}
@end

View File

@@ -0,0 +1,12 @@
#pragma once
#import <UIKit/UIKit.h>
@interface RNSScrollViewHelper : NSObject
/**
* Finds and overrides contentInsetAdjustmentBehavior for first ScrollView in first descendant chain from view.
*/
+ (void)overrideScrollViewBehaviorInFirstDescendantChainFrom:(nullable UIView *)view;
@end

View File

@@ -0,0 +1,15 @@
#import "RNSScrollViewHelper.h"
#import "RNSScrollViewFinder.h"
@implementation RNSScrollViewHelper
+ (void)overrideScrollViewBehaviorInFirstDescendantChainFrom:(nullable UIView *)view
{
UIScrollView *scrollView = [RNSScrollViewFinder findScrollViewInFirstDescendantChainFrom:view];
if ([scrollView contentInsetAdjustmentBehavior] == UIScrollViewContentInsetAdjustmentNever) {
[scrollView setContentInsetAdjustmentBehavior:UIScrollViewContentInsetAdjustmentAutomatic];
}
}
@end