yeet
This commit is contained in:
77
node_modules/react-native-screens/ios/RNSScreen.h
generated
vendored
Normal file
77
node_modules/react-native-screens/ios/RNSScreen.h
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
#import <React/RCTViewManager.h>
|
||||
#import <React/RCTView.h>
|
||||
#import <React/RCTComponent.h>
|
||||
|
||||
#import "RNSScreenContainer.h"
|
||||
|
||||
@class RNSScreenContainerView;
|
||||
|
||||
typedef NS_ENUM(NSInteger, RNSScreenStackPresentation) {
|
||||
RNSScreenStackPresentationPush,
|
||||
RNSScreenStackPresentationModal,
|
||||
RNSScreenStackPresentationTransparentModal,
|
||||
RNSScreenStackPresentationContainedModal,
|
||||
RNSScreenStackPresentationContainedTransparentModal,
|
||||
RNSScreenStackPresentationFullScreenModal,
|
||||
RNSScreenStackPresentationFormSheet
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, RNSScreenStackAnimation) {
|
||||
RNSScreenStackAnimationDefault,
|
||||
RNSScreenStackAnimationNone,
|
||||
RNSScreenStackAnimationFade,
|
||||
RNSScreenStackAnimationFlip,
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, RNSScreenReplaceAnimation) {
|
||||
RNSScreenReplaceAnimationPop,
|
||||
RNSScreenReplaceAnimationPush,
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, RNSActivityState) {
|
||||
RNSActivityStateInactive = 0,
|
||||
RNSActivityStateTransitioningOrBelowTop = 1,
|
||||
RNSActivityStateOnTop = 2
|
||||
};
|
||||
|
||||
@interface RCTConvert (RNSScreen)
|
||||
|
||||
+ (RNSScreenStackPresentation)RNSScreenStackPresentation:(id)json;
|
||||
+ (RNSScreenStackAnimation)RNSScreenStackAnimation:(id)json;
|
||||
|
||||
@end
|
||||
|
||||
@interface RNSScreen : UIViewController <RNScreensViewControllerDelegate>
|
||||
|
||||
- (instancetype)initWithView:(UIView *)view;
|
||||
- (void)notifyFinishTransitioning;
|
||||
|
||||
@end
|
||||
|
||||
@interface RNSScreenManager : RCTViewManager
|
||||
|
||||
@end
|
||||
|
||||
@interface RNSScreenView : RCTView
|
||||
|
||||
@property (nonatomic, copy) RCTDirectEventBlock onAppear;
|
||||
@property (nonatomic, copy) RCTDirectEventBlock onDisappear;
|
||||
@property (nonatomic, copy) RCTDirectEventBlock onDismissed;
|
||||
@property (nonatomic, copy) RCTDirectEventBlock onWillAppear;
|
||||
@property (nonatomic, copy) RCTDirectEventBlock onWillDisappear;
|
||||
@property (weak, nonatomic) UIView<RNSScreenContainerDelegate> *reactSuperview;
|
||||
@property (nonatomic, retain) UIViewController *controller;
|
||||
@property (nonatomic, readonly) BOOL dismissed;
|
||||
@property (nonatomic) int activityState;
|
||||
@property (nonatomic) BOOL gestureEnabled;
|
||||
@property (nonatomic) RNSScreenStackAnimation stackAnimation;
|
||||
@property (nonatomic) RNSScreenStackPresentation stackPresentation;
|
||||
@property (nonatomic) RNSScreenReplaceAnimation replaceAnimation;
|
||||
|
||||
- (void)notifyFinishTransitioning;
|
||||
|
||||
@end
|
||||
|
||||
@interface UIView (RNSScreen)
|
||||
- (UIViewController *)parentViewController;
|
||||
@end
|
532
node_modules/react-native-screens/ios/RNSScreen.m
generated
vendored
Normal file
532
node_modules/react-native-screens/ios/RNSScreen.m
generated
vendored
Normal file
@ -0,0 +1,532 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RNSScreen.h"
|
||||
#import "RNSScreenStackHeaderConfig.h"
|
||||
#import "RNSScreenContainer.h"
|
||||
|
||||
#import <React/RCTUIManager.h>
|
||||
#import <React/RCTShadowView.h>
|
||||
#import <React/RCTTouchHandler.h>
|
||||
|
||||
@interface RNSScreenView () <UIAdaptivePresentationControllerDelegate, RCTInvalidating>
|
||||
@end
|
||||
|
||||
@implementation RNSScreenView {
|
||||
__weak RCTBridge *_bridge;
|
||||
RNSScreen *_controller;
|
||||
RCTTouchHandler *_touchHandler;
|
||||
CGRect _reactFrame;
|
||||
}
|
||||
|
||||
@synthesize controller = _controller;
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_bridge = bridge;
|
||||
_controller = [[RNSScreen alloc] initWithView:self];
|
||||
_stackPresentation = RNSScreenStackPresentationPush;
|
||||
_stackAnimation = RNSScreenStackAnimationDefault;
|
||||
_gestureEnabled = YES;
|
||||
_replaceAnimation = RNSScreenReplaceAnimationPop;
|
||||
_dismissed = NO;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)reactSetFrame:(CGRect)frame
|
||||
{
|
||||
_reactFrame = frame;
|
||||
UIViewController *parentVC = self.reactViewController.parentViewController;
|
||||
if (parentVC != nil && ![parentVC isKindOfClass:[UINavigationController class]]) {
|
||||
[super reactSetFrame:frame];
|
||||
}
|
||||
// when screen is mounted under UINavigationController it's size is controller
|
||||
// by the navigation controller itself. That is, it is set to fill space of
|
||||
// the controller. In that case we ignore react layout system from managing
|
||||
// the screen dimensions and we wait for the screen VC to update and then we
|
||||
// pass the dimensions to ui view manager to take into account when laying out
|
||||
// subviews
|
||||
}
|
||||
|
||||
- (UIViewController *)reactViewController
|
||||
{
|
||||
return _controller;
|
||||
}
|
||||
|
||||
- (void)updateBounds
|
||||
{
|
||||
[_bridge.uiManager setSize:self.bounds.size forView:self];
|
||||
}
|
||||
|
||||
// Nil will be provided when activityState is set as an animated value and we change
|
||||
// it from JS to be a plain value (non animated).
|
||||
// In case when nil is received, we want to ignore such value and not make
|
||||
// any updates as the actual non-nil value will follow immediately.
|
||||
- (void)setActivityStateOrNil:(NSNumber *)activityStateOrNil
|
||||
{
|
||||
int activityState = [activityStateOrNil intValue];
|
||||
if (activityStateOrNil != nil && activityState != _activityState) {
|
||||
_activityState = activityState;
|
||||
[_reactSuperview markChildUpdated];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setPointerEvents:(RCTPointerEvents)pointerEvents
|
||||
{
|
||||
// pointer events settings are managed by the parent screen container, we ignore
|
||||
// any attempt of setting that via React props
|
||||
}
|
||||
|
||||
- (void)setStackPresentation:(RNSScreenStackPresentation)stackPresentation
|
||||
{
|
||||
switch (stackPresentation) {
|
||||
case RNSScreenStackPresentationModal:
|
||||
#ifdef __IPHONE_13_0
|
||||
if (@available(iOS 13.0, *)) {
|
||||
_controller.modalPresentationStyle = UIModalPresentationAutomatic;
|
||||
} else {
|
||||
_controller.modalPresentationStyle = UIModalPresentationFullScreen;
|
||||
}
|
||||
#else
|
||||
_controller.modalPresentationStyle = UIModalPresentationFullScreen;
|
||||
#endif
|
||||
break;
|
||||
case RNSScreenStackPresentationFullScreenModal:
|
||||
_controller.modalPresentationStyle = UIModalPresentationFullScreen;
|
||||
break;
|
||||
#if (TARGET_OS_IOS)
|
||||
case RNSScreenStackPresentationFormSheet:
|
||||
_controller.modalPresentationStyle = UIModalPresentationFormSheet;
|
||||
break;
|
||||
#endif
|
||||
case RNSScreenStackPresentationTransparentModal:
|
||||
_controller.modalPresentationStyle = UIModalPresentationOverFullScreen;
|
||||
break;
|
||||
case RNSScreenStackPresentationContainedModal:
|
||||
_controller.modalPresentationStyle = UIModalPresentationCurrentContext;
|
||||
break;
|
||||
case RNSScreenStackPresentationContainedTransparentModal:
|
||||
_controller.modalPresentationStyle = UIModalPresentationOverCurrentContext;
|
||||
break;
|
||||
case RNSScreenStackPresentationPush:
|
||||
// ignored, we only need to keep in mind not to set presentation delegate
|
||||
break;
|
||||
}
|
||||
// There is a bug in UIKit which causes retain loop when presentationController is accessed for a
|
||||
// controller that is not going to be presented modally. We therefore need to avoid setting the
|
||||
// delegate for screens presented using push. This also means that when controller is updated from
|
||||
// modal to push type, this may cause memory leak, we warn about that as well.
|
||||
if (stackPresentation != RNSScreenStackPresentationPush) {
|
||||
// `modalPresentationStyle` must be set before accessing `presentationController`
|
||||
// otherwise a default controller will be created and cannot be changed after.
|
||||
// Documented here: https://developer.apple.com/documentation/uikit/uiviewcontroller/1621426-presentationcontroller?language=objc
|
||||
_controller.presentationController.delegate = self;
|
||||
} else if (_stackPresentation != RNSScreenStackPresentationPush) {
|
||||
RCTLogError(@"Screen presentation updated from modal to push, this may likely result in a screen object leakage. If you need to change presentation style create a new screen object instead");
|
||||
}
|
||||
_stackPresentation = stackPresentation;
|
||||
}
|
||||
|
||||
- (void)setStackAnimation:(RNSScreenStackAnimation)stackAnimation
|
||||
{
|
||||
_stackAnimation = stackAnimation;
|
||||
|
||||
switch (stackAnimation) {
|
||||
case RNSScreenStackAnimationFade:
|
||||
_controller.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
|
||||
break;
|
||||
#if (TARGET_OS_IOS)
|
||||
case RNSScreenStackAnimationFlip:
|
||||
_controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
|
||||
break;
|
||||
#endif
|
||||
case RNSScreenStackAnimationNone:
|
||||
case RNSScreenStackAnimationDefault:
|
||||
// Default
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setGestureEnabled:(BOOL)gestureEnabled
|
||||
{
|
||||
#ifdef __IPHONE_13_0
|
||||
if (@available(iOS 13.0, *)) {
|
||||
_controller.modalInPresentation = !gestureEnabled;
|
||||
}
|
||||
#endif
|
||||
|
||||
_gestureEnabled = gestureEnabled;
|
||||
}
|
||||
|
||||
- (void)setReplaceAnimation:(RNSScreenReplaceAnimation)replaceAnimation
|
||||
{
|
||||
_replaceAnimation = replaceAnimation;
|
||||
}
|
||||
|
||||
- (UIView *)reactSuperview
|
||||
{
|
||||
return _reactSuperview;
|
||||
}
|
||||
|
||||
- (void)addSubview:(UIView *)view
|
||||
{
|
||||
if (![view isKindOfClass:[RNSScreenStackHeaderConfig class]]) {
|
||||
[super addSubview:view];
|
||||
} else {
|
||||
((RNSScreenStackHeaderConfig*) view).screenView = self;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)notifyFinishTransitioning
|
||||
{
|
||||
[_controller notifyFinishTransitioning];
|
||||
}
|
||||
|
||||
- (void)notifyDismissed
|
||||
{
|
||||
_dismissed = YES;
|
||||
if (self.onDismissed) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (self.onDismissed) {
|
||||
self.onDismissed(nil);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)notifyWillAppear
|
||||
{
|
||||
if (self.onWillAppear) {
|
||||
self.onWillAppear(nil);
|
||||
}
|
||||
// we do it here too because at this moment the `parentViewController` is already not nil,
|
||||
// so if the parent is not UINavCtr, the frame will be updated to the correct one.
|
||||
[self reactSetFrame:_reactFrame];
|
||||
}
|
||||
|
||||
- (void)notifyWillDisappear
|
||||
{
|
||||
if (self.onWillDisappear) {
|
||||
self.onWillDisappear(nil);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)notifyAppear
|
||||
{
|
||||
if (self.onAppear) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (self.onAppear) {
|
||||
self.onAppear(nil);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)notifyDisappear
|
||||
{
|
||||
if (self.onDisappear) {
|
||||
self.onDisappear(nil);
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isMountedUnderScreenOrReactRoot
|
||||
{
|
||||
for (UIView *parent = self.superview; parent != nil; parent = parent.superview) {
|
||||
if ([parent isKindOfClass:[RCTRootView class]] || [parent isKindOfClass:[RNSScreenView class]]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)didMoveToWindow
|
||||
{
|
||||
// For RN touches to work we need to instantiate and connect RCTTouchHandler. This only applies
|
||||
// for screens that aren't mounted under RCTRootView e.g., modals that are mounted directly to
|
||||
// root application window.
|
||||
if (self.window != nil && ![self isMountedUnderScreenOrReactRoot]) {
|
||||
if (_touchHandler == nil) {
|
||||
_touchHandler = [[RCTTouchHandler alloc] initWithBridge:_bridge];
|
||||
}
|
||||
[_touchHandler attachToView:self];
|
||||
} else {
|
||||
[_touchHandler detachFromView:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)presentationControllerWillDismiss:(UIPresentationController *)presentationController
|
||||
{
|
||||
// We need to call both "cancel" and "reset" here because RN's gesture recognizer
|
||||
// does not handle the scenario when it gets cancelled by other top
|
||||
// level gesture recognizer. In this case by the modal dismiss gesture.
|
||||
// Because of that, at the moment when this method gets called the React's
|
||||
// gesture recognizer is already in FAILED state but cancel events never gets
|
||||
// send to JS. Calling "reset" forces RCTTouchHanler to dispatch cancel event.
|
||||
// To test this behavior one need to open a dismissable modal and start
|
||||
// pulling down starting at some touchable item. Without "reset" the touchable
|
||||
// will never go back from highlighted state even when the modal start sliding
|
||||
// down.
|
||||
[_touchHandler cancel];
|
||||
[_touchHandler reset];
|
||||
}
|
||||
|
||||
- (BOOL)presentationControllerShouldDismiss:(UIPresentationController *)presentationController
|
||||
{
|
||||
return _gestureEnabled;
|
||||
}
|
||||
|
||||
- (void)presentationControllerDidDismiss:(UIPresentationController *)presentationController
|
||||
{
|
||||
if ([_reactSuperview respondsToSelector:@selector(presentationControllerDidDismiss:)]) {
|
||||
[_reactSuperview performSelector:@selector(presentationControllerDidDismiss:)
|
||||
withObject:presentationController];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
_controller = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation RNSScreen {
|
||||
__weak id _previousFirstResponder;
|
||||
CGRect _lastViewFrame;
|
||||
}
|
||||
|
||||
- (instancetype)initWithView:(UIView *)view
|
||||
{
|
||||
if (self = [super init]) {
|
||||
self.view = view;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (UIViewController *)childViewControllerForStatusBarStyle
|
||||
{
|
||||
UIViewController *vc = [self findChildVCForConfig];
|
||||
return vc == self ? nil : vc;
|
||||
}
|
||||
|
||||
- (UIStatusBarStyle)preferredStatusBarStyle
|
||||
{
|
||||
RNSScreenStackHeaderConfig *config = [self findScreenConfig];
|
||||
return [RNSScreenStackHeaderConfig statusBarStyleForRNSStatusBarStyle:config && config.statusBarStyle ? config.statusBarStyle : RNSStatusBarStyleAuto];
|
||||
}
|
||||
|
||||
- (UIViewController *)childViewControllerForStatusBarHidden
|
||||
{
|
||||
UIViewController *vc = [self findChildVCForConfig];
|
||||
return vc == self ? nil : vc;
|
||||
}
|
||||
|
||||
- (BOOL)prefersStatusBarHidden
|
||||
{
|
||||
RNSScreenStackHeaderConfig *config = [self findScreenConfig];
|
||||
return config && config.statusBarHidden ? config.statusBarHidden : NO;
|
||||
}
|
||||
|
||||
- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation
|
||||
{
|
||||
UIViewController *vc = [self findChildVCForConfig];
|
||||
if (vc != self && vc != nil) {
|
||||
return vc.preferredStatusBarUpdateAnimation;
|
||||
}
|
||||
|
||||
RNSScreenStackHeaderConfig *config = [self findScreenConfig];
|
||||
return config && config.statusBarAnimation ? config.statusBarAnimation : UIStatusBarAnimationFade;
|
||||
}
|
||||
|
||||
// if the returned vc is a child, it means that it can provide config;
|
||||
// if the returned vc is self, it means that there is no child for config and self has config to provide,
|
||||
// so we return self which results in asking self for preferredStatusBarStyle;
|
||||
// if the returned vc is nil, it means none of children could provide config and self does not have config either,
|
||||
// so if it was asked by parent, it will fallback to parent's option, or use default option if it is the top Screen
|
||||
- (UIViewController *)findChildVCForConfig
|
||||
{
|
||||
UIViewController *lastViewController = [[self childViewControllers] lastObject];
|
||||
if (self.presentedViewController != nil) {
|
||||
lastViewController = self.presentedViewController;
|
||||
// setting this makes the modal vc being asked for appearance,
|
||||
// so it doesn't matter what we return here since the modal's root screen will be asked
|
||||
lastViewController.modalPresentationCapturesStatusBarAppearance = YES;
|
||||
return nil;
|
||||
}
|
||||
|
||||
UIViewController *selfOrNil = [self findScreenConfig] != nil ? self : nil;
|
||||
if (lastViewController == nil) {
|
||||
return selfOrNil;
|
||||
} else {
|
||||
if ([lastViewController conformsToProtocol:@protocol(RNScreensViewControllerDelegate)]) {
|
||||
// If there is a child (should be VC of ScreenContainer or ScreenStack), that has a child that could provide config,
|
||||
// we recursively go into its findChildVCForConfig, and if one of the children has the config, we return it,
|
||||
// otherwise we return self if this VC has config, and nil if it doesn't
|
||||
// we use `childViewControllerForStatusBarStyle` for all options since the behavior is the same for all of them
|
||||
UIViewController *childScreen = [lastViewController childViewControllerForStatusBarStyle];
|
||||
if ([childScreen isKindOfClass:[RNSScreen class]]) {
|
||||
return [(RNSScreen *)childScreen findChildVCForConfig] ?: selfOrNil;
|
||||
} else {
|
||||
return selfOrNil;
|
||||
}
|
||||
} else {
|
||||
// child vc is not from this library, so we don't ask it
|
||||
return selfOrNil;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (RNSScreenStackHeaderConfig *)findScreenConfig
|
||||
{
|
||||
for (UIView *subview in self.view.reactSubviews) {
|
||||
if ([subview isKindOfClass:[RNSScreenStackHeaderConfig class]]) {
|
||||
return (RNSScreenStackHeaderConfig *)subview;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)viewDidLayoutSubviews
|
||||
{
|
||||
[super viewDidLayoutSubviews];
|
||||
|
||||
// The below code makes the screen view adapt dimensions provided by the system. We take these
|
||||
// into account only when the view is mounted under UINavigationController in which case system
|
||||
// provides additional padding to account for possible header, and in the case when screen is
|
||||
// shown as a native modal, as the final dimensions of the modal on iOS 12+ are shorter than the
|
||||
// screen size
|
||||
BOOL isDisplayedWithinUINavController = [self.parentViewController isKindOfClass:[UINavigationController class]];
|
||||
BOOL isPresentedAsNativeModal = self.parentViewController == nil && self.presentingViewController != nil;
|
||||
if ((isDisplayedWithinUINavController || isPresentedAsNativeModal) && !CGRectEqualToRect(_lastViewFrame, self.view.frame)) {
|
||||
_lastViewFrame = self.view.frame;
|
||||
[((RNSScreenView *)self.viewIfLoaded) updateBounds];
|
||||
}
|
||||
}
|
||||
|
||||
- (id)findFirstResponder:(UIView*)parent
|
||||
{
|
||||
if (parent.isFirstResponder) {
|
||||
return parent;
|
||||
}
|
||||
for (UIView *subView in parent.subviews) {
|
||||
id responder = [self findFirstResponder:subView];
|
||||
if (responder != nil) {
|
||||
return responder;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)willMoveToParentViewController:(UIViewController *)parent
|
||||
{
|
||||
[super willMoveToParentViewController:parent];
|
||||
if (parent == nil) {
|
||||
id responder = [self findFirstResponder:self.view];
|
||||
if (responder != nil) {
|
||||
_previousFirstResponder = responder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
[RNSScreenStackHeaderConfig updateStatusBarAppearance];
|
||||
[((RNSScreenView *)self.view) notifyWillAppear];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewWillDisappear:animated];
|
||||
|
||||
[((RNSScreenView *)self.view) notifyWillDisappear];
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated
|
||||
{
|
||||
[super viewDidAppear:animated];
|
||||
[((RNSScreenView *)self.view) notifyAppear];
|
||||
}
|
||||
|
||||
- (void)viewDidDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewDidDisappear:animated];
|
||||
|
||||
[((RNSScreenView *)self.view) notifyDisappear];
|
||||
if (self.parentViewController == nil && self.presentingViewController == nil) {
|
||||
// screen dismissed, send event
|
||||
[((RNSScreenView *)self.view) notifyDismissed];
|
||||
}
|
||||
[self traverseForScrollView:self.view];
|
||||
}
|
||||
|
||||
- (void)traverseForScrollView:(UIView*)view
|
||||
{
|
||||
if([view isKindOfClass:[UIScrollView class]] && ([[(UIScrollView*)view delegate] respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) ) {
|
||||
[[(UIScrollView*)view delegate] scrollViewDidEndDecelerating:(id)view];
|
||||
}
|
||||
[view.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
[self traverseForScrollView:obj];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)notifyFinishTransitioning
|
||||
{
|
||||
[_previousFirstResponder becomeFirstResponder];
|
||||
_previousFirstResponder = nil;
|
||||
// the correct Screen for appearance is set after the transition
|
||||
[RNSScreenStackHeaderConfig updateStatusBarAppearance];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation RNSScreenManager
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
// we want to handle the case when activityState is nil
|
||||
RCT_REMAP_VIEW_PROPERTY(activityState, activityStateOrNil, NSNumber)
|
||||
RCT_EXPORT_VIEW_PROPERTY(gestureEnabled, BOOL)
|
||||
RCT_EXPORT_VIEW_PROPERTY(replaceAnimation, RNSScreenReplaceAnimation)
|
||||
RCT_EXPORT_VIEW_PROPERTY(stackPresentation, RNSScreenStackPresentation)
|
||||
RCT_EXPORT_VIEW_PROPERTY(stackAnimation, RNSScreenStackAnimation)
|
||||
RCT_EXPORT_VIEW_PROPERTY(onWillAppear, RCTDirectEventBlock);
|
||||
RCT_EXPORT_VIEW_PROPERTY(onWillDisappear, RCTDirectEventBlock);
|
||||
RCT_EXPORT_VIEW_PROPERTY(onAppear, RCTDirectEventBlock);
|
||||
RCT_EXPORT_VIEW_PROPERTY(onDisappear, RCTDirectEventBlock);
|
||||
RCT_EXPORT_VIEW_PROPERTY(onDismissed, RCTDirectEventBlock);
|
||||
|
||||
- (UIView *)view
|
||||
{
|
||||
return [[RNSScreenView alloc] initWithBridge:self.bridge];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTConvert (RNSScreen)
|
||||
|
||||
RCT_ENUM_CONVERTER(RNSScreenStackPresentation, (@{
|
||||
@"push": @(RNSScreenStackPresentationPush),
|
||||
@"modal": @(RNSScreenStackPresentationModal),
|
||||
@"fullScreenModal": @(RNSScreenStackPresentationFullScreenModal),
|
||||
@"formSheet": @(RNSScreenStackPresentationFormSheet),
|
||||
@"containedModal": @(RNSScreenStackPresentationContainedModal),
|
||||
@"transparentModal": @(RNSScreenStackPresentationTransparentModal),
|
||||
@"containedTransparentModal": @(RNSScreenStackPresentationContainedTransparentModal)
|
||||
}), RNSScreenStackPresentationPush, integerValue)
|
||||
|
||||
RCT_ENUM_CONVERTER(RNSScreenStackAnimation, (@{
|
||||
@"default": @(RNSScreenStackAnimationDefault),
|
||||
@"none": @(RNSScreenStackAnimationNone),
|
||||
@"fade": @(RNSScreenStackAnimationFade),
|
||||
@"flip": @(RNSScreenStackAnimationFlip),
|
||||
}), RNSScreenStackAnimationDefault, integerValue)
|
||||
|
||||
RCT_ENUM_CONVERTER(RNSScreenReplaceAnimation, (@{
|
||||
@"push": @(RNSScreenReplaceAnimationPush),
|
||||
@"pop": @(RNSScreenReplaceAnimationPop),
|
||||
}), RNSScreenReplaceAnimationPop, integerValue)
|
||||
|
||||
@end
|
19
node_modules/react-native-screens/ios/RNSScreenContainer.h
generated
vendored
Normal file
19
node_modules/react-native-screens/ios/RNSScreenContainer.h
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
#import <React/RCTViewManager.h>
|
||||
|
||||
@protocol RNSScreenContainerDelegate
|
||||
|
||||
- (void)markChildUpdated;
|
||||
|
||||
@end
|
||||
|
||||
@protocol RNScreensViewControllerDelegate
|
||||
|
||||
@end
|
||||
|
||||
@interface RNScreensViewController: UIViewController <RNScreensViewControllerDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@interface RNSScreenContainerView : UIView <RNSScreenContainerDelegate>
|
||||
|
||||
@end
|
279
node_modules/react-native-screens/ios/RNSScreenContainer.m
generated
vendored
Normal file
279
node_modules/react-native-screens/ios/RNSScreenContainer.m
generated
vendored
Normal file
@ -0,0 +1,279 @@
|
||||
#import "RNSScreenContainer.h"
|
||||
#import "RNSScreen.h"
|
||||
|
||||
#import <React/RCTUIManager.h>
|
||||
#import <React/RCTUIManagerObserverCoordinator.h>
|
||||
#import <React/RCTUIManagerUtils.h>
|
||||
|
||||
@interface RNSScreenContainerManager : RCTViewManager
|
||||
|
||||
- (void)markUpdated:(RNSScreenContainerView *)screen;
|
||||
|
||||
@end
|
||||
|
||||
@interface RNSScreenContainerView () <RCTInvalidating>
|
||||
|
||||
@property (nonatomic, retain) UIViewController *controller;
|
||||
@property (nonatomic, retain) NSMutableSet<RNSScreenView *> *activeScreens;
|
||||
@property (nonatomic, retain) NSMutableArray<RNSScreenView *> *reactSubviews;
|
||||
|
||||
- (void)updateContainer;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RNScreensViewController
|
||||
|
||||
- (UIViewController *)childViewControllerForStatusBarStyle
|
||||
{
|
||||
return [self findActiveChildVC];
|
||||
}
|
||||
|
||||
- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation
|
||||
{
|
||||
return [self findActiveChildVC].preferredStatusBarUpdateAnimation;
|
||||
}
|
||||
|
||||
- (UIViewController *)childViewControllerForStatusBarHidden
|
||||
{
|
||||
return [self findActiveChildVC];
|
||||
|
||||
}
|
||||
|
||||
- (UIViewController *)findActiveChildVC
|
||||
{
|
||||
for (UIViewController *childVC in self.childViewControllers) {
|
||||
if ([childVC isKindOfClass:[RNSScreen class]] && ((RNSScreenView *)((RNSScreen *)childVC.view)).activityState == RNSActivityStateOnTop) {
|
||||
return childVC;
|
||||
}
|
||||
}
|
||||
return [[self childViewControllers] lastObject];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation RNSScreenContainerView {
|
||||
BOOL _needUpdate;
|
||||
BOOL _invalidated;
|
||||
__weak RNSScreenContainerManager *_manager;
|
||||
}
|
||||
|
||||
- (instancetype)initWithManager:(RNSScreenContainerManager *)manager
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_activeScreens = [NSMutableSet new];
|
||||
_reactSubviews = [NSMutableArray new];
|
||||
_controller = [[RNScreensViewController alloc] init];
|
||||
_needUpdate = NO;
|
||||
_invalidated = NO;
|
||||
_manager = manager;
|
||||
[self addSubview:_controller.view];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)markChildUpdated
|
||||
{
|
||||
// We want 'updateContainer' to be executed on main thread after all enqueued operations in
|
||||
// uimanager are complete. For that we collect all marked containers in manager class and enqueue
|
||||
// operation on ui thread that should run once all the updates are completed.
|
||||
if (!_needUpdate) {
|
||||
_needUpdate = YES;
|
||||
[_manager markUpdated:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)insertReactSubview:(RNSScreenView *)subview atIndex:(NSInteger)atIndex
|
||||
{
|
||||
subview.reactSuperview = self;
|
||||
[_reactSubviews insertObject:subview atIndex:atIndex];
|
||||
[subview reactSetFrame:CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height)];
|
||||
}
|
||||
|
||||
- (void)removeReactSubview:(RNSScreenView *)subview
|
||||
{
|
||||
subview.reactSuperview = nil;
|
||||
[_reactSubviews removeObject:subview];
|
||||
}
|
||||
|
||||
- (NSArray<UIView *> *)reactSubviews
|
||||
{
|
||||
return _reactSubviews;
|
||||
}
|
||||
|
||||
- (UIViewController *)reactViewController
|
||||
{
|
||||
return _controller;
|
||||
}
|
||||
|
||||
- (UIViewController*)findChildControllerForScreen:(RNSScreenView*)screen
|
||||
{
|
||||
for (UIViewController *vc in _controller.childViewControllers) {
|
||||
if (vc.view == screen) {
|
||||
return vc;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)prepareDetach:(RNSScreenView *)screen
|
||||
{
|
||||
[[self findChildControllerForScreen:screen] willMoveToParentViewController:nil];
|
||||
}
|
||||
|
||||
- (void)detachScreen:(RNSScreenView *)screen
|
||||
{
|
||||
// We use findChildControllerForScreen method instead of directly accesing
|
||||
// screen.controller because screen.controller may be reset to nil when the
|
||||
// original screen view gets detached from the view hierarchy (we reset controller
|
||||
// reference to avoid reference loops)
|
||||
UIViewController *detachController = [self findChildControllerForScreen:screen];
|
||||
[detachController willMoveToParentViewController:nil];
|
||||
[screen removeFromSuperview];
|
||||
[detachController removeFromParentViewController];
|
||||
[_activeScreens removeObject:screen];
|
||||
}
|
||||
|
||||
- (void)attachScreen:(RNSScreenView *)screen atIndex:(NSInteger)index
|
||||
{
|
||||
[_controller addChildViewController:screen.controller];
|
||||
// the frame is already set at this moment because we adjust it in insertReactSubview. We don't
|
||||
// want to update it here as react-driven animations may already run and hence changing the frame
|
||||
// would result in visual glitches
|
||||
[_controller.view insertSubview:screen.controller.view atIndex:index];
|
||||
[screen.controller didMoveToParentViewController:_controller];
|
||||
[_activeScreens addObject:screen];
|
||||
}
|
||||
|
||||
- (void)updateContainer
|
||||
{
|
||||
_needUpdate = NO;
|
||||
BOOL screenRemoved = NO;
|
||||
// remove screens that are no longer active
|
||||
NSMutableSet *orphaned = [NSMutableSet setWithSet:_activeScreens];
|
||||
for (RNSScreenView *screen in _reactSubviews) {
|
||||
if (screen.activityState == RNSActivityStateInactive && [_activeScreens containsObject:screen]) {
|
||||
screenRemoved = YES;
|
||||
[self detachScreen:screen];
|
||||
}
|
||||
[orphaned removeObject:screen];
|
||||
}
|
||||
for (RNSScreenView *screen in orphaned) {
|
||||
screenRemoved = YES;
|
||||
[self detachScreen:screen];
|
||||
}
|
||||
|
||||
// detect if new screen is going to be activated
|
||||
BOOL screenAdded = NO;
|
||||
for (RNSScreenView *screen in _reactSubviews) {
|
||||
if (screen.activityState != RNSActivityStateInactive && ![_activeScreens containsObject:screen]) {
|
||||
screenAdded = YES;
|
||||
}
|
||||
}
|
||||
|
||||
if (screenAdded) {
|
||||
// add new screens in order they are placed in subviews array
|
||||
NSInteger index = 0;
|
||||
for (RNSScreenView *screen in _reactSubviews) {
|
||||
if (screen.activityState != RNSActivityStateInactive) {
|
||||
if ([_activeScreens containsObject:screen] && screen.activityState == RNSActivityStateTransitioningOrBelowTop) {
|
||||
// for screens that were already active we want to mimick the effect UINavigationController
|
||||
// has when willMoveToWindow:nil is triggered before the animation starts
|
||||
[self prepareDetach:screen];
|
||||
} else if (![_activeScreens containsObject:screen]) {
|
||||
[self attachScreen:screen atIndex:index];
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (screenRemoved || screenAdded) {
|
||||
// we disable interaction for the duration of the transition until one of the screens changes its state to "onTop"
|
||||
self.userInteractionEnabled = NO;
|
||||
}
|
||||
|
||||
for (RNSScreenView *screen in _reactSubviews) {
|
||||
if (screen.activityState == RNSActivityStateOnTop) {
|
||||
// if there is an "onTop" screen it means the transition has ended so we restore interactions
|
||||
self.userInteractionEnabled = YES;
|
||||
[screen notifyFinishTransitioning];
|
||||
}
|
||||
}
|
||||
|
||||
if ((screenRemoved || screenAdded) && _controller.presentedViewController == nil && _controller.presentingViewController == nil) {
|
||||
// if user has reachability enabled (one hand use) and the window is slided down the below
|
||||
// method will force it to slide back up as it is expected to happen with UINavController when
|
||||
// we push or pop views.
|
||||
// We only do that if `presentedViewController` is nil, as otherwise it'd mean that modal has
|
||||
// been presented on top of recently changed controller in which case the below method would
|
||||
// dismiss such a modal (e.g., permission modal or alert)
|
||||
[_controller dismissViewControllerAnimated:NO completion:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)didUpdateReactSubviews
|
||||
{
|
||||
[self markChildUpdated];
|
||||
}
|
||||
|
||||
- (void)didMoveToWindow
|
||||
{
|
||||
if (self.window && !_invalidated) {
|
||||
// We check whether the view has been invalidated before running side-effects in didMoveToWindow
|
||||
// This is needed because when LayoutAnimations are used it is possible for view to be re-attached
|
||||
// to a window despite the fact it has been removed from the React Native view hierarchy.
|
||||
[self reactAddControllerToClosestParent:_controller];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
_invalidated = YES;
|
||||
[_controller willMoveToParentViewController:nil];
|
||||
[_controller removeFromParentViewController];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
_controller.view.frame = self.bounds;
|
||||
for (RNSScreenView *subview in _reactSubviews) {
|
||||
[subview reactSetFrame:CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height)];
|
||||
[subview setNeedsLayout];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation RNSScreenContainerManager {
|
||||
NSMutableArray<RNSScreenContainerView *> *_markedContainers;
|
||||
}
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (UIView *)view
|
||||
{
|
||||
if (!_markedContainers) {
|
||||
_markedContainers = [NSMutableArray new];
|
||||
}
|
||||
return [[RNSScreenContainerView alloc] initWithManager:self];
|
||||
}
|
||||
|
||||
- (void)markUpdated:(RNSScreenContainerView *)screen
|
||||
{
|
||||
RCTAssertMainQueue();
|
||||
[_markedContainers addObject:screen];
|
||||
if ([_markedContainers count] == 1) {
|
||||
// we enqueue updates to be run on the main queue in order to make sure that
|
||||
// all this updates (new screens attached etc) are executed in one batch
|
||||
RCTExecuteOnMainQueue(^{
|
||||
for (RNSScreenContainerView *container in self->_markedContainers) {
|
||||
[container updateContainer];
|
||||
}
|
||||
[self->_markedContainers removeAllObjects];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
21
node_modules/react-native-screens/ios/RNSScreenStack.h
generated
vendored
Normal file
21
node_modules/react-native-screens/ios/RNSScreenStack.h
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
#import <React/RCTViewManager.h>
|
||||
#import <React/RCTUIManagerObserverCoordinator.h>
|
||||
|
||||
#import "RNSScreenContainer.h"
|
||||
|
||||
@interface RNScreensNavigationController: UINavigationController <RNScreensViewControllerDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@interface RNSScreenStackView : UIView <RNSScreenContainerDelegate, RCTInvalidating>
|
||||
|
||||
@property (nonatomic, copy) RCTDirectEventBlock onFinishTransitioning;
|
||||
|
||||
- (void)markChildUpdated;
|
||||
- (void)didUpdateChildren;
|
||||
|
||||
@end
|
||||
|
||||
@interface RNSScreenStackManager : RCTViewManager <RCTInvalidating>
|
||||
|
||||
@end
|
583
node_modules/react-native-screens/ios/RNSScreenStack.m
generated
vendored
Normal file
583
node_modules/react-native-screens/ios/RNSScreenStack.m
generated
vendored
Normal file
@ -0,0 +1,583 @@
|
||||
#import "RNSScreenStack.h"
|
||||
#import "RNSScreen.h"
|
||||
#import "RNSScreenStackHeaderConfig.h"
|
||||
|
||||
#import <React/RCTBridge.h>
|
||||
#import <React/RCTUIManager.h>
|
||||
#import <React/RCTUIManagerUtils.h>
|
||||
#import <React/RCTShadowView.h>
|
||||
#import <React/RCTRootContentView.h>
|
||||
#import <React/RCTTouchHandler.h>
|
||||
|
||||
@interface RNSScreenStackView () <UINavigationControllerDelegate, UIAdaptivePresentationControllerDelegate, UIGestureRecognizerDelegate>
|
||||
|
||||
@property (nonatomic) NSMutableArray<UIViewController *> *presentedModals;
|
||||
@property (nonatomic) BOOL updatingModals;
|
||||
@property (nonatomic) BOOL scheduleModalsUpdate;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RNScreensNavigationController
|
||||
|
||||
- (UIViewController *)childViewControllerForStatusBarStyle
|
||||
{
|
||||
return [self topViewController];
|
||||
}
|
||||
|
||||
- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation
|
||||
{
|
||||
return [self topViewController].preferredStatusBarUpdateAnimation;
|
||||
}
|
||||
|
||||
- (UIViewController *)childViewControllerForStatusBarHidden
|
||||
{
|
||||
return [self topViewController];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface RNSScreenStackAnimator : NSObject <UIViewControllerAnimatedTransitioning>
|
||||
- (instancetype)initWithOperation:(UINavigationControllerOperation)operation;
|
||||
@end
|
||||
|
||||
@implementation RNSScreenStackView {
|
||||
UINavigationController *_controller;
|
||||
NSMutableArray<RNSScreenView *> *_reactSubviews;
|
||||
__weak RNSScreenStackManager *_manager;
|
||||
BOOL _hasLayout;
|
||||
BOOL _invalidated;
|
||||
}
|
||||
|
||||
- (instancetype)initWithManager:(RNSScreenStackManager*)manager
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_hasLayout = NO;
|
||||
_invalidated = NO;
|
||||
_manager = manager;
|
||||
_reactSubviews = [NSMutableArray new];
|
||||
_presentedModals = [NSMutableArray new];
|
||||
_controller = [[RNScreensNavigationController alloc] init];
|
||||
_controller.delegate = self;
|
||||
|
||||
// we have to initialize viewControllers with a non empty array for
|
||||
// largeTitle header to render in the opened state. If it is empty
|
||||
// the header will render in collapsed state which is perhaps a bug
|
||||
// in UIKit but ¯\_(ツ)_/¯
|
||||
[_controller setViewControllers:@[[UIViewController new]]];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (UIViewController *)reactViewController
|
||||
{
|
||||
return _controller;
|
||||
}
|
||||
|
||||
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
|
||||
{
|
||||
UIView *view = viewController.view;
|
||||
RNSScreenStackHeaderConfig *config = nil;
|
||||
for (UIView *subview in view.reactSubviews) {
|
||||
if ([subview isKindOfClass:[RNSScreenStackHeaderConfig class]]) {
|
||||
config = (RNSScreenStackHeaderConfig*) subview;
|
||||
break;
|
||||
}
|
||||
}
|
||||
[RNSScreenStackHeaderConfig willShowViewController:viewController animated:animated withConfig:config];
|
||||
}
|
||||
|
||||
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
|
||||
{
|
||||
if (self.onFinishTransitioning) {
|
||||
self.onFinishTransitioning(nil);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)presentationControllerDidDismiss:(UIPresentationController *)presentationController
|
||||
{
|
||||
// We don't directly set presentation delegate but instead rely on the ScreenView's delegate to
|
||||
// forward certain calls to the container (Stack).
|
||||
UIView *screenView = presentationController.presentedViewController.view;
|
||||
if ([screenView isKindOfClass:[RNSScreenView class]]) {
|
||||
// we trigger the update of status bar's appearance here because there is no other lifecycle method
|
||||
// that can handle it when dismissing a modal
|
||||
[RNSScreenStackHeaderConfig updateStatusBarAppearance];
|
||||
[_presentedModals removeObject:presentationController.presentedViewController];
|
||||
if (self.onFinishTransitioning) {
|
||||
// instead of directly triggering onFinishTransitioning this time we enqueue the event on the
|
||||
// main queue. We do that because onDismiss event is also enqueued and we want for the transition
|
||||
// finish event to arrive later than onDismiss (see RNSScreen#notifyDismiss)
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (self.onFinishTransitioning) {
|
||||
self.onFinishTransitioning(nil);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC
|
||||
{
|
||||
RNSScreenView *screen;
|
||||
if (operation == UINavigationControllerOperationPush) {
|
||||
screen = (RNSScreenView *) toVC.view;
|
||||
} else if (operation == UINavigationControllerOperationPop) {
|
||||
screen = (RNSScreenView *) fromVC.view;
|
||||
}
|
||||
if (screen != nil && (screen.stackAnimation == RNSScreenStackAnimationFade || screen.stackAnimation == RNSScreenStackAnimationNone)) {
|
||||
return [[RNSScreenStackAnimator alloc] initWithOperation:operation];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
|
||||
{
|
||||
// cancel touches in parent, this is needed to cancel RN touch events. For example when Touchable
|
||||
// item is close to an edge and we start pulling from edge we want the Touchable to be cancelled.
|
||||
// Without the below code the Touchable will remain active (highlighted) for the duration of back
|
||||
// gesture and onPress may fire when we release the finger.
|
||||
UIView *parent = _controller.view;
|
||||
while (parent != nil && ![parent isKindOfClass:[RCTRootContentView class]]) parent = parent.superview;
|
||||
RCTRootContentView *rootView = (RCTRootContentView *)parent;
|
||||
[rootView.touchHandler cancel];
|
||||
|
||||
RNSScreenView *topScreen = (RNSScreenView *)_controller.viewControllers.lastObject.view;
|
||||
|
||||
return _controller.viewControllers.count > 1 && topScreen.gestureEnabled;
|
||||
}
|
||||
|
||||
- (void)markChildUpdated
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
- (void)didUpdateChildren
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
- (void)insertReactSubview:(RNSScreenView *)subview atIndex:(NSInteger)atIndex
|
||||
{
|
||||
if (![subview isKindOfClass:[RNSScreenView class]]) {
|
||||
RCTLogError(@"ScreenStack only accepts children of type Screen");
|
||||
return;
|
||||
}
|
||||
subview.reactSuperview = self;
|
||||
[_reactSubviews insertObject:subview atIndex:atIndex];
|
||||
}
|
||||
|
||||
- (void)removeReactSubview:(RNSScreenView *)subview
|
||||
{
|
||||
subview.reactSuperview = nil;
|
||||
[_reactSubviews removeObject:subview];
|
||||
}
|
||||
|
||||
- (NSArray<UIView *> *)reactSubviews
|
||||
{
|
||||
return _reactSubviews;
|
||||
}
|
||||
|
||||
- (void)didUpdateReactSubviews
|
||||
{
|
||||
// we need to wait until children have their layout set. At this point they don't have the layout
|
||||
// set yet, however the layout call is already enqueued on ui thread. Enqueuing update call on the
|
||||
// ui queue will guarantee that the update will run after layout.
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self->_hasLayout = YES;
|
||||
[self maybeAddToParentAndUpdateContainer];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)didMoveToWindow
|
||||
{
|
||||
[super didMoveToWindow];
|
||||
if (!_invalidated) {
|
||||
// We check whether the view has been invalidated before running side-effects in didMoveToWindow
|
||||
// This is needed because when LayoutAnimations are used it is possible for view to be re-attached
|
||||
// to a window despite the fact it has been removed from the React Native view hierarchy.
|
||||
[self maybeAddToParentAndUpdateContainer];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)maybeAddToParentAndUpdateContainer
|
||||
{
|
||||
BOOL wasScreenMounted = _controller.parentViewController != nil;
|
||||
BOOL isScreenReadyForShowing = self.window && _hasLayout;
|
||||
if (!isScreenReadyForShowing && !wasScreenMounted) {
|
||||
// We wait with adding to parent controller until the stack is mounted and has its initial
|
||||
// layout done.
|
||||
// If we add it before layout, some of the items (specifically items from the navigation bar),
|
||||
// won't be able to position properly. Also the position and size of such items, even if it
|
||||
// happens to change, won't be properly updated (this is perhaps some internal issue of UIKit).
|
||||
// If we add it when window is not attached, some of the view transitions will be bloced (i.e.
|
||||
// modal transitions) and the internal view controler's state will get out of sync with what's
|
||||
// on screen without us knowing.
|
||||
return;
|
||||
}
|
||||
[self updateContainer];
|
||||
if (!wasScreenMounted) {
|
||||
// when stack hasn't been added to parent VC yet we do two things:
|
||||
// 1) we run updateContainer (the one above) – we do this because we want push view controllers to
|
||||
// be installed before the VC is mounted. If we do that after it is added to parent the push
|
||||
// updates operations are going to be blocked by UIKit.
|
||||
// 2) we add navigation VS to parent – this is needed for the VC lifecycle events to be dispatched
|
||||
// properly
|
||||
// 3) we again call updateContainer – this time we do this to open modal controllers. Modals
|
||||
// won't open in (1) because they require navigator to be added to parent. We handle that case
|
||||
// gracefully in setModalViewControllers and can retry opening at any point.
|
||||
[self reactAddControllerToClosestParent:_controller];
|
||||
[self updateContainer];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)reactAddControllerToClosestParent:(UIViewController *)controller
|
||||
{
|
||||
if (!controller.parentViewController) {
|
||||
UIView *parentView = (UIView *)self.reactSuperview;
|
||||
while (parentView) {
|
||||
if (parentView.reactViewController) {
|
||||
[parentView.reactViewController addChildViewController:controller];
|
||||
[self addSubview:controller.view];
|
||||
#if (TARGET_OS_IOS)
|
||||
_controller.interactivePopGestureRecognizer.delegate = self;
|
||||
#endif
|
||||
[controller didMoveToParentViewController:parentView.reactViewController];
|
||||
// On iOS pre 12 we observed that `willShowViewController` delegate method does not always
|
||||
// get triggered when the navigation controller is instantiated. As the only thing we do in
|
||||
// that delegate method is ask nav header to update to the current state it does not hurt to
|
||||
// trigger that logic from here too such that we can be sure the header is properly updated.
|
||||
[self navigationController:_controller willShowViewController:_controller.topViewController animated:NO];
|
||||
break;
|
||||
}
|
||||
parentView = (UIView *)parentView.reactSuperview;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setModalViewControllers:(NSArray<UIViewController *> *)controllers
|
||||
{
|
||||
// prevent re-entry
|
||||
if (_updatingModals) {
|
||||
_scheduleModalsUpdate = YES;
|
||||
return;
|
||||
}
|
||||
|
||||
// when there is no change we return immediately. This check is important because sometime we may
|
||||
// accidently trigger modal dismiss if we don't verify to run the below code only when an actual
|
||||
// change in the list of presented modal was made.
|
||||
if ([_presentedModals isEqualToArray:controllers]) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if view controller is not yet attached to window we skip updates now and run them when view
|
||||
// is attached
|
||||
if (self.window == nil && _presentedModals.lastObject.view.window == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
_updatingModals = YES;
|
||||
|
||||
NSMutableArray<UIViewController *> *newControllers = [NSMutableArray arrayWithArray:controllers];
|
||||
[newControllers removeObjectsInArray:_presentedModals];
|
||||
|
||||
// find bottom-most controller that should stay on the stack for the duration of transition
|
||||
NSUInteger changeRootIndex = 0;
|
||||
UIViewController *changeRootController = _controller;
|
||||
for (NSUInteger i = 0; i < MIN(_presentedModals.count, controllers.count); i++) {
|
||||
if (_presentedModals[i] == controllers[i]) {
|
||||
changeRootController = controllers[i];
|
||||
changeRootIndex = i + 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// we verify that controllers added on top of changeRootIndex are all new. Unfortunately modal
|
||||
// VCs cannot be reshuffled (there are some visual glitches when we try to dismiss then show as
|
||||
// even non-animated dismissal has delay and updates the screen several times)
|
||||
for (NSUInteger i = changeRootIndex; i < controllers.count; i++) {
|
||||
if ([_presentedModals containsObject:controllers[i]]) {
|
||||
RCTAssert(false, @"Modally presented controllers are being reshuffled, this is not allowed");
|
||||
}
|
||||
}
|
||||
|
||||
__weak RNSScreenStackView *weakSelf = self;
|
||||
|
||||
void (^afterTransitions)(void) = ^{
|
||||
if (weakSelf.onFinishTransitioning) {
|
||||
weakSelf.onFinishTransitioning(nil);
|
||||
}
|
||||
weakSelf.updatingModals = NO;
|
||||
if (weakSelf.scheduleModalsUpdate) {
|
||||
// if modals update was requested during setModalViewControllers we set scheduleModalsUpdate
|
||||
// flag in order to perform updates at a later point. Here we are done with all modals
|
||||
// transitions and check this flag again. If it was set, we reset the flag and execute updates.
|
||||
weakSelf.scheduleModalsUpdate = NO;
|
||||
[weakSelf updateContainer];
|
||||
}
|
||||
};
|
||||
|
||||
void (^finish)(void) = ^{
|
||||
NSUInteger oldCount = weakSelf.presentedModals.count;
|
||||
if (changeRootIndex < oldCount) {
|
||||
[weakSelf.presentedModals
|
||||
removeObjectsInRange:NSMakeRange(changeRootIndex, oldCount - changeRootIndex)];
|
||||
}
|
||||
BOOL isAttached = changeRootController.parentViewController != nil || changeRootController.presentingViewController != nil;
|
||||
if (!isAttached || changeRootIndex >= controllers.count) {
|
||||
// if change controller view is not attached, presenting modals will silently fail on iOS.
|
||||
// In such a case we trigger controllers update from didMoveToWindow.
|
||||
// We also don't run any present transitions if changeRootIndex is greater or equal to the size
|
||||
// of new controllers array. This means that no new controllers should be presented.
|
||||
afterTransitions();
|
||||
return;
|
||||
} else {
|
||||
UIViewController *previous = changeRootController;
|
||||
for (NSUInteger i = changeRootIndex; i < controllers.count; i++) {
|
||||
UIViewController *next = controllers[i];
|
||||
BOOL lastModal = (i == controllers.count - 1);
|
||||
|
||||
#ifdef __IPHONE_13_0
|
||||
if (@available(iOS 13.0, *)) {
|
||||
// Inherit UI style from its parent - solves an issue with incorrect style being applied to some UIKit views like date picker or segmented control.
|
||||
next.overrideUserInterfaceStyle = _controller.overrideUserInterfaceStyle;
|
||||
}
|
||||
#endif
|
||||
|
||||
BOOL shouldAnimate = lastModal && [next isKindOfClass:[RNSScreen class]] && ((RNSScreenView *) next.view).stackAnimation != RNSScreenStackAnimationNone;
|
||||
|
||||
[previous presentViewController:next
|
||||
animated:shouldAnimate
|
||||
completion:^{
|
||||
[weakSelf.presentedModals addObject:next];
|
||||
if (lastModal) {
|
||||
afterTransitions();
|
||||
};
|
||||
}];
|
||||
previous = next;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (changeRootController.presentedViewController != nil
|
||||
&& [_presentedModals containsObject:changeRootController.presentedViewController]) {
|
||||
|
||||
BOOL shouldAnimate = changeRootIndex == controllers.count && [changeRootController.presentedViewController isKindOfClass:[RNSScreen class]] && ((RNSScreenView *) changeRootController.presentedViewController.view).stackAnimation != RNSScreenStackAnimationNone;
|
||||
[changeRootController
|
||||
dismissViewControllerAnimated:shouldAnimate
|
||||
completion:finish];
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setPushViewControllers:(NSArray<UIViewController *> *)controllers
|
||||
{
|
||||
// when there is no change we return immediately
|
||||
if ([_controller.viewControllers isEqualToArray:controllers]) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if view controller is not yet attached to window we skip updates now and run them when view
|
||||
// is attached
|
||||
if (self.window == nil) {
|
||||
return;
|
||||
}
|
||||
// when transition is ongoing, any updates made to the controller will not be reflected until the
|
||||
// transition is complete. In particular, when we push/pop view controllers we expect viewControllers
|
||||
// property to be updated immediately. Based on that property we then calculate future updates.
|
||||
// When the transition is ongoing the property won't be updated immediatly. We therefore avoid
|
||||
// making any updated when transition is ongoing and schedule updates for when the transition
|
||||
// is complete.
|
||||
if (_controller.transitionCoordinator != nil) {
|
||||
__weak RNSScreenStackView *weakSelf = self;
|
||||
[_controller.transitionCoordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
|
||||
// do nothing here, we only want to be notified when transition is complete
|
||||
} completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
|
||||
[weakSelf updateContainer];
|
||||
}];
|
||||
return;
|
||||
}
|
||||
|
||||
UIViewController *top = controllers.lastObject;
|
||||
UIViewController *lastTop = _controller.viewControllers.lastObject;
|
||||
|
||||
// at the start we set viewControllers to contain a single UIVIewController
|
||||
// instance. This is a workaround for header height adjustment bug (see comment
|
||||
// in the init function). Here, we need to detect if the initial empty
|
||||
// controller is still there
|
||||
BOOL firstTimePush = ![lastTop isKindOfClass:[RNSScreen class]];
|
||||
|
||||
BOOL shouldAnimate = !firstTimePush && ((RNSScreenView *) lastTop.view).stackAnimation != RNSScreenStackAnimationNone;
|
||||
|
||||
if (firstTimePush) {
|
||||
// nothing pushed yet
|
||||
[_controller setViewControllers:controllers animated:NO];
|
||||
} else if (top != lastTop) {
|
||||
if (![controllers containsObject:lastTop]) {
|
||||
// if the previous top screen does not exist anymore and the new top was not on the stack before, probably replace was called, so we check the animation
|
||||
if ( ![_controller.viewControllers containsObject:top] && ((RNSScreenView *) top.view).replaceAnimation == RNSScreenReplaceAnimationPush) {
|
||||
NSMutableArray *newControllers = [NSMutableArray arrayWithArray:controllers];
|
||||
[_controller pushViewController:top animated:shouldAnimate];
|
||||
[_controller setViewControllers:newControllers animated:NO];
|
||||
} else {
|
||||
// last top controller is no longer on stack
|
||||
// in this case we set the controllers stack to the new list with
|
||||
// added the last top element to it and perform (animated) pop
|
||||
NSMutableArray *newControllers = [NSMutableArray arrayWithArray:controllers];
|
||||
[newControllers addObject:lastTop];
|
||||
[_controller setViewControllers:newControllers animated:NO];
|
||||
[_controller popViewControllerAnimated:shouldAnimate];
|
||||
}
|
||||
} else if (![_controller.viewControllers containsObject:top]) {
|
||||
// new top controller is not on the stack
|
||||
// in such case we update the stack except from the last element with
|
||||
// no animation and do animated push of the last item
|
||||
NSMutableArray *newControllers = [NSMutableArray arrayWithArray:controllers];
|
||||
[newControllers removeLastObject];
|
||||
[_controller setViewControllers:newControllers animated:NO];
|
||||
[_controller pushViewController:top animated:shouldAnimate];
|
||||
} else {
|
||||
// don't really know what this case could be, but may need to handle it
|
||||
// somehow
|
||||
[_controller setViewControllers:controllers animated:shouldAnimate];
|
||||
}
|
||||
} else {
|
||||
// change wasn't on the top of the stack. We don't need animation.
|
||||
[_controller setViewControllers:controllers animated:NO];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateContainer
|
||||
{
|
||||
NSMutableArray<UIViewController *> *pushControllers = [NSMutableArray new];
|
||||
NSMutableArray<UIViewController *> *modalControllers = [NSMutableArray new];
|
||||
for (RNSScreenView *screen in _reactSubviews) {
|
||||
if (!screen.dismissed && screen.controller != nil) {
|
||||
if (pushControllers.count == 0) {
|
||||
// first screen on the list needs to be places as "push controller"
|
||||
[pushControllers addObject:screen.controller];
|
||||
} else {
|
||||
if (screen.stackPresentation == RNSScreenStackPresentationPush) {
|
||||
[pushControllers addObject:screen.controller];
|
||||
} else {
|
||||
[modalControllers addObject:screen.controller];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[self setPushViewControllers:pushControllers];
|
||||
[self setModalViewControllers:modalControllers];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
_controller.view.frame = self.bounds;
|
||||
}
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
_invalidated = YES;
|
||||
for (UIViewController *controller in _presentedModals) {
|
||||
[controller dismissViewControllerAnimated:NO completion:nil];
|
||||
}
|
||||
[_presentedModals removeAllObjects];
|
||||
[_controller willMoveToParentViewController:nil];
|
||||
[_controller removeFromParentViewController];
|
||||
}
|
||||
|
||||
- (void)dismissOnReload
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self invalidate];
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation RNSScreenStackManager {
|
||||
NSPointerArray *_stacks;
|
||||
}
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(onFinishTransitioning, RCTDirectEventBlock);
|
||||
|
||||
- (UIView *)view
|
||||
{
|
||||
RNSScreenStackView *view = [[RNSScreenStackView alloc] initWithManager:self];
|
||||
if (!_stacks) {
|
||||
_stacks = [NSPointerArray weakObjectsPointerArray];
|
||||
}
|
||||
[_stacks addPointer:(__bridge void *)view];
|
||||
return view;
|
||||
}
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
for (RNSScreenStackView *stack in _stacks) {
|
||||
[stack dismissOnReload];
|
||||
}
|
||||
_stacks = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation RNSScreenStackAnimator {
|
||||
UINavigationControllerOperation _operation;
|
||||
}
|
||||
|
||||
- (instancetype)initWithOperation:(UINavigationControllerOperation)operation
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_operation = operation;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
|
||||
{
|
||||
RNSScreenView *screen;
|
||||
if (_operation == UINavigationControllerOperationPush) {
|
||||
UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
|
||||
screen = (RNSScreenView *)toViewController.view;
|
||||
} else if (_operation == UINavigationControllerOperationPop) {
|
||||
UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
|
||||
screen = (RNSScreenView *)fromViewController.view;
|
||||
}
|
||||
|
||||
if (screen != nil && screen.stackAnimation == RNSScreenStackAnimationNone) {
|
||||
return 0;
|
||||
}
|
||||
return 0.35; // default duration
|
||||
}
|
||||
|
||||
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
|
||||
{
|
||||
UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
|
||||
UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
|
||||
toViewController.view.frame = [transitionContext finalFrameForViewController:toViewController];
|
||||
|
||||
if (_operation == UINavigationControllerOperationPush) {
|
||||
[[transitionContext containerView] addSubview:toViewController.view];
|
||||
toViewController.view.alpha = 0.0;
|
||||
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
|
||||
toViewController.view.alpha = 1.0;
|
||||
} completion:^(BOOL finished) {
|
||||
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
|
||||
}];
|
||||
} else if (_operation == UINavigationControllerOperationPop) {
|
||||
[[transitionContext containerView] insertSubview:toViewController.view belowSubview:fromViewController.view];
|
||||
|
||||
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
|
||||
fromViewController.view.alpha = 0.0;
|
||||
} completion:^(BOOL finished) {
|
||||
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
74
node_modules/react-native-screens/ios/RNSScreenStackHeaderConfig.h
generated
vendored
Normal file
74
node_modules/react-native-screens/ios/RNSScreenStackHeaderConfig.h
generated
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
#import <React/RCTViewManager.h>
|
||||
#import <React/RCTConvert.h>
|
||||
|
||||
#import "RNSScreen.h"
|
||||
|
||||
typedef NS_ENUM(NSInteger, RNSStatusBarStyle) {
|
||||
RNSStatusBarStyleAuto,
|
||||
RNSStatusBarStyleInverted,
|
||||
RNSStatusBarStyleLight,
|
||||
RNSStatusBarStyleDark,
|
||||
};
|
||||
|
||||
@interface RNSScreenStackHeaderConfig : UIView
|
||||
|
||||
@property (nonatomic, weak) RNSScreenView *screenView;
|
||||
|
||||
@property (nonatomic, retain) NSString *title;
|
||||
@property (nonatomic, retain) NSString *titleFontFamily;
|
||||
@property (nonatomic, retain) NSNumber *titleFontSize;
|
||||
@property (nonatomic, retain) UIColor *titleColor;
|
||||
@property (nonatomic, retain) NSString *backTitle;
|
||||
@property (nonatomic, retain) NSString *backTitleFontFamily;
|
||||
@property (nonatomic, retain) NSNumber *backTitleFontSize;
|
||||
@property (nonatomic, retain) UIColor *backgroundColor;
|
||||
@property (nonatomic) UIBlurEffectStyle blurEffect;
|
||||
@property (nonatomic, retain) UIColor *color;
|
||||
@property (nonatomic) BOOL hide;
|
||||
@property (nonatomic) BOOL largeTitle;
|
||||
@property (nonatomic, retain) NSString *largeTitleFontFamily;
|
||||
@property (nonatomic, retain) NSNumber *largeTitleFontSize;
|
||||
@property (nonatomic, retain) UIColor *largeTitleBackgroundColor;
|
||||
@property (nonatomic) BOOL largeTitleHideShadow;
|
||||
@property (nonatomic, retain) UIColor *largeTitleColor;
|
||||
@property (nonatomic) BOOL hideBackButton;
|
||||
@property (nonatomic) BOOL backButtonInCustomView;
|
||||
@property (nonatomic) BOOL hideShadow;
|
||||
@property (nonatomic) BOOL translucent;
|
||||
@property (nonatomic) UISemanticContentAttribute direction;
|
||||
@property (nonatomic) RNSStatusBarStyle statusBarStyle;
|
||||
@property (nonatomic) UIStatusBarAnimation statusBarAnimation;
|
||||
@property (nonatomic) BOOL statusBarHidden;
|
||||
|
||||
+ (void)willShowViewController:(UIViewController *)vc animated:(BOOL)animated withConfig:(RNSScreenStackHeaderConfig*)config;
|
||||
+ (void)updateStatusBarAppearance;
|
||||
+ (UIStatusBarStyle)statusBarStyleForRNSStatusBarStyle:(RNSStatusBarStyle)statusBarStyle;
|
||||
|
||||
@end
|
||||
|
||||
@interface RNSScreenStackHeaderConfigManager : RCTViewManager
|
||||
|
||||
@end
|
||||
|
||||
typedef NS_ENUM(NSInteger, RNSScreenStackHeaderSubviewType) {
|
||||
RNSScreenStackHeaderSubviewTypeBackButton,
|
||||
RNSScreenStackHeaderSubviewTypeLeft,
|
||||
RNSScreenStackHeaderSubviewTypeRight,
|
||||
RNSScreenStackHeaderSubviewTypeTitle,
|
||||
RNSScreenStackHeaderSubviewTypeCenter,
|
||||
};
|
||||
|
||||
@interface RCTConvert (RNSScreenStackHeader)
|
||||
|
||||
+ (RNSScreenStackHeaderSubviewType)RNSScreenStackHeaderSubviewType:(id)json;
|
||||
+ (UIBlurEffectStyle)UIBlurEffectStyle:(id)json;
|
||||
+ (UISemanticContentAttribute)UISemanticContentAttribute:(id)json;
|
||||
+ (RNSStatusBarStyle)RNSStatusBarStyle:(id)json;
|
||||
|
||||
@end
|
||||
|
||||
@interface RNSScreenStackHeaderSubviewManager : RCTViewManager
|
||||
|
||||
@property (nonatomic) RNSScreenStackHeaderSubviewType type;
|
||||
|
||||
@end
|
664
node_modules/react-native-screens/ios/RNSScreenStackHeaderConfig.m
generated
vendored
Normal file
664
node_modules/react-native-screens/ios/RNSScreenStackHeaderConfig.m
generated
vendored
Normal file
@ -0,0 +1,664 @@
|
||||
#import "RNSScreenStackHeaderConfig.h"
|
||||
#import "RNSScreen.h"
|
||||
|
||||
#import <React/RCTBridge.h>
|
||||
#import <React/RCTUIManager.h>
|
||||
#import <React/RCTUIManagerUtils.h>
|
||||
#import <React/RCTShadowView.h>
|
||||
#import <React/RCTImageLoader.h>
|
||||
#import <React/RCTImageView.h>
|
||||
#import <React/RCTImageSource.h>
|
||||
#import <React/RCTFont.h>
|
||||
|
||||
// Some RN private method hacking below. Couldn't figure out better way to access image data
|
||||
// of a given RCTImageView. See more comments in the code section processing SubviewTypeBackButton
|
||||
@interface RCTImageView (Private)
|
||||
- (UIImage*)image;
|
||||
@end
|
||||
|
||||
@interface RCTImageLoader (Private)
|
||||
- (id<RCTImageCache>)imageCache;
|
||||
@end
|
||||
|
||||
|
||||
@interface RNSScreenStackHeaderSubview : UIView
|
||||
|
||||
@property (nonatomic, weak) RCTBridge *bridge;
|
||||
@property (nonatomic, weak) UIView *reactSuperview;
|
||||
@property (nonatomic) RNSScreenStackHeaderSubviewType type;
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge*)bridge;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RNSScreenStackHeaderSubview
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_bridge = bridge;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation RNSScreenStackHeaderConfig {
|
||||
NSMutableArray<RNSScreenStackHeaderSubview *> *_reactSubviews;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
self.hidden = YES;
|
||||
_translucent = YES;
|
||||
_reactSubviews = [NSMutableArray new];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)insertReactSubview:(RNSScreenStackHeaderSubview *)subview atIndex:(NSInteger)atIndex
|
||||
{
|
||||
[_reactSubviews insertObject:subview atIndex:atIndex];
|
||||
subview.reactSuperview = self;
|
||||
}
|
||||
|
||||
- (void)removeReactSubview:(RNSScreenStackHeaderSubview *)subview
|
||||
{
|
||||
[_reactSubviews removeObject:subview];
|
||||
}
|
||||
|
||||
- (NSArray<UIView *> *)reactSubviews
|
||||
{
|
||||
return _reactSubviews;
|
||||
}
|
||||
|
||||
- (UIView *)reactSuperview
|
||||
{
|
||||
return _screenView;
|
||||
}
|
||||
|
||||
- (void)removeFromSuperview
|
||||
{
|
||||
[super removeFromSuperview];
|
||||
_screenView = nil;
|
||||
}
|
||||
|
||||
- (void)updateViewControllerIfNeeded
|
||||
{
|
||||
UIViewController *vc = _screenView.controller;
|
||||
UINavigationController *nav = (UINavigationController*) vc.parentViewController;
|
||||
UIViewController *nextVC = nav.visibleViewController;
|
||||
if (nav.transitionCoordinator != nil) {
|
||||
// if navigator is performing transition instead of allowing to update of `visibleConttroller`
|
||||
// we look at `topController`. This is because during transitiong the `visibleController` won't
|
||||
// point to the controller that is going to be revealed after transition. This check fixes the
|
||||
// problem when config gets updated while the transition is ongoing.
|
||||
nextVC = nav.topViewController;
|
||||
}
|
||||
|
||||
if (vc != nil && nextVC == vc) {
|
||||
[RNSScreenStackHeaderConfig updateViewController:self.screenView.controller
|
||||
withConfig:self
|
||||
animated:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)didSetProps:(NSArray<NSString *> *)changedProps
|
||||
{
|
||||
[super didSetProps:changedProps];
|
||||
[self updateViewControllerIfNeeded];
|
||||
}
|
||||
|
||||
- (void)didUpdateReactSubviews
|
||||
{
|
||||
[super didUpdateReactSubviews];
|
||||
[self updateViewControllerIfNeeded];
|
||||
}
|
||||
|
||||
+ (void)setAnimatedConfig:(UIViewController *)vc withConfig:(RNSScreenStackHeaderConfig *)config
|
||||
{
|
||||
UINavigationBar *navbar = ((UINavigationController *)vc.parentViewController).navigationBar;
|
||||
// It is workaround for loading custom back icon when transitioning from a screen without header to the screen which has one.
|
||||
// This action fails when navigating to the screen with header for the second time and loads default back button.
|
||||
// It looks like changing the tint color of navbar triggers an update of the items belonging to it and it seems to load the custom back image
|
||||
// so we change the tint color's alpha by a very small amount and then set it to the one it should have.
|
||||
[navbar setTintColor:[config.color colorWithAlphaComponent:CGColorGetAlpha(config.color.CGColor) - 0.01]];
|
||||
[navbar setTintColor:config.color];
|
||||
|
||||
#if defined(__IPHONE_13_0) && TARGET_OS_IOS
|
||||
if (@available(iOS 13.0, *)) {
|
||||
// font customized on the navigation item level, so nothing to do here
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
BOOL hideShadow = config.hideShadow;
|
||||
|
||||
if (config.backgroundColor && CGColorGetAlpha(config.backgroundColor.CGColor) == 0.) {
|
||||
[navbar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
|
||||
[navbar setBarTintColor:[UIColor clearColor]];
|
||||
hideShadow = YES;
|
||||
} else {
|
||||
[navbar setBackgroundImage:nil forBarMetrics:UIBarMetricsDefault];
|
||||
[navbar setBarTintColor:config.backgroundColor];
|
||||
}
|
||||
[navbar setTranslucent:config.translucent];
|
||||
[navbar setValue:@(hideShadow ? YES : NO) forKey:@"hidesShadow"];
|
||||
|
||||
if (config.titleFontFamily || config.titleFontSize || config.titleColor) {
|
||||
NSMutableDictionary *attrs = [NSMutableDictionary new];
|
||||
|
||||
if (config.titleColor) {
|
||||
attrs[NSForegroundColorAttributeName] = config.titleColor;
|
||||
}
|
||||
|
||||
NSNumber *size = config.titleFontSize ?: @17;
|
||||
if (config.titleFontFamily) {
|
||||
attrs[NSFontAttributeName] = [RCTFont updateFont:nil withFamily:config.titleFontFamily size:size weight:nil style:nil variant:nil scaleMultiplier:1.0];
|
||||
} else {
|
||||
attrs[NSFontAttributeName] = [UIFont boldSystemFontOfSize:[size floatValue]];
|
||||
}
|
||||
[navbar setTitleTextAttributes:attrs];
|
||||
}
|
||||
|
||||
#if (TARGET_OS_IOS)
|
||||
if (@available(iOS 11.0, *)) {
|
||||
if (config.largeTitle && (config.largeTitleFontFamily || config.largeTitleFontSize || config.largeTitleColor || config.titleColor)) {
|
||||
NSMutableDictionary *largeAttrs = [NSMutableDictionary new];
|
||||
if (config.largeTitleColor || config.titleColor) {
|
||||
largeAttrs[NSForegroundColorAttributeName] = config.largeTitleColor ? config.largeTitleColor : config.titleColor;
|
||||
}
|
||||
NSNumber *largeSize = config.largeTitleFontSize ?: @34;
|
||||
if (config.largeTitleFontFamily) {
|
||||
largeAttrs[NSFontAttributeName] = [RCTFont updateFont:nil withFamily:config.largeTitleFontFamily size:largeSize weight:nil style:nil variant:nil scaleMultiplier:1.0];
|
||||
} else {
|
||||
largeAttrs[NSFontAttributeName] = [UIFont systemFontOfSize:[largeSize floatValue] weight:UIFontWeightBold];
|
||||
}
|
||||
[navbar setLargeTitleTextAttributes:largeAttrs];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)setTitleAttibutes:(NSDictionary *)attrs forButton:(UIBarButtonItem *)button
|
||||
{
|
||||
[button setTitleTextAttributes:attrs forState:UIControlStateNormal];
|
||||
[button setTitleTextAttributes:attrs forState:UIControlStateHighlighted];
|
||||
[button setTitleTextAttributes:attrs forState:UIControlStateDisabled];
|
||||
[button setTitleTextAttributes:attrs forState:UIControlStateSelected];
|
||||
if (@available(iOS 9.0, *)) {
|
||||
[button setTitleTextAttributes:attrs forState:UIControlStateFocused];
|
||||
}
|
||||
}
|
||||
|
||||
+ (UIImage*)loadBackButtonImageInViewController:(UIViewController *)vc
|
||||
withConfig:(RNSScreenStackHeaderConfig *)config
|
||||
{
|
||||
BOOL hasBackButtonImage = NO;
|
||||
for (RNSScreenStackHeaderSubview *subview in config.reactSubviews) {
|
||||
if (subview.type == RNSScreenStackHeaderSubviewTypeBackButton && subview.subviews.count > 0) {
|
||||
hasBackButtonImage = YES;
|
||||
RCTImageView *imageView = subview.subviews[0];
|
||||
if (imageView.image == nil) {
|
||||
// This is yet another workaround for loading custom back icon. It turns out that under
|
||||
// certain circumstances image attribute can be null despite the app running in production
|
||||
// mode (when images are loaded from the filesystem). This can happen because image attribute
|
||||
// is reset when image view is detached from window, and also in some cases initialization
|
||||
// does not populate the frame of the image view before the loading start. The latter result
|
||||
// in the image attribute not being updated. We manually set frame to the size of an image
|
||||
// in order to trigger proper reload that'd update the image attribute.
|
||||
RCTImageSource *source = imageView.imageSources[0];
|
||||
[imageView reactSetFrame:CGRectMake(imageView.frame.origin.x,
|
||||
imageView.frame.origin.y,
|
||||
source.size.width,
|
||||
source.size.height)];
|
||||
}
|
||||
UIImage *image = imageView.image;
|
||||
// IMPORTANT!!!
|
||||
// image can be nil in DEV MODE ONLY
|
||||
//
|
||||
// It is so, because in dev mode images are loaded over HTTP from the packager. In that case
|
||||
// we first check if image is already loaded in cache and if it is, we take it from cache and
|
||||
// display immediately. Otherwise we wait for the transition to finish and retry updating
|
||||
// header config.
|
||||
// Unfortunately due to some problems in UIKit we cannot update the image while the screen
|
||||
// transition is ongoing. This results in the settings being reset after the transition is done
|
||||
// to the state from before the transition.
|
||||
if (image == nil) {
|
||||
// in DEV MODE we try to load from cache (we use private API for that as it is not exposed
|
||||
// publically in headers).
|
||||
RCTImageSource *source = imageView.imageSources[0];
|
||||
image = [subview.bridge.imageLoader.imageCache
|
||||
imageForUrl:source.request.URL.absoluteString
|
||||
size:source.size
|
||||
scale:source.scale
|
||||
resizeMode:imageView.resizeMode];
|
||||
}
|
||||
if (image == nil) {
|
||||
// This will be triggered if the image is not in the cache yet. What we do is we wait until
|
||||
// the end of transition and run header config updates again. We could potentially wait for
|
||||
// image on load to trigger, but that would require even more private method hacking.
|
||||
if (vc.transitionCoordinator) {
|
||||
[vc.transitionCoordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
|
||||
// nothing, we just want completion
|
||||
} completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
|
||||
// in order for new back button image to be loaded we need to trigger another change
|
||||
// in back button props that'd make UIKit redraw the button. Otherwise the changes are
|
||||
// not reflected. Here we change back button visibility which is then immediately restored
|
||||
#if (TARGET_OS_IOS)
|
||||
vc.navigationItem.hidesBackButton = YES;
|
||||
#endif
|
||||
[config updateViewControllerIfNeeded];
|
||||
}];
|
||||
}
|
||||
return [UIImage new];
|
||||
} else {
|
||||
return image;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (void)willShowViewController:(UIViewController *)vc animated:(BOOL)animated withConfig:(RNSScreenStackHeaderConfig *)config
|
||||
{
|
||||
[self updateViewController:vc withConfig:config animated:animated];
|
||||
}
|
||||
|
||||
#if defined(__IPHONE_13_0) && TARGET_OS_IOS
|
||||
+ (UINavigationBarAppearance*)buildAppearance:(UIViewController *)vc withConfig:(RNSScreenStackHeaderConfig *)config
|
||||
{
|
||||
UINavigationBarAppearance *appearance = [UINavigationBarAppearance new];
|
||||
|
||||
if (config.backgroundColor && CGColorGetAlpha(config.backgroundColor.CGColor) == 0.) {
|
||||
// transparent background color
|
||||
[appearance configureWithTransparentBackground];
|
||||
} else {
|
||||
[appearance configureWithOpaqueBackground];
|
||||
}
|
||||
|
||||
// set background color if specified
|
||||
if (config.backgroundColor) {
|
||||
appearance.backgroundColor = config.backgroundColor;
|
||||
}
|
||||
|
||||
if (config.blurEffect) {
|
||||
appearance.backgroundEffect = [UIBlurEffect effectWithStyle:config.blurEffect];
|
||||
}
|
||||
|
||||
if (config.hideShadow) {
|
||||
appearance.shadowColor = nil;
|
||||
}
|
||||
|
||||
if (config.titleFontFamily || config.titleFontSize || config.titleColor) {
|
||||
NSMutableDictionary *attrs = [NSMutableDictionary new];
|
||||
|
||||
if (config.titleColor) {
|
||||
attrs[NSForegroundColorAttributeName] = config.titleColor;
|
||||
}
|
||||
|
||||
NSNumber *size = config.titleFontSize ?: @17;
|
||||
if (config.titleFontFamily) {
|
||||
attrs[NSFontAttributeName] = [RCTFont updateFont:nil withFamily:config.titleFontFamily size:size weight:nil style:nil variant:nil scaleMultiplier:1.0];
|
||||
} else {
|
||||
attrs[NSFontAttributeName] = [UIFont boldSystemFontOfSize:[size floatValue]];
|
||||
}
|
||||
appearance.titleTextAttributes = attrs;
|
||||
}
|
||||
|
||||
if (config.largeTitleFontFamily || config.largeTitleFontSize || config.largeTitleColor || config.titleColor) {
|
||||
NSMutableDictionary *largeAttrs = [NSMutableDictionary new];
|
||||
|
||||
if (config.largeTitleColor || config.titleColor) {
|
||||
largeAttrs[NSForegroundColorAttributeName] = config.largeTitleColor ? config.largeTitleColor : config.titleColor;
|
||||
}
|
||||
|
||||
NSNumber *largeSize = config.largeTitleFontSize ?: @34;
|
||||
if (config.largeTitleFontFamily) {
|
||||
largeAttrs[NSFontAttributeName] = [RCTFont updateFont:nil withFamily:config.largeTitleFontFamily size:largeSize weight:nil style:nil variant:nil scaleMultiplier:1.0];
|
||||
} else {
|
||||
largeAttrs[NSFontAttributeName] = [UIFont systemFontOfSize:[largeSize floatValue] weight:UIFontWeightBold];
|
||||
}
|
||||
|
||||
appearance.largeTitleTextAttributes = largeAttrs;
|
||||
}
|
||||
|
||||
UIImage *backButtonImage = [self loadBackButtonImageInViewController:vc withConfig:config];
|
||||
if (backButtonImage) {
|
||||
[appearance setBackIndicatorImage:backButtonImage transitionMaskImage:backButtonImage];
|
||||
} else if (appearance.backIndicatorImage) {
|
||||
[appearance setBackIndicatorImage:nil transitionMaskImage:nil];
|
||||
}
|
||||
return appearance;
|
||||
}
|
||||
#endif
|
||||
|
||||
+ (void)updateViewController:(UIViewController *)vc withConfig:(RNSScreenStackHeaderConfig *)config animated:(BOOL)animated
|
||||
{
|
||||
UINavigationItem *navitem = vc.navigationItem;
|
||||
UINavigationController *navctr = (UINavigationController *)vc.parentViewController;
|
||||
|
||||
NSUInteger currentIndex = [navctr.viewControllers indexOfObject:vc];
|
||||
UINavigationItem *prevItem = currentIndex > 0 ? [navctr.viewControllers objectAtIndex:currentIndex - 1].navigationItem : nil;
|
||||
|
||||
BOOL wasHidden = navctr.navigationBarHidden;
|
||||
BOOL shouldHide = config == nil || config.hide;
|
||||
|
||||
if (!shouldHide && !config.translucent) {
|
||||
// when nav bar is not translucent we chage edgesForExtendedLayout to avoid system laying out
|
||||
// the screen underneath navigation controllers
|
||||
vc.edgesForExtendedLayout = UIRectEdgeNone;
|
||||
} else {
|
||||
// system default is UIRectEdgeAll
|
||||
vc.edgesForExtendedLayout = UIRectEdgeAll;
|
||||
}
|
||||
|
||||
[navctr setNavigationBarHidden:shouldHide animated:animated];
|
||||
|
||||
#if (TARGET_OS_IOS)
|
||||
// we put it before check with return because we want to apply changes to status bar even if the header is hidden
|
||||
if (config != nil) {
|
||||
if (config.statusBarStyle || config.statusBarAnimation || config.statusBarHidden) {
|
||||
[RNSScreenStackHeaderConfig assertViewControllerBasedStatusBarAppearenceSet];
|
||||
if ([vc isKindOfClass:[RNSScreen class]]) {
|
||||
[RNSScreenStackHeaderConfig updateStatusBarAppearance];
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (shouldHide) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.direction == UISemanticContentAttributeForceLeftToRight || config.direction == UISemanticContentAttributeForceRightToLeft) {
|
||||
navctr.view.semanticContentAttribute = config.direction;
|
||||
navctr.navigationBar.semanticContentAttribute = config.direction;
|
||||
}
|
||||
|
||||
navitem.title = config.title;
|
||||
#if (TARGET_OS_IOS)
|
||||
if (config.backTitle != nil || config.backTitleFontFamily || config.backTitleFontSize) {
|
||||
prevItem.backBarButtonItem = [[UIBarButtonItem alloc]
|
||||
initWithTitle:config.backTitle ?: prevItem.title
|
||||
style:UIBarButtonItemStylePlain
|
||||
target:nil
|
||||
action:nil];
|
||||
if (config.backTitleFontFamily || config.backTitleFontSize) {
|
||||
NSMutableDictionary *attrs = [NSMutableDictionary new];
|
||||
NSNumber *size = config.backTitleFontSize ?: @17;
|
||||
if (config.backTitleFontFamily) {
|
||||
attrs[NSFontAttributeName] = [RCTFont updateFont:nil withFamily:config.backTitleFontFamily size:size weight:nil style:nil variant:nil scaleMultiplier:1.0];
|
||||
} else {
|
||||
attrs[NSFontAttributeName] = [UIFont boldSystemFontOfSize:[size floatValue]];
|
||||
}
|
||||
[self setTitleAttibutes:attrs forButton:prevItem.backBarButtonItem];
|
||||
}
|
||||
} else {
|
||||
prevItem.backBarButtonItem = nil;
|
||||
}
|
||||
|
||||
if (@available(iOS 11.0, *)) {
|
||||
if (config.largeTitle) {
|
||||
navctr.navigationBar.prefersLargeTitles = YES;
|
||||
}
|
||||
navitem.largeTitleDisplayMode = config.largeTitle ? UINavigationItemLargeTitleDisplayModeAlways : UINavigationItemLargeTitleDisplayModeNever;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__IPHONE_13_0) && TARGET_OS_IOS
|
||||
if (@available(iOS 13.0, *)) {
|
||||
UINavigationBarAppearance *appearance = [self buildAppearance:vc withConfig:config];
|
||||
navitem.standardAppearance = appearance;
|
||||
navitem.compactAppearance = appearance;
|
||||
|
||||
UINavigationBarAppearance *scrollEdgeAppearance = [[UINavigationBarAppearance alloc] initWithBarAppearance:appearance];
|
||||
if (config.largeTitleBackgroundColor != nil) {
|
||||
scrollEdgeAppearance.backgroundColor = config.largeTitleBackgroundColor;
|
||||
}
|
||||
if (config.largeTitleHideShadow) {
|
||||
scrollEdgeAppearance.shadowColor = nil;
|
||||
}
|
||||
navitem.scrollEdgeAppearance = scrollEdgeAppearance;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
#if (TARGET_OS_IOS)
|
||||
// updating backIndicatotImage does not work when called during transition. On iOS pre 13 we need
|
||||
// to update it before the navigation starts.
|
||||
UIImage *backButtonImage = [self loadBackButtonImageInViewController:vc withConfig:config];
|
||||
if (backButtonImage) {
|
||||
navctr.navigationBar.backIndicatorImage = backButtonImage;
|
||||
navctr.navigationBar.backIndicatorTransitionMaskImage = backButtonImage;
|
||||
} else if (navctr.navigationBar.backIndicatorImage) {
|
||||
navctr.navigationBar.backIndicatorImage = nil;
|
||||
navctr.navigationBar.backIndicatorTransitionMaskImage = nil;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#if (TARGET_OS_IOS)
|
||||
navitem.hidesBackButton = config.hideBackButton;
|
||||
#endif
|
||||
navitem.leftBarButtonItem = nil;
|
||||
navitem.rightBarButtonItem = nil;
|
||||
navitem.titleView = nil;
|
||||
for (RNSScreenStackHeaderSubview *subview in config.reactSubviews) {
|
||||
switch (subview.type) {
|
||||
case RNSScreenStackHeaderSubviewTypeLeft: {
|
||||
#if (TARGET_OS_IOS)
|
||||
navitem.leftItemsSupplementBackButton = config.backButtonInCustomView;
|
||||
#endif
|
||||
UIBarButtonItem *buttonItem = [[UIBarButtonItem alloc] initWithCustomView:subview];
|
||||
navitem.leftBarButtonItem = buttonItem;
|
||||
break;
|
||||
}
|
||||
case RNSScreenStackHeaderSubviewTypeRight: {
|
||||
UIBarButtonItem *buttonItem = [[UIBarButtonItem alloc] initWithCustomView:subview];
|
||||
navitem.rightBarButtonItem = buttonItem;
|
||||
break;
|
||||
}
|
||||
case RNSScreenStackHeaderSubviewTypeCenter:
|
||||
case RNSScreenStackHeaderSubviewTypeTitle: {
|
||||
navitem.titleView = subview;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (animated
|
||||
&& vc.transitionCoordinator != nil
|
||||
&& vc.transitionCoordinator.presentationStyle == UIModalPresentationNone
|
||||
&& !wasHidden) {
|
||||
// when there is an ongoing transition we may need to update navbar setting in animation block
|
||||
// using animateAlongsideTransition. However, we only do that given the transition is not a modal
|
||||
// transition (presentationStyle == UIModalPresentationNone) and that the bar was not previously
|
||||
// hidden. This is because both for modal transitions and transitions from screen with hidden bar
|
||||
// the transition animation block does not get triggered. This is ok, because with both of those
|
||||
// types of transitions there is no "shared" navigation bar that needs to be updated in an animated
|
||||
// way.
|
||||
[vc.transitionCoordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
|
||||
[self setAnimatedConfig:vc withConfig:config];
|
||||
} completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
|
||||
if ([context isCancelled]) {
|
||||
UIViewController* fromVC = [context viewControllerForKey:UITransitionContextFromViewControllerKey];
|
||||
RNSScreenStackHeaderConfig* config = nil;
|
||||
for (UIView *subview in fromVC.view.reactSubviews) {
|
||||
if ([subview isKindOfClass:[RNSScreenStackHeaderConfig class]]) {
|
||||
config = (RNSScreenStackHeaderConfig*) subview;
|
||||
break;
|
||||
}
|
||||
}
|
||||
[self setAnimatedConfig:fromVC withConfig:config];
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
[self setAnimatedConfig:vc withConfig:config];
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)assertViewControllerBasedStatusBarAppearenceSet
|
||||
{
|
||||
static dispatch_once_t once;
|
||||
static bool viewControllerBasedAppearence;
|
||||
dispatch_once(&once, ^{
|
||||
viewControllerBasedAppearence = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIViewControllerBasedStatusBarAppearance"] boolValue];
|
||||
});
|
||||
if (!viewControllerBasedAppearence) {
|
||||
RCTLogError(@"If you want to change the appearance of status bar, you have to change \
|
||||
UIViewControllerBasedStatusBarAppearance key in the Info.plist to YES");
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)updateStatusBarAppearance
|
||||
{
|
||||
[UIView animateWithDuration:0.4 animations:^{ // duration based on "Programming iOS 13" p. 311 implementation
|
||||
if (@available(iOS 13, *)) {
|
||||
UIWindow *firstWindow = [[[UIApplication sharedApplication] windows] firstObject];
|
||||
if (firstWindow != nil) {
|
||||
[[firstWindow rootViewController] setNeedsStatusBarAppearanceUpdate];
|
||||
}
|
||||
} else {
|
||||
[UIApplication.sharedApplication.keyWindow.rootViewController setNeedsStatusBarAppearanceUpdate];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
+ (UIStatusBarStyle)statusBarStyleForRNSStatusBarStyle:(RNSStatusBarStyle)statusBarStyle
|
||||
{
|
||||
#ifdef __IPHONE_13_0
|
||||
if (@available(iOS 13.0, *)) {
|
||||
switch (statusBarStyle) {
|
||||
case RNSStatusBarStyleAuto:
|
||||
return [UITraitCollection.currentTraitCollection userInterfaceStyle] == UIUserInterfaceStyleDark ? UIStatusBarStyleLightContent : UIStatusBarStyleDarkContent;
|
||||
case RNSStatusBarStyleInverted:
|
||||
return [UITraitCollection.currentTraitCollection userInterfaceStyle] == UIUserInterfaceStyleDark ? UIStatusBarStyleDarkContent : UIStatusBarStyleLightContent;
|
||||
case RNSStatusBarStyleLight:
|
||||
return UIStatusBarStyleLightContent;
|
||||
case RNSStatusBarStyleDark:
|
||||
return UIStatusBarStyleDarkContent;
|
||||
default:
|
||||
return UIStatusBarStyleLightContent;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return UIStatusBarStyleLightContent;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation RNSScreenStackHeaderConfigManager
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (UIView *)view
|
||||
{
|
||||
return [RNSScreenStackHeaderConfig new];
|
||||
}
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(title, NSString)
|
||||
RCT_EXPORT_VIEW_PROPERTY(titleFontFamily, NSString)
|
||||
RCT_EXPORT_VIEW_PROPERTY(titleFontSize, NSNumber)
|
||||
RCT_EXPORT_VIEW_PROPERTY(titleColor, UIColor)
|
||||
RCT_EXPORT_VIEW_PROPERTY(backTitle, NSString)
|
||||
RCT_EXPORT_VIEW_PROPERTY(backTitleFontFamily, NSString)
|
||||
RCT_EXPORT_VIEW_PROPERTY(backTitleFontSize, NSNumber)
|
||||
RCT_EXPORT_VIEW_PROPERTY(backgroundColor, UIColor)
|
||||
RCT_EXPORT_VIEW_PROPERTY(blurEffect, UIBlurEffectStyle)
|
||||
RCT_EXPORT_VIEW_PROPERTY(color, UIColor)
|
||||
RCT_EXPORT_VIEW_PROPERTY(direction, UISemanticContentAttribute)
|
||||
RCT_EXPORT_VIEW_PROPERTY(largeTitle, BOOL)
|
||||
RCT_EXPORT_VIEW_PROPERTY(largeTitleFontFamily, NSString)
|
||||
RCT_EXPORT_VIEW_PROPERTY(largeTitleFontSize, NSNumber)
|
||||
RCT_EXPORT_VIEW_PROPERTY(largeTitleColor, UIColor)
|
||||
RCT_EXPORT_VIEW_PROPERTY(largeTitleBackgroundColor, UIColor)
|
||||
RCT_EXPORT_VIEW_PROPERTY(largeTitleHideShadow, BOOL)
|
||||
RCT_EXPORT_VIEW_PROPERTY(hideBackButton, BOOL)
|
||||
RCT_EXPORT_VIEW_PROPERTY(hideShadow, BOOL)
|
||||
RCT_EXPORT_VIEW_PROPERTY(backButtonInCustomView, BOOL)
|
||||
// `hidden` is an UIView property, we need to use different name internally
|
||||
RCT_REMAP_VIEW_PROPERTY(hidden, hide, BOOL)
|
||||
RCT_EXPORT_VIEW_PROPERTY(translucent, BOOL)
|
||||
RCT_EXPORT_VIEW_PROPERTY(statusBarStyle, RNSStatusBarStyle)
|
||||
RCT_EXPORT_VIEW_PROPERTY(statusBarAnimation, UIStatusBarAnimation)
|
||||
RCT_EXPORT_VIEW_PROPERTY(statusBarHidden, BOOL)
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTConvert (RNSScreenStackHeader)
|
||||
|
||||
+ (NSMutableDictionary *)blurEffectsForIOSVersion
|
||||
{
|
||||
NSMutableDictionary *blurEffects = [NSMutableDictionary new];
|
||||
[blurEffects addEntriesFromDictionary:@{
|
||||
@"extraLight": @(UIBlurEffectStyleExtraLight),
|
||||
@"light": @(UIBlurEffectStyleLight),
|
||||
@"dark": @(UIBlurEffectStyleDark),
|
||||
}];
|
||||
|
||||
if (@available(iOS 10.0, *)) {
|
||||
[blurEffects addEntriesFromDictionary:@{
|
||||
@"regular": @(UIBlurEffectStyleRegular),
|
||||
@"prominent": @(UIBlurEffectStyleProminent),
|
||||
}];
|
||||
}
|
||||
#if defined(__IPHONE_13_0) && TARGET_OS_IOS
|
||||
if (@available(iOS 13.0, *)) {
|
||||
[blurEffects addEntriesFromDictionary:@{
|
||||
@"systemUltraThinMaterial": @(UIBlurEffectStyleSystemUltraThinMaterial),
|
||||
@"systemThinMaterial": @(UIBlurEffectStyleSystemThinMaterial),
|
||||
@"systemMaterial": @(UIBlurEffectStyleSystemMaterial),
|
||||
@"systemThickMaterial": @(UIBlurEffectStyleSystemThickMaterial),
|
||||
@"systemChromeMaterial": @(UIBlurEffectStyleSystemChromeMaterial),
|
||||
@"systemUltraThinMaterialLight": @(UIBlurEffectStyleSystemUltraThinMaterialLight),
|
||||
@"systemThinMaterialLight": @(UIBlurEffectStyleSystemThinMaterialLight),
|
||||
@"systemMaterialLight": @(UIBlurEffectStyleSystemMaterialLight),
|
||||
@"systemThickMaterialLight": @(UIBlurEffectStyleSystemThickMaterialLight),
|
||||
@"systemChromeMaterialLight": @(UIBlurEffectStyleSystemChromeMaterialLight),
|
||||
@"systemUltraThinMaterialDark": @(UIBlurEffectStyleSystemUltraThinMaterialDark),
|
||||
@"systemThinMaterialDark": @(UIBlurEffectStyleSystemThinMaterialDark),
|
||||
@"systemMaterialDark": @(UIBlurEffectStyleSystemMaterialDark),
|
||||
@"systemThickMaterialDark": @(UIBlurEffectStyleSystemThickMaterialDark),
|
||||
@"systemChromeMaterialDark": @(UIBlurEffectStyleSystemChromeMaterialDark),
|
||||
}];
|
||||
}
|
||||
#endif
|
||||
return blurEffects;
|
||||
}
|
||||
|
||||
RCT_ENUM_CONVERTER(RNSScreenStackHeaderSubviewType, (@{
|
||||
@"back": @(RNSScreenStackHeaderSubviewTypeBackButton),
|
||||
@"left": @(RNSScreenStackHeaderSubviewTypeLeft),
|
||||
@"right": @(RNSScreenStackHeaderSubviewTypeRight),
|
||||
@"title": @(RNSScreenStackHeaderSubviewTypeTitle),
|
||||
@"center": @(RNSScreenStackHeaderSubviewTypeCenter),
|
||||
}), RNSScreenStackHeaderSubviewTypeTitle, integerValue)
|
||||
|
||||
RCT_ENUM_CONVERTER(UISemanticContentAttribute, (@{
|
||||
@"ltr": @(UISemanticContentAttributeForceLeftToRight),
|
||||
@"rtl": @(UISemanticContentAttributeForceRightToLeft),
|
||||
}), UISemanticContentAttributeUnspecified, integerValue)
|
||||
|
||||
RCT_ENUM_CONVERTER(UIBlurEffectStyle, ([self blurEffectsForIOSVersion]), UIBlurEffectStyleExtraLight, integerValue)
|
||||
|
||||
RCT_ENUM_CONVERTER(RNSStatusBarStyle, (@{
|
||||
@"auto": @(RNSStatusBarStyleAuto),
|
||||
@"inverted": @(RNSStatusBarStyleInverted),
|
||||
@"light": @(RNSStatusBarStyleLight),
|
||||
@"dark": @(RNSStatusBarStyleDark),
|
||||
}), RNSStatusBarStyleAuto, integerValue)
|
||||
|
||||
@end
|
||||
|
||||
@implementation RNSScreenStackHeaderSubviewManager
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(type, RNSScreenStackHeaderSubviewType)
|
||||
|
||||
- (UIView *)view
|
||||
{
|
||||
return [[RNSScreenStackHeaderSubview alloc] initWithBridge:self.bridge];
|
||||
}
|
||||
|
||||
@end
|
457
node_modules/react-native-screens/ios/RNScreens.xcodeproj/project.pbxproj
generated
vendored
Normal file
457
node_modules/react-native-screens/ios/RNScreens.xcodeproj/project.pbxproj
generated
vendored
Normal file
@ -0,0 +1,457 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
442389EC22DF259000611BBE /* RNSScreen.m in Sources */ = {isa = PBXBuildFile; fileRef = 442389EB22DF259000611BBE /* RNSScreen.m */; };
|
||||
448078F52114595900280661 /* RNSScreenContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 448078F12114595900280661 /* RNSScreenContainer.m */; };
|
||||
4482D5EF22CB391800D5A5B9 /* RNSScreenStackHeaderConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 4482D5EE22CB391800D5A5B9 /* RNSScreenStackHeaderConfig.m */; };
|
||||
44A67C3122C3B8B40017156F /* RNSScreenStack.m in Sources */ = {isa = PBXBuildFile; fileRef = 44A67C3022C3B8B40017156F /* RNSScreenStack.m */; };
|
||||
B5C32A48220C6379000FFB8D /* RNSScreenContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 448078F12114595900280661 /* RNSScreenContainer.m */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
58B511D91A9E6C8500147676 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "include/$(PRODUCT_NAME)";
|
||||
dstSubfolderSpec = 16;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
B5C32A4A220C6379000FFB8D /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "include/$(PRODUCT_NAME)";
|
||||
dstSubfolderSpec = 16;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
134814201AA4EA6300B7C361 /* libRNScreens.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNScreens.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
442389EB22DF259000611BBE /* RNSScreen.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNSScreen.m; sourceTree = "<group>"; };
|
||||
448078EF2114595900280661 /* RNSScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSScreen.h; sourceTree = "<group>"; };
|
||||
448078F02114595900280661 /* RNSScreenContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSScreenContainer.h; sourceTree = "<group>"; };
|
||||
448078F12114595900280661 /* RNSScreenContainer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSScreenContainer.m; sourceTree = "<group>"; };
|
||||
4482D5ED22CB391800D5A5B9 /* RNSScreenStackHeaderConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNSScreenStackHeaderConfig.h; sourceTree = "<group>"; };
|
||||
4482D5EE22CB391800D5A5B9 /* RNSScreenStackHeaderConfig.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNSScreenStackHeaderConfig.m; sourceTree = "<group>"; };
|
||||
44A67C2F22C3B8B40017156F /* RNSScreenStack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSScreenStack.h; sourceTree = "<group>"; };
|
||||
44A67C3022C3B8B40017156F /* RNSScreenStack.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSScreenStack.m; sourceTree = "<group>"; };
|
||||
B5C32A4F220C6379000FFB8D /* libRNScreens-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libRNScreens-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
58B511D81A9E6C8500147676 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
B5C32A49220C6379000FFB8D /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
134814211AA4EA7D00B7C361 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
134814201AA4EA6300B7C361 /* libRNScreens.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
58B511D21A9E6C8500147676 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4482D5ED22CB391800D5A5B9 /* RNSScreenStackHeaderConfig.h */,
|
||||
4482D5EE22CB391800D5A5B9 /* RNSScreenStackHeaderConfig.m */,
|
||||
44A67C2F22C3B8B40017156F /* RNSScreenStack.h */,
|
||||
44A67C3022C3B8B40017156F /* RNSScreenStack.m */,
|
||||
448078EF2114595900280661 /* RNSScreen.h */,
|
||||
442389EB22DF259000611BBE /* RNSScreen.m */,
|
||||
448078F02114595900280661 /* RNSScreenContainer.h */,
|
||||
448078F12114595900280661 /* RNSScreenContainer.m */,
|
||||
134814211AA4EA7D00B7C361 /* Products */,
|
||||
B5C32A4F220C6379000FFB8D /* libRNScreens-tvOS.a */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
58B511DA1A9E6C8500147676 /* RNScreens */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNScreens" */;
|
||||
buildPhases = (
|
||||
58B511D71A9E6C8500147676 /* Sources */,
|
||||
58B511D81A9E6C8500147676 /* Frameworks */,
|
||||
58B511D91A9E6C8500147676 /* CopyFiles */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = RNScreens;
|
||||
productName = RCTDataManager;
|
||||
productReference = 134814201AA4EA6300B7C361 /* libRNScreens.a */;
|
||||
productType = "com.apple.product-type.library.static";
|
||||
};
|
||||
B5C32A45220C6379000FFB8D /* RNScreens-tvOS */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = B5C32A4B220C6379000FFB8D /* Build configuration list for PBXNativeTarget "RNScreens-tvOS" */;
|
||||
buildPhases = (
|
||||
B5C32A46220C6379000FFB8D /* Sources */,
|
||||
B5C32A49220C6379000FFB8D /* Frameworks */,
|
||||
B5C32A4A220C6379000FFB8D /* CopyFiles */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = "RNScreens-tvOS";
|
||||
productName = RCTDataManager;
|
||||
productReference = B5C32A4F220C6379000FFB8D /* libRNScreens-tvOS.a */;
|
||||
productType = "com.apple.product-type.library.static";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
58B511D31A9E6C8500147676 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 920;
|
||||
ORGANIZATIONNAME = Facebook;
|
||||
TargetAttributes = {
|
||||
58B511DA1A9E6C8500147676 = {
|
||||
CreatedOnToolsVersion = 6.1.1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNScreens" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
English,
|
||||
en,
|
||||
);
|
||||
mainGroup = 58B511D21A9E6C8500147676;
|
||||
productRefGroup = 58B511D21A9E6C8500147676;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
58B511DA1A9E6C8500147676 /* RNScreens */,
|
||||
B5C32A45220C6379000FFB8D /* RNScreens-tvOS */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
58B511D71A9E6C8500147676 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
442389EC22DF259000611BBE /* RNSScreen.m in Sources */,
|
||||
448078F52114595900280661 /* RNSScreenContainer.m in Sources */,
|
||||
44A67C3122C3B8B40017156F /* RNSScreenStack.m in Sources */,
|
||||
4482D5EF22CB391800D5A5B9 /* RNSScreenStackHeaderConfig.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
B5C32A46220C6379000FFB8D /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B5C32A48220C6379000FFB8D /* RNSScreenContainer.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
0CE596A6BAEE45CA860361AD /* Testflight */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../../../React/**",
|
||||
"$(SRCROOT)/../../react-native/React/**",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = RNScreens;
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Testflight;
|
||||
};
|
||||
58B511ED1A9E6C8500147676 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
58B511EE1A9E6C8500147676 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = YES;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
58B511F01A9E6C8500147676 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../../../React/**",
|
||||
"$(SRCROOT)/../../react-native/React/**",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = RNScreens;
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
58B511F11A9E6C8500147676 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../../../React/**",
|
||||
"$(SRCROOT)/../../react-native/React/**",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = RNScreens;
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
B5C32A4C220C6379000FFB8D /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../../../React/**",
|
||||
"$(SRCROOT)/../../react-native/React/**",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = appletvos;
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
B5C32A4D220C6379000FFB8D /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../../../React/**",
|
||||
"$(SRCROOT)/../../react-native/React/**",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = appletvos;
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
B5C32A4E220C6379000FFB8D /* Testflight */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../../../React/**",
|
||||
"$(SRCROOT)/../../react-native/React/**",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = appletvos;
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Testflight;
|
||||
};
|
||||
C7F03305A3464E75B4F5A6CE /* Testflight */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = YES;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Testflight;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNScreens" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
58B511ED1A9E6C8500147676 /* Debug */,
|
||||
58B511EE1A9E6C8500147676 /* Release */,
|
||||
C7F03305A3464E75B4F5A6CE /* Testflight */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNScreens" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
58B511F01A9E6C8500147676 /* Debug */,
|
||||
58B511F11A9E6C8500147676 /* Release */,
|
||||
0CE596A6BAEE45CA860361AD /* Testflight */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
B5C32A4B220C6379000FFB8D /* Build configuration list for PBXNativeTarget "RNScreens-tvOS" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
B5C32A4C220C6379000FFB8D /* Debug */,
|
||||
B5C32A4D220C6379000FFB8D /* Release */,
|
||||
B5C32A4E220C6379000FFB8D /* Testflight */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 58B511D31A9E6C8500147676 /* Project object */;
|
||||
}
|
9
node_modules/react-native-screens/ios/UIViewController+RNScreens.h
generated
vendored
Normal file
9
node_modules/react-native-screens/ios/UIViewController+RNScreens.h
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface UIViewController (RNScreens)
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
52
node_modules/react-native-screens/ios/UIViewController+RNScreens.m
generated
vendored
Normal file
52
node_modules/react-native-screens/ios/UIViewController+RNScreens.m
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
#import "UIViewController+RNScreens.h"
|
||||
#import "RNSScreenContainer.h"
|
||||
|
||||
#import <objc/runtime.h>
|
||||
|
||||
@implementation UIViewController (RNScreens)
|
||||
|
||||
- (UIViewController *)reactNativeScreensChildViewControllerForStatusBarStyle
|
||||
{
|
||||
UIViewController *childVC = [self findChildRNScreensViewController];
|
||||
return childVC ?: [self reactNativeScreensChildViewControllerForStatusBarStyle];
|
||||
}
|
||||
|
||||
- (UIViewController *)reactNativeScreensChildViewControllerForStatusBarHidden
|
||||
{
|
||||
UIViewController *childVC = [self findChildRNScreensViewController];
|
||||
return childVC ?: [self reactNativeScreensChildViewControllerForStatusBarHidden];
|
||||
}
|
||||
|
||||
- (UIStatusBarAnimation)reactNativeScreensPreferredStatusBarUpdateAnimation
|
||||
{
|
||||
UIViewController *childVC = [self findChildRNScreensViewController];
|
||||
return childVC ? childVC.preferredStatusBarUpdateAnimation : [self reactNativeScreensPreferredStatusBarUpdateAnimation];
|
||||
}
|
||||
|
||||
- (UIViewController *)findChildRNScreensViewController
|
||||
{
|
||||
UIViewController *lastViewController = [[self childViewControllers] lastObject];
|
||||
if ([lastViewController conformsToProtocol:@protocol(RNScreensViewControllerDelegate)]) {
|
||||
return lastViewController;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (void)load
|
||||
{
|
||||
static dispatch_once_t once_token;
|
||||
dispatch_once(&once_token, ^{
|
||||
Class uiVCClass = [UIViewController class];
|
||||
|
||||
method_exchangeImplementations(class_getInstanceMethod(uiVCClass, @selector(childViewControllerForStatusBarStyle)),
|
||||
class_getInstanceMethod(uiVCClass, @selector(reactNativeScreensChildViewControllerForStatusBarStyle)));
|
||||
|
||||
method_exchangeImplementations(class_getInstanceMethod(uiVCClass, @selector(childViewControllerForStatusBarHidden)),
|
||||
class_getInstanceMethod(uiVCClass, @selector(reactNativeScreensChildViewControllerForStatusBarHidden)));
|
||||
|
||||
method_exchangeImplementations(class_getInstanceMethod(uiVCClass, @selector(preferredStatusBarUpdateAnimation)),
|
||||
class_getInstanceMethod(uiVCClass, @selector(reactNativeScreensPreferredStatusBarUpdateAnimation)));
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
Reference in New Issue
Block a user