533 lines
18 KiB
Mathematica
533 lines
18 KiB
Mathematica
![]() |
#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
|