Last active
November 5, 2017 00:49
-
-
Save didierbrun/0895f231f5de768f2720dd2b97786db2 to your computer and use it in GitHub Desktop.
Hack react-native-navigation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "RCCTabBarController.h" | |
#import "RCCViewController.h" | |
#import <React/RCTConvert.h> | |
#import "RCCManager.h" | |
#import "RCTHelpers.h" | |
#import <React/RCTUIManager.h> | |
#import "UIViewController+Rotation.h" | |
@interface RCTUIManager () | |
- (void)configureNextLayoutAnimation:(NSDictionary *)config | |
withCallback:(RCTResponseSenderBlock)callback | |
errorCallback:(__unused RCTResponseSenderBlock)errorCallback; | |
@end | |
@implementation RCCTabBarController | |
-(UIInterfaceOrientationMask)supportedInterfaceOrientations { | |
return [self supportedControllerOrientations]; | |
} | |
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController { | |
id queue = [[RCCManager sharedInstance].getBridge uiManager].methodQueue; | |
dispatch_async(queue, ^{ | |
[[[RCCManager sharedInstance].getBridge uiManager] configureNextLayoutAnimation:nil withCallback:^(NSArray* arr){} errorCallback:^(NSArray* arr){}]; | |
}); | |
if (tabBarController.selectedIndex != [tabBarController.viewControllers indexOfObject:viewController]) { | |
NSDictionary *body = @{ | |
@"selectedTabIndex": @([tabBarController.viewControllers indexOfObject:viewController]), | |
@"unselectedTabIndex": @(tabBarController.selectedIndex) | |
}; | |
[RCCTabBarController sendScreenTabChangedEvent:viewController body:body]; | |
[[[RCCManager sharedInstance] getBridge].eventDispatcher sendAppEventWithName:@"bottomTabSelected" body:body]; | |
if ([viewController isKindOfClass:[UINavigationController class]]) { | |
UINavigationController *navigationController = (UINavigationController*)viewController; | |
UIViewController *topViewController = navigationController.topViewController; | |
if ([topViewController isKindOfClass:[RCCViewController class]]) { | |
RCCViewController *topRCCViewController = (RCCViewController*)topViewController; | |
topRCCViewController.commandType = COMMAND_TYPE_BOTTOME_TAB_SELECTED; | |
topRCCViewController.timestamp = [RCTHelpers getTimestampString]; | |
} | |
} | |
} else { | |
[RCCTabBarController sendScreenTabPressedEvent:viewController body:nil]; | |
} | |
return YES; | |
} | |
- (UIImage *)image:(UIImage*)image withColor:(UIColor *)color1 | |
{ | |
UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale); | |
CGContextRef context = UIGraphicsGetCurrentContext(); | |
CGContextTranslateCTM(context, 0, image.size.height); | |
CGContextScaleCTM(context, 1.0, -1.0); | |
CGContextSetBlendMode(context, kCGBlendModeNormal); | |
CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height); | |
CGContextClipToMask(context, rect, image.CGImage); | |
[color1 setFill]; | |
CGContextFillRect(context, rect); | |
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); | |
UIGraphicsEndImageContext(); | |
return newImage; | |
} | |
- (instancetype)initWithProps:(NSDictionary *)props children:(NSArray *)children globalProps:(NSDictionary*)globalProps bridge:(RCTBridge *)bridge | |
{ | |
self = [super init]; | |
if (!self) return nil; | |
self.delegate = self; | |
self.tabBar.translucent = YES; // default | |
UIColor *buttonColor = nil; | |
UIColor *selectedButtonColor = nil; | |
UIColor *labelColor = nil; | |
UIColor *selectedLabelColor = nil; | |
NSDictionary *tabsStyle = props[@"style"]; | |
if (tabsStyle) | |
{ | |
NSString *tabBarButtonColor = tabsStyle[@"tabBarButtonColor"]; | |
if (tabBarButtonColor) | |
{ | |
UIColor *color = tabBarButtonColor != (id)[NSNull null] ? [RCTConvert UIColor:tabBarButtonColor] : nil; | |
self.tabBar.tintColor = color; | |
buttonColor = color; | |
selectedButtonColor = color; | |
} | |
NSString *tabBarSelectedButtonColor = tabsStyle[@"tabBarSelectedButtonColor"]; | |
if (tabBarSelectedButtonColor) | |
{ | |
UIColor *color = tabBarSelectedButtonColor != (id)[NSNull null] ? [RCTConvert UIColor:tabBarSelectedButtonColor] : nil; | |
self.tabBar.tintColor = color; | |
selectedButtonColor = color; | |
} | |
NSString *tabBarLabelColor = tabsStyle[@"tabBarLabelColor"]; | |
if(tabBarLabelColor) { | |
UIColor *color = tabBarLabelColor != (id)[NSNull null] ? [RCTConvert UIColor:tabBarLabelColor] : nil; | |
labelColor = color; | |
} | |
NSString *tabBarSelectedLabelColor = tabsStyle[@"tabBarSelectedLabelColor"]; | |
if(tabBarLabelColor) { | |
UIColor *color = tabBarSelectedLabelColor != (id)[NSNull null] ? [RCTConvert UIColor: | |
tabBarSelectedLabelColor] : nil; | |
selectedLabelColor = color; | |
} | |
NSString *tabBarBackgroundColor = tabsStyle[@"tabBarBackgroundColor"]; | |
if (tabBarBackgroundColor) | |
{ | |
UIColor *color = tabBarBackgroundColor != (id)[NSNull null] ? [RCTConvert UIColor:tabBarBackgroundColor] : nil; | |
self.tabBar.barTintColor = color; | |
} | |
NSString *tabBarTranslucent = tabsStyle[@"tabBarTranslucent"]; | |
if (tabBarTranslucent) | |
{ | |
self.tabBar.translucent = [tabBarTranslucent boolValue] ? YES : NO; | |
} | |
NSString *tabBarHideShadow = tabsStyle[@"tabBarHideShadow"]; | |
if (tabBarHideShadow) | |
{ | |
self.tabBar.clipsToBounds = [tabBarHideShadow boolValue] ? YES : NO; | |
} | |
} | |
NSMutableArray *viewControllers = [NSMutableArray array]; | |
// go over all the tab bar items | |
for (NSDictionary *tabItemLayout in children) | |
{ | |
// make sure the layout is valid | |
if (![tabItemLayout[@"type"] isEqualToString:@"TabBarControllerIOS.Item"]) continue; | |
if (!tabItemLayout[@"props"]) continue; | |
// get the view controller inside | |
if (!tabItemLayout[@"children"]) continue; | |
if (![tabItemLayout[@"children"] isKindOfClass:[NSArray class]]) continue; | |
if ([tabItemLayout[@"children"] count] < 1) continue; | |
NSDictionary *childLayout = tabItemLayout[@"children"][0]; | |
UIViewController *viewController = [RCCViewController controllerWithLayout:childLayout globalProps:globalProps bridge:bridge]; | |
if (!viewController) continue; | |
// create the tab icon and title | |
NSString *title = tabItemLayout[@"props"][@"title"]; | |
UIImage *iconImage = nil; | |
id icon = tabItemLayout[@"props"][@"icon"]; | |
if (icon) | |
{ | |
iconImage = [RCTConvert UIImage:icon]; | |
if (buttonColor) | |
{ | |
iconImage = [[self image:iconImage withColor:buttonColor] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]; | |
} | |
} | |
UIImage *iconImageSelected = nil; | |
id selectedIcon = tabItemLayout[@"props"][@"selectedIcon"]; | |
if (selectedIcon) { | |
iconImageSelected = [RCTConvert UIImage:selectedIcon]; | |
} else { | |
iconImageSelected = [RCTConvert UIImage:icon]; | |
} | |
viewController.tabBarItem = [[UITabBarItem alloc] initWithTitle:title image:iconImage tag:0]; | |
viewController.tabBarItem.accessibilityIdentifier = tabItemLayout[@"props"][@"testID"]; | |
viewController.tabBarItem.selectedImage = iconImageSelected; | |
id imageInsets = tabItemLayout[@"props"][@"iconInsets"]; | |
if (imageInsets && imageInsets != (id)[NSNull null]) | |
{ | |
id topInset = imageInsets[@"top"]; | |
id leftInset = imageInsets[@"left"]; | |
id bottomInset = imageInsets[@"bottom"]; | |
id rightInset = imageInsets[@"right"]; | |
CGFloat top = topInset != (id)[NSNull null] ? [RCTConvert CGFloat:topInset] : 0; | |
CGFloat left = topInset != (id)[NSNull null] ? [RCTConvert CGFloat:leftInset] : 0; | |
CGFloat bottom = topInset != (id)[NSNull null] ? [RCTConvert CGFloat:bottomInset] : 0; | |
CGFloat right = topInset != (id)[NSNull null] ? [RCTConvert CGFloat:rightInset] : 0; | |
viewController.tabBarItem.imageInsets = UIEdgeInsetsMake(top, left, bottom, right); | |
} | |
NSMutableDictionary *unselectedAttributes = [RCTHelpers textAttributesFromDictionary:tabsStyle withPrefix:@"tabBarText" baseFont:[UIFont systemFontOfSize:10]]; | |
if (!unselectedAttributes[NSForegroundColorAttributeName] && labelColor) { | |
unselectedAttributes[NSForegroundColorAttributeName] = labelColor; | |
} | |
[viewController.tabBarItem setTitleTextAttributes:unselectedAttributes forState:UIControlStateNormal]; | |
NSMutableDictionary *selectedAttributes = [RCTHelpers textAttributesFromDictionary:tabsStyle withPrefix:@"tabBarSelectedText" baseFont:[UIFont systemFontOfSize:10]]; | |
if (!selectedAttributes[NSForegroundColorAttributeName] && selectedLabelColor) { | |
selectedAttributes[NSForegroundColorAttributeName] = selectedLabelColor; | |
} | |
[viewController.tabBarItem setTitleTextAttributes:selectedAttributes forState:UIControlStateSelected]; | |
// create badge | |
NSObject *badge = tabItemLayout[@"props"][@"badge"]; | |
if (badge == nil || [badge isEqual:[NSNull null]]) | |
{ | |
viewController.tabBarItem.badgeValue = nil; | |
} | |
else | |
{ | |
viewController.tabBarItem.badgeValue = [NSString stringWithFormat:@"%@", badge]; | |
} | |
[viewControllers addObject:viewController]; | |
} | |
// replace the tabs | |
self.viewControllers = viewControllers; | |
NSNumber *initialTab = tabsStyle[@"initialTabIndex"]; | |
if (initialTab) | |
{ | |
NSInteger initialTabIndex = initialTab.integerValue; | |
[self setSelectedIndex:initialTabIndex]; | |
} | |
[self setRotation:props]; | |
return self; | |
} | |
-(void)viewDidLayoutSubviews { | |
[super viewDidLayoutSubviews]; | |
self.tabBar.hidden = YES; | |
} | |
- (void)performAction:(NSString*)performAction actionParams:(NSDictionary*)actionParams bridge:(RCTBridge *)bridge completion:(void (^)(void))completion | |
{ | |
if ([performAction isEqualToString:@"setBadge"]) | |
{ | |
UIViewController *viewController = nil; | |
NSNumber *tabIndex = actionParams[@"tabIndex"]; | |
if (tabIndex) | |
{ | |
int i = (int)[tabIndex integerValue]; | |
if ([self.viewControllers count] > i) | |
{ | |
viewController = [self.viewControllers objectAtIndex:i]; | |
} | |
} | |
NSString *contentId = actionParams[@"contentId"]; | |
NSString *contentType = actionParams[@"contentType"]; | |
if (contentId && contentType) | |
{ | |
viewController = [[RCCManager sharedInstance] getControllerWithId:contentId componentType:contentType]; | |
} | |
if (viewController) | |
{ | |
NSObject *badge = actionParams[@"badge"]; | |
if (badge == nil || [badge isEqual:[NSNull null]]) | |
{ | |
viewController.tabBarItem.badgeValue = nil; | |
} | |
else | |
{ | |
NSString *badgeColor = actionParams[@"badgeColor"]; | |
UIColor *color = badgeColor != (id)[NSNull null] ? [RCTConvert UIColor:badgeColor] : nil; | |
if ([viewController.tabBarItem respondsToSelector:@selector(badgeColor)]) { | |
viewController.tabBarItem.badgeColor = color; | |
} | |
viewController.tabBarItem.badgeValue = [NSString stringWithFormat:@"%@", badge]; | |
} | |
} | |
} | |
if ([performAction isEqualToString:@"switchTo"]) | |
{ | |
UIViewController *viewController = nil; | |
NSNumber *tabIndex = actionParams[@"tabIndex"]; | |
if (tabIndex) | |
{ | |
int i = (int)[tabIndex integerValue]; | |
if ([self.viewControllers count] > i) | |
{ | |
viewController = [self.viewControllers objectAtIndex:i]; | |
} | |
} | |
NSString *contentId = actionParams[@"contentId"]; | |
NSString *contentType = actionParams[@"contentType"]; | |
if (contentId && contentType) | |
{ | |
viewController = [[RCCManager sharedInstance] getControllerWithId:contentId componentType:contentType]; | |
} | |
if (viewController) | |
{ | |
[self setSelectedViewController:viewController]; | |
} | |
} | |
if ([performAction isEqualToString:@"setTabButton"]) | |
{ | |
UIViewController *viewController = nil; | |
NSNumber *tabIndex = actionParams[@"tabIndex"]; | |
if (tabIndex) | |
{ | |
int i = (int)[tabIndex integerValue]; | |
if ([self.viewControllers count] > i) | |
{ | |
viewController = [self.viewControllers objectAtIndex:i]; | |
} | |
} | |
NSString *contentId = actionParams[@"contentId"]; | |
NSString *contentType = actionParams[@"contentType"]; | |
if (contentId && contentType) | |
{ | |
viewController = [[RCCManager sharedInstance] getControllerWithId:contentId componentType:contentType]; | |
} | |
if (viewController) | |
{ | |
UIImage *iconImage = nil; | |
id icon = actionParams[@"icon"]; | |
if (icon && icon != (id)[NSNull null]) | |
{ | |
iconImage = [RCTConvert UIImage:icon]; | |
iconImage = [[self image:iconImage withColor:self.tabBar.tintColor] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]; | |
viewController.tabBarItem.image = iconImage; | |
} | |
UIImage *iconImageSelected = nil; | |
id selectedIcon = actionParams[@"selectedIcon"]; | |
if (selectedIcon && selectedIcon != (id)[NSNull null]) | |
{ | |
iconImageSelected = [RCTConvert UIImage:selectedIcon]; | |
viewController.tabBarItem.selectedImage = iconImageSelected; | |
} | |
} | |
} | |
if ([performAction isEqualToString:@"setTabBarHidden"]) | |
{ | |
BOOL hidden = [actionParams[@"hidden"] boolValue]; | |
CGRect nextFrame = self.tabBar.frame; | |
nextFrame.origin.y = UIScreen.mainScreen.bounds.size.height - (hidden ? 0 : self.tabBar.frame.size.height); | |
[UIView animateWithDuration: ([actionParams[@"animated"] boolValue] ? 0.45 : 0) | |
delay: 0 | |
usingSpringWithDamping: 0.75 | |
initialSpringVelocity: 0 | |
options: (hidden ? UIViewAnimationOptionCurveEaseIn : UIViewAnimationOptionCurveEaseOut) | |
animations:^() | |
{ | |
[self.tabBar setFrame:nextFrame]; | |
} | |
completion:^(BOOL finished) | |
{ | |
if (completion != nil) | |
{ | |
completion(); | |
} | |
}]; | |
return; | |
} | |
else if (completion != nil) | |
{ | |
completion(); | |
} | |
} | |
+(void)sendScreenTabChangedEvent:(UIViewController*)viewController body:(NSDictionary*)body{ | |
[RCCTabBarController sendTabEvent:@"bottomTabSelected" controller:viewController body:body]; | |
} | |
+(void)sendScreenTabPressedEvent:(UIViewController*)viewController body:(NSDictionary*)body{ | |
[RCCTabBarController sendTabEvent:@"bottomTabReselected" controller:viewController body:body]; | |
} | |
+(void)sendTabEvent:(NSString *)event controller:(UIViewController*)viewController body:(NSDictionary*)body{ | |
if ([viewController.view isKindOfClass:[RCTRootView class]]){ | |
RCTRootView *rootView = (RCTRootView *)viewController.view; | |
if (rootView.appProperties && rootView.appProperties[@"navigatorEventID"]) { | |
NSString *navigatorID = rootView.appProperties[@"navigatorID"]; | |
NSString *screenInstanceID = rootView.appProperties[@"screenInstanceID"]; | |
NSMutableDictionary *screenDict = [NSMutableDictionary dictionaryWithDictionary:@ | |
{ | |
@"id": event, | |
@"navigatorID": navigatorID, | |
@"screenInstanceID": screenInstanceID | |
}]; | |
if (body) { | |
[screenDict addEntriesFromDictionary:body]; | |
} | |
[[[RCCManager sharedInstance] getBridge].eventDispatcher sendAppEventWithName:rootView.appProperties[@"navigatorEventID"] body:screenDict]; | |
} | |
} | |
if ([viewController isKindOfClass:[UINavigationController class]]) { | |
UINavigationController *navigationController = (UINavigationController*)viewController; | |
UIViewController *topViewController = [navigationController topViewController]; | |
[RCCTabBarController sendTabEvent:event controller:topViewController body:body]; | |
} | |
} | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "RCCViewController.h" | |
#import "RCCNavigationController.h" | |
#import "RCCTabBarController.h" | |
#import "RCCDrawerController.h" | |
#import "RCCTheSideBarManagerViewController.h" | |
#import <React/RCTRootView.h> | |
#import "RCCManager.h" | |
#import <React/RCTConvert.h> | |
#import <React/RCTEventDispatcher.h> | |
#import "RCCExternalViewControllerProtocol.h" | |
#import "RCTHelpers.h" | |
#import "RCCTitleViewHelper.h" | |
#import "RCCCustomTitleView.h" | |
NSString* const RCCViewControllerCancelReactTouchesNotification = @"RCCViewControllerCancelReactTouchesNotification"; | |
const NSInteger BLUR_STATUS_TAG = 78264801; | |
const NSInteger BLUR_NAVBAR_TAG = 78264802; | |
const NSInteger TRANSPARENT_NAVBAR_TAG = 78264803; | |
@interface RCCViewController() <UIGestureRecognizerDelegate> | |
@property (nonatomic) BOOL _hidesBottomBarWhenPushed; | |
@property (nonatomic) BOOL _statusBarHideWithNavBar; | |
@property (nonatomic) BOOL _statusBarHidden; | |
@property (nonatomic) BOOL _statusBarTextColorSchemeLight; | |
@property (nonatomic, strong) NSDictionary *originalNavBarImages; | |
@property (nonatomic, strong) UIImageView *navBarHairlineImageView; | |
@property (nonatomic, weak) id <UIGestureRecognizerDelegate> originalInteractivePopGestureDelegate; | |
@end | |
@implementation RCCViewController | |
-(UIImageView *)navBarHairlineImageView { | |
if (!_navBarHairlineImageView) { | |
_navBarHairlineImageView = [self findHairlineImageViewUnder:self.navigationController.navigationBar]; | |
} | |
return _navBarHairlineImageView; | |
} | |
+ (UIViewController*)controllerWithLayout:(NSDictionary *)layout globalProps:(NSDictionary *)globalProps bridge:(RCTBridge *)bridge | |
{ | |
UIViewController* controller = nil; | |
if (!layout) return nil; | |
// get props | |
if (!layout[@"props"]) return nil; | |
if (![layout[@"props"] isKindOfClass:[NSDictionary class]]) return nil; | |
NSDictionary *props = layout[@"props"]; | |
// get children | |
if (!layout[@"children"]) return nil; | |
if (![layout[@"children"] isKindOfClass:[NSArray class]]) return nil; | |
NSArray *children = layout[@"children"]; | |
// create according to type | |
NSString *type = layout[@"type"]; | |
if (!type) return nil; | |
// regular view controller | |
if ([type isEqualToString:@"ViewControllerIOS"]) | |
{ | |
controller = [[RCCViewController alloc] initWithProps:props children:children globalProps:globalProps bridge:bridge]; | |
} | |
// navigation controller | |
if ([type isEqualToString:@"NavigationControllerIOS"]) | |
{ | |
controller = [[RCCNavigationController alloc] initWithProps:props children:children globalProps:globalProps bridge:bridge]; | |
} | |
// tab bar controller | |
if ([type isEqualToString:@"TabBarControllerIOS"]) | |
{ | |
controller = [[RCCTabBarController alloc] initWithProps:props children:children globalProps:globalProps bridge:bridge]; | |
} | |
// side menu controller | |
if ([type isEqualToString:@"DrawerControllerIOS"]) | |
{ | |
NSString *drawerType = props[@"type"]; | |
if ([drawerType isEqualToString:@"TheSideBar"]) { | |
controller = [[RCCTheSideBarManagerViewController alloc] initWithProps:props children:children globalProps:globalProps bridge:bridge]; | |
} | |
else { | |
controller = [[RCCDrawerController alloc] initWithProps:props children:children globalProps:globalProps bridge:bridge]; | |
} | |
} | |
// register the controller if we have an id | |
NSString *componentId = props[@"id"]; | |
if (controller && componentId) | |
{ | |
[[RCCManager sharedInstance] registerController:controller componentId:componentId componentType:type]; | |
if([controller isKindOfClass:[RCCViewController class]]) | |
{ | |
((RCCViewController*)controller).controllerId = componentId; | |
} | |
} | |
// set background image at root level | |
NSString *rootBackgroundImageName = props[@"style"][@"rootBackgroundImageName"]; | |
if (rootBackgroundImageName) { | |
UIImage *image = [UIImage imageNamed: rootBackgroundImageName]; | |
UIImageView *imageView = [[UIImageView alloc] initWithImage:image]; | |
[controller.view insertSubview:imageView atIndex:0]; | |
} | |
return controller; | |
} | |
-(NSDictionary*)addCommandTypeAndTimestampIfExists:(NSDictionary*)globalProps passProps:(NSDictionary*)passProps { | |
NSMutableDictionary *modifiedPassProps = [NSMutableDictionary dictionaryWithDictionary:passProps]; | |
NSString *commandType = globalProps[GLOBAL_SCREEN_ACTION_COMMAND_TYPE]; | |
if (commandType) { | |
modifiedPassProps[GLOBAL_SCREEN_ACTION_COMMAND_TYPE] = commandType; | |
} | |
NSString *timestamp = globalProps[GLOBAL_SCREEN_ACTION_TIMESTAMP]; | |
if (timestamp) { | |
modifiedPassProps[GLOBAL_SCREEN_ACTION_TIMESTAMP] = timestamp; | |
} | |
return modifiedPassProps; | |
} | |
- (instancetype)initWithProps:(NSDictionary *)props children:(NSArray *)children globalProps:(NSDictionary *)globalProps bridge:(RCTBridge *)bridge | |
{ | |
NSString *component = props[@"component"]; | |
if (!component) return nil; | |
NSDictionary *passProps = props[@"passProps"]; | |
NSDictionary *navigatorStyle = props[@"style"]; | |
NSMutableDictionary *mergedProps = [NSMutableDictionary dictionaryWithDictionary:globalProps]; | |
[mergedProps addEntriesFromDictionary:passProps]; | |
RCTRootView *reactView = [[RCTRootView alloc] initWithBridge:bridge moduleName:component initialProperties:mergedProps]; | |
if (!reactView) return nil; | |
self = [super init]; | |
if (!self) return nil; | |
[self commonInit:reactView navigatorStyle:navigatorStyle props:props]; | |
self.navigationController.interactivePopGestureRecognizer.delegate = self; | |
[self.navigationController setNavigationBarHidden:YES animated:NO]; | |
return self; | |
} | |
- (instancetype)initWithComponent:(NSString *)component passProps:(NSDictionary *)passProps navigatorStyle:(NSDictionary*)navigatorStyle globalProps:(NSDictionary *)globalProps bridge:(RCTBridge *)bridge | |
{ | |
NSMutableDictionary *mergedProps = [NSMutableDictionary dictionaryWithDictionary:globalProps]; | |
[mergedProps addEntriesFromDictionary:passProps]; | |
RCTRootView *reactView = [[RCTRootView alloc] initWithBridge:bridge moduleName:component initialProperties:mergedProps]; | |
if (!reactView) return nil; | |
self = [super init]; | |
if (!self) return nil; | |
NSDictionary *modifiedPassProps = [self addCommandTypeAndTimestampIfExists:globalProps passProps:passProps]; | |
[self commonInit:reactView navigatorStyle:navigatorStyle props:modifiedPassProps]; | |
[self.navigationController setNavigationBarHidden:YES animated:NO]; | |
return self; | |
} | |
- (void)commonInit:(RCTRootView*)reactView navigatorStyle:(NSDictionary*)navigatorStyle props:(NSDictionary*)props | |
{ | |
self.view = reactView; | |
self.edgesForExtendedLayout = UIRectEdgeNone; // default | |
self.automaticallyAdjustsScrollViewInsets = NO; // default | |
self.navigatorStyle = [NSMutableDictionary dictionaryWithDictionary:[[RCCManager sharedInstance] getAppStyle]]; | |
[self.navigatorStyle addEntriesFromDictionary:navigatorStyle]; | |
[self setStyleOnInit]; | |
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onRNReload) name:RCTJavaScriptWillStartLoadingNotification object:nil]; | |
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onCancelReactTouches) name:RCCViewControllerCancelReactTouchesNotification object:nil]; | |
self.commandType = props[GLOBAL_SCREEN_ACTION_COMMAND_TYPE]; | |
self.timestamp = props[GLOBAL_SCREEN_ACTION_TIMESTAMP]; | |
// In order to support 3rd party native ViewControllers, we support passing a class name as a prop named `ExternalNativeScreenClass` | |
// In this case, we create an instance and add it as a child ViewController which preserves the VC lifecycle. | |
// In case some props are necessary in the native ViewController, the ExternalNativeScreenProps can be used to pass them | |
[self addExternalVCIfNecessary:props]; | |
} | |
- (void)dealloc | |
{ | |
[[NSNotificationCenter defaultCenter] removeObserver:self]; | |
self.view = nil; | |
} | |
-(void)onRNReload | |
{ | |
[[NSNotificationCenter defaultCenter] removeObserver:self]; | |
self.view = nil; | |
} | |
-(void)onCancelReactTouches | |
{ | |
if ([self.view isKindOfClass:[RCTRootView class]]){ | |
[(RCTRootView*)self.view cancelTouches]; | |
} | |
} | |
- (void)sendScreenChangedEvent:(NSString *)eventName | |
{ | |
if ([self.view isKindOfClass:[RCTRootView class]]){ | |
RCTRootView *rootView = (RCTRootView *)self.view; | |
if (rootView.appProperties && rootView.appProperties[@"navigatorEventID"]) { | |
[[[RCCManager sharedInstance] getBridge].eventDispatcher sendAppEventWithName:rootView.appProperties[@"navigatorEventID"] body:@ | |
{ | |
@"type": @"ScreenChangedEvent", | |
@"id": eventName | |
}]; | |
} | |
} | |
} | |
- (void)sendGlobalScreenEvent:(NSString *)eventName endTimestampString:(NSString *)endTimestampStr shouldReset:(BOOL)shouldReset { | |
if (!self.commandType) return; | |
if ([self.view isKindOfClass:[RCTRootView class]]){ | |
NSString *screenName = [((RCTRootView*)self.view) moduleName]; | |
[[[RCCManager sharedInstance] getBridge].eventDispatcher sendAppEventWithName:eventName body:@ | |
{ | |
@"commandType": self.commandType ? self.commandType : @"", | |
@"screen": screenName ? screenName : @"", | |
@"startTime": self.timestamp ? self.timestamp : @"", | |
@"endTime": endTimestampStr ? endTimestampStr : @"" | |
}]; | |
if (shouldReset) { | |
self.commandType = nil; | |
self.timestamp = nil; | |
} | |
} | |
} | |
-(BOOL)isDisappearTriggeredFromPop:(NSString *)eventName { | |
NSArray *navigationViewControllers = self.navigationController.viewControllers; | |
if (navigationViewControllers.lastObject == self || [navigationViewControllers indexOfObject:self] == NSNotFound) { | |
return YES; | |
} | |
return NO; | |
} | |
- (NSString *)getTimestampString { | |
long long milliseconds = (long long)([[NSDate date] timeIntervalSince1970] * 1000.0); | |
return [NSString stringWithFormat:@"%lld", milliseconds]; | |
} | |
// This is walk around for React-Native bug. | |
// https://github.com/wix/react-native-navigation/issues/1446 | |
// | |
// Buttons in ScrollView after changing route/pushing/showing modal | |
// while there is a momentum scroll are not clickable. | |
// Back to normal after user start scroll with momentum | |
- (void)_traverseAndCall:(UIView*)view | |
{ | |
if([view isKindOfClass:[UIScrollView class]] && ([[(UIScrollView*)view delegate] respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) ) { | |
dispatch_async(dispatch_get_main_queue(), ^{ | |
[[(UIScrollView*)view delegate] scrollViewDidEndDecelerating:(id)view]; | |
}); | |
} | |
[view.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { | |
[self _traverseAndCall:obj]; | |
}]; | |
} | |
// fix iOS11 safeArea - https://github.com/facebook/react-native/issues/15681 | |
// rnn issue - https://github.com/wix/react-native-navigation/issues/1858 | |
- (void)_traverseAndFixScrollViewSafeArea:(UIView *)view { | |
#ifdef __IPHONE_11_0 | |
if ([view isKindOfClass:UIScrollView.class] && [view respondsToSelector:@selector(setContentInsetAdjustmentBehavior:)]) { | |
[((UIScrollView*)view) setContentInsetAdjustmentBehavior:UIScrollViewContentInsetAdjustmentNever]; | |
} | |
[view.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { | |
[self _traverseAndFixScrollViewSafeArea:obj]; | |
}]; | |
#endif | |
} | |
- (void)viewDidAppear:(BOOL)animated | |
{ | |
[super viewDidAppear:animated]; | |
[self sendGlobalScreenEvent:@"didAppear" endTimestampString:[self getTimestampString] shouldReset:YES]; | |
[self sendScreenChangedEvent:@"didAppear"]; | |
} | |
- (void)viewWillAppear:(BOOL)animated | |
{ | |
[super viewWillAppear:animated]; | |
[self _traverseAndFixScrollViewSafeArea:self.view]; | |
[self sendGlobalScreenEvent:@"willAppear" endTimestampString:[self getTimestampString] shouldReset:NO]; | |
[self sendScreenChangedEvent:@"willAppear"]; | |
[self setStyleOnAppear]; | |
} | |
- (void)viewDidDisappear:(BOOL)animated | |
{ | |
[self _traverseAndCall:self.view]; | |
[super viewDidDisappear:animated]; | |
[self sendGlobalScreenEvent:@"didDisappear" endTimestampString:[self getTimestampString] shouldReset:YES]; | |
[self sendScreenChangedEvent:@"didDisappear"]; | |
} | |
- (void)viewWillDisappear:(BOOL)animated | |
{ | |
[super viewWillDisappear:animated]; | |
[self sendGlobalScreenEvent:@"willDisappear" endTimestampString:[self getTimestampString] shouldReset:NO]; | |
[self sendScreenChangedEvent:@"willDisappear"]; | |
[self setStyleOnDisappear]; | |
} | |
// most styles should be set here since when we pop a view controller that changed them | |
// we want to reset the style to what we expect (so we need to reset on every willAppear) | |
- (void)setStyleOnAppear | |
{ | |
[self setStyleOnAppearForViewController:self appeared:false]; | |
} | |
- (void)updateStyle | |
{ | |
[self setStyleOnAppearForViewController:self appeared:true]; | |
} | |
-(void)setStyleOnAppearForViewController:(UIViewController*)viewController appeared:(BOOL)appeared | |
{ | |
NSString *screenBackgroundColor = self.navigatorStyle[@"screenBackgroundColor"]; | |
if (screenBackgroundColor) { | |
UIColor *color = screenBackgroundColor != (id)[NSNull null] ? [RCTConvert UIColor:screenBackgroundColor] : nil; | |
viewController.view.backgroundColor = color; | |
} | |
NSString *screenBackgroundImageName = self.navigatorStyle[@"screenBackgroundImageName"]; | |
if (screenBackgroundImageName) { | |
UIImage *image = [UIImage imageNamed: screenBackgroundImageName]; | |
viewController.view.layer.contents = (__bridge id _Nullable)(image.CGImage); | |
} | |
NSString *navBarBackgroundColor = self.navigatorStyle[@"navBarBackgroundColor"]; | |
if (navBarBackgroundColor) { | |
UIColor *color = navBarBackgroundColor != (id)[NSNull null] ? [RCTConvert UIColor:navBarBackgroundColor] : nil; | |
viewController.navigationController.navigationBar.barTintColor = color; | |
} else { | |
viewController.navigationController.navigationBar.barTintColor = nil; | |
} | |
NSMutableDictionary *titleTextAttributes = [RCTHelpers textAttributesFromDictionary:self.navigatorStyle withPrefix:@"navBarText" baseFont:[UIFont boldSystemFontOfSize:17]]; | |
[self.navigationController.navigationBar setTitleTextAttributes:titleTextAttributes]; | |
if (self.navigationItem.titleView && [self.navigationItem.titleView isKindOfClass:[RCCTitleView class]]) { | |
RCCTitleView *titleView = (RCCTitleView *)self.navigationItem.titleView; | |
RCCTitleViewHelper *helper = [[RCCTitleViewHelper alloc] init:viewController navigationController:viewController.navigationController title:titleView.titleLabel.text subtitle:titleView.subtitleLabel.text titleImageData:nil isSetSubtitle:NO]; | |
[helper setup:self.navigatorStyle]; | |
} | |
NSMutableDictionary *navButtonTextAttributes = [RCTHelpers textAttributesFromDictionary:self.navigatorStyle withPrefix:@"navBarButton"]; | |
NSMutableDictionary *leftNavButtonTextAttributes = [RCTHelpers textAttributesFromDictionary:self.navigatorStyle withPrefix:@"navBarLeftButton"]; | |
NSMutableDictionary *rightNavButtonTextAttributes = [RCTHelpers textAttributesFromDictionary:self.navigatorStyle withPrefix:@"navBarRightButton"]; | |
if ( | |
navButtonTextAttributes.allKeys.count > 0 || | |
leftNavButtonTextAttributes.allKeys.count > 0 || | |
rightNavButtonTextAttributes.allKeys.count > 0 | |
) { | |
for (UIBarButtonItem *item in viewController.navigationItem.leftBarButtonItems) { | |
[item setTitleTextAttributes:navButtonTextAttributes forState:UIControlStateNormal]; | |
if (leftNavButtonTextAttributes.allKeys.count > 0) { | |
[item setTitleTextAttributes:leftNavButtonTextAttributes forState:UIControlStateNormal]; | |
} | |
} | |
for (UIBarButtonItem *item in viewController.navigationItem.rightBarButtonItems) { | |
[item setTitleTextAttributes:navButtonTextAttributes forState:UIControlStateNormal]; | |
if (rightNavButtonTextAttributes.allKeys.count > 0) { | |
[item setTitleTextAttributes:rightNavButtonTextAttributes forState:UIControlStateNormal]; | |
} | |
} | |
// At the moment, this seems to be the only thing that gets the back button correctly | |
[navButtonTextAttributes removeObjectForKey:NSForegroundColorAttributeName]; | |
[[UIBarButtonItem appearance] setTitleTextAttributes:navButtonTextAttributes forState:UIControlStateNormal]; | |
} | |
NSString *navBarButtonColor = self.navigatorStyle[@"navBarButtonColor"]; | |
if (navBarButtonColor) { | |
UIColor *color = navBarButtonColor != (id)[NSNull null] ? [RCTConvert UIColor:navBarButtonColor] : nil; | |
viewController.navigationController.navigationBar.tintColor = color; | |
} else | |
{ | |
viewController.navigationController.navigationBar.tintColor = nil; | |
} | |
BOOL viewControllerBasedStatusBar = false; | |
NSObject *viewControllerBasedStatusBarAppearance = [[NSBundle mainBundle] infoDictionary][@"UIViewControllerBasedStatusBarAppearance"]; | |
if (viewControllerBasedStatusBarAppearance && [viewControllerBasedStatusBarAppearance isKindOfClass:[NSNumber class]]) { | |
viewControllerBasedStatusBar = [(NSNumber *)viewControllerBasedStatusBarAppearance boolValue]; | |
} | |
NSString *statusBarTextColorSchemeSingleScreen = self.navigatorStyle[@"statusBarTextColorSchemeSingleScreen"]; | |
NSString *statusBarTextColorScheme = self.navigatorStyle[@"statusBarTextColorScheme"]; | |
NSString *finalColorScheme = statusBarTextColorSchemeSingleScreen ? : statusBarTextColorScheme; | |
if (finalColorScheme && [finalColorScheme isEqualToString:@"light"]) { | |
if (!statusBarTextColorSchemeSingleScreen) { | |
viewController.navigationController.navigationBar.barStyle = UIBarStyleBlack; | |
} | |
self._statusBarTextColorSchemeLight = true; | |
if (!viewControllerBasedStatusBarAppearance) { | |
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent]; | |
} | |
[viewController setNeedsStatusBarAppearanceUpdate]; | |
} else { | |
if (!statusBarTextColorSchemeSingleScreen) { | |
viewController.navigationController.navigationBar.barStyle = UIBarStyleDefault; | |
} | |
self._statusBarTextColorSchemeLight = false; | |
if (!viewControllerBasedStatusBarAppearance) { | |
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault]; | |
} | |
[viewController setNeedsStatusBarAppearanceUpdate]; | |
} | |
NSNumber *tabBarHidden = self.navigatorStyle[@"tabBarHidden"]; | |
BOOL tabBarHiddenBool = tabBarHidden ? [tabBarHidden boolValue] : NO; | |
if (YES) { | |
UITabBar *tabBar = viewController.tabBarController.tabBar; | |
tabBar.transform = CGAffineTransformMakeTranslation(0, tabBar.frame.size.height); | |
} | |
NSNumber *navBarHidden = self.navigatorStyle[@"navBarHidden"]; | |
BOOL navBarHiddenBool = navBarHidden ? [navBarHidden boolValue] : NO; | |
if (viewController.navigationController.navigationBarHidden != navBarHiddenBool) { | |
[viewController.navigationController setNavigationBarHidden:YES animated:NO]; | |
} | |
NSNumber *navBarHideOnScroll = self.navigatorStyle[@"navBarHideOnScroll"]; | |
BOOL navBarHideOnScrollBool = navBarHideOnScroll ? [navBarHideOnScroll boolValue] : NO; | |
if (navBarHideOnScrollBool) { | |
viewController.navigationController.hidesBarsOnSwipe = YES; | |
} else { | |
viewController.navigationController.hidesBarsOnSwipe = NO; | |
} | |
NSNumber *statusBarBlur = self.navigatorStyle[@"statusBarBlur"]; | |
BOOL statusBarBlurBool = statusBarBlur ? [statusBarBlur boolValue] : NO; | |
if (statusBarBlurBool && ![viewController.view viewWithTag:BLUR_STATUS_TAG]) { | |
UIVisualEffectView *blur = [[UIVisualEffectView alloc] initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]]; | |
blur.frame = [[UIApplication sharedApplication] statusBarFrame]; | |
blur.tag = BLUR_STATUS_TAG; | |
[viewController.view insertSubview:blur atIndex:0]; | |
} | |
NSNumber *navBarBlur = self.navigatorStyle[@"navBarBlur"]; | |
BOOL navBarBlurBool = navBarBlur ? [navBarBlur boolValue] : NO; | |
if (navBarBlurBool) { | |
if (![viewController.navigationController.navigationBar viewWithTag:BLUR_NAVBAR_TAG]) { | |
[self storeOriginalNavBarImages]; | |
[viewController.navigationController.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault]; | |
viewController.navigationController.navigationBar.shadowImage = [UIImage new]; | |
UIVisualEffectView *blur = [[UIVisualEffectView alloc] initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]]; | |
CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame]; | |
blur.frame = CGRectMake(0, -1 * statusBarFrame.size.height, viewController.navigationController.navigationBar.frame.size.width, viewController.navigationController.navigationBar.frame.size.height + statusBarFrame.size.height); | |
blur.userInteractionEnabled = NO; | |
blur.tag = BLUR_NAVBAR_TAG; | |
[viewController.navigationController.navigationBar insertSubview:blur atIndex:0]; | |
[viewController.navigationController.navigationBar sendSubviewToBack:blur]; | |
} | |
} else { | |
UIView *blur = [viewController.navigationController.navigationBar viewWithTag:BLUR_NAVBAR_TAG]; | |
if (blur) { | |
[blur removeFromSuperview]; | |
[viewController.navigationController.navigationBar setBackgroundImage:self.originalNavBarImages[@"bgImage"] forBarMetrics:UIBarMetricsDefault]; | |
viewController.navigationController.navigationBar.shadowImage = self.originalNavBarImages[@"shadowImage"]; | |
self.originalNavBarImages = nil; | |
} | |
} | |
NSNumber *navBarTransparent = self.navigatorStyle[@"navBarTransparent"]; | |
BOOL navBarTransparentBool = navBarTransparent ? [navBarTransparent boolValue] : NO; | |
void (^action)() = ^ { | |
if (navBarTransparentBool) | |
{ | |
if (![viewController.navigationController.navigationBar viewWithTag:TRANSPARENT_NAVBAR_TAG]) | |
{ | |
[self storeOriginalNavBarImages]; | |
[viewController.navigationController.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault]; | |
viewController.navigationController.navigationBar.shadowImage = [UIImage new]; | |
UIView *transparentView = [[UIView alloc] initWithFrame:CGRectZero]; | |
transparentView.tag = TRANSPARENT_NAVBAR_TAG; | |
[viewController.navigationController.navigationBar insertSubview:transparentView atIndex:0]; | |
} | |
} | |
else | |
{ | |
UIView *transparentView = [viewController.navigationController.navigationBar viewWithTag:TRANSPARENT_NAVBAR_TAG]; | |
if (transparentView) | |
{ | |
[transparentView removeFromSuperview]; | |
[viewController.navigationController.navigationBar setBackgroundImage:self.originalNavBarImages[@"bgImage"] forBarMetrics:UIBarMetricsDefault]; | |
viewController.navigationController.navigationBar.shadowImage = self.originalNavBarImages[@"shadowImage"]; | |
self.originalNavBarImages = nil; | |
} | |
} | |
}; | |
if (!self.transitionCoordinator || self.transitionCoordinator.initiallyInteractive || !navBarTransparentBool || appeared) { | |
action(); | |
} else { | |
UIView* backgroundView = [self.navigationController.navigationBar valueForKey:@"backgroundView"]; | |
CGFloat originalAlpha = backgroundView.alpha; | |
backgroundView.alpha = navBarTransparentBool ? 0.0 : 1.0; | |
[self.transitionCoordinator animateAlongsideTransition:nil completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) { | |
action(); | |
backgroundView.alpha = originalAlpha; | |
}]; | |
} | |
NSNumber *autoAdjustsScrollViewInsets = self.navigatorStyle[@"autoAdjustScrollViewInsets"]; | |
viewController.automaticallyAdjustsScrollViewInsets = autoAdjustsScrollViewInsets ? [autoAdjustsScrollViewInsets boolValue] : false; | |
NSNumber *navBarTranslucent = self.navigatorStyle[@"navBarTranslucent"]; | |
BOOL navBarTranslucentBool = navBarTranslucent ? [navBarTranslucent boolValue] : NO; | |
if (navBarTranslucentBool || navBarBlurBool) { | |
viewController.navigationController.navigationBar.translucent = YES; | |
} else { | |
viewController.navigationController.navigationBar.translucent = NO; | |
} | |
NSNumber *extendedLayoutIncludesOpaqueBars = self.navigatorStyle[@"extendedLayoutIncludesOpaqueBars"]; | |
BOOL extendedLayoutIncludesOpaqueBarsBool = extendedLayoutIncludesOpaqueBars ? [extendedLayoutIncludesOpaqueBars boolValue] : NO; | |
viewController.extendedLayoutIncludesOpaqueBars = extendedLayoutIncludesOpaqueBarsBool; | |
NSNumber *drawUnderNavBar = self.navigatorStyle[@"drawUnderNavBar"]; | |
BOOL drawUnderNavBarBool = drawUnderNavBar ? [drawUnderNavBar boolValue] : NO; | |
if (drawUnderNavBarBool) { | |
viewController.edgesForExtendedLayout |= UIRectEdgeTop; | |
} | |
else { | |
viewController.edgesForExtendedLayout &= ~UIRectEdgeTop; | |
} | |
NSNumber *drawUnderTabBar = self.navigatorStyle[@"drawUnderTabBar"]; | |
BOOL drawUnderTabBarBool = drawUnderTabBar ? [drawUnderTabBar boolValue] : NO; | |
if (YES) { | |
viewController.edgesForExtendedLayout |= UIRectEdgeBottom; | |
} else { | |
viewController.edgesForExtendedLayout &= ~UIRectEdgeBottom; | |
} | |
NSNumber *removeNavBarBorder = self.navigatorStyle[@"navBarNoBorder"]; | |
BOOL removeNavBarBorderBool = removeNavBarBorder ? [removeNavBarBorder boolValue] : NO; | |
if (removeNavBarBorderBool) { | |
self.navBarHairlineImageView.hidden = YES; | |
} else { | |
self.navBarHairlineImageView.hidden = NO; | |
} | |
//Bug fix: in case there is a interactivePopGestureRecognizer, it prevents react-native from getting touch events on the left screen area that the gesture handles | |
//overriding the delegate of the gesture prevents this from happening while keeping the gesture intact (another option was to disable it completely by demand) | |
if(self.navigationController.viewControllers.count > 1){ | |
if (self.navigationController != nil && self.navigationController.interactivePopGestureRecognizer != nil) | |
{ | |
id <UIGestureRecognizerDelegate> interactivePopGestureRecognizer = self.navigationController.interactivePopGestureRecognizer.delegate; | |
if (interactivePopGestureRecognizer != nil && interactivePopGestureRecognizer != self) | |
{ | |
self.originalInteractivePopGestureDelegate = interactivePopGestureRecognizer; | |
self.navigationController.interactivePopGestureRecognizer.delegate = self; | |
} | |
} | |
} | |
NSString *navBarCustomView = self.navigatorStyle[@"navBarCustomView"]; | |
if (navBarCustomView && ![self.navigationItem.titleView isKindOfClass:[RCCCustomTitleView class]]) { | |
if ([self.view isKindOfClass:[RCTRootView class]]) { | |
RCTBridge *bridge = ((RCTRootView*)self.view).bridge; | |
NSDictionary *initialProps = self.navigatorStyle[@"navBarCustomViewInitialProps"]; | |
RCTRootView *reactView = [[RCTRootView alloc] initWithBridge:bridge moduleName:navBarCustomView initialProperties:initialProps]; | |
RCCCustomTitleView *titleView = [[RCCCustomTitleView alloc] initWithFrame:self.navigationController.navigationBar.bounds subView:reactView alignment:self.navigatorStyle[@"navBarComponentAlignment"]]; | |
titleView.backgroundColor = [UIColor clearColor]; | |
reactView.backgroundColor = [UIColor clearColor]; | |
self.navigationItem.titleView = titleView; | |
self.navigationItem.titleView.backgroundColor = [UIColor clearColor]; | |
self.navigationItem.titleView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; | |
self.navigationItem.titleView.clipsToBounds = YES; | |
} | |
} | |
} | |
-(void)storeOriginalNavBarImages { | |
NSMutableDictionary *originalNavBarImages = [@{} mutableCopy]; | |
UIImage *bgImage = [self.navigationController.navigationBar backgroundImageForBarMetrics:UIBarMetricsDefault]; | |
if (bgImage != nil) { | |
originalNavBarImages[@"bgImage"] = bgImage; | |
} | |
UIImage *shadowImage = self.navigationController.navigationBar.shadowImage; | |
if (shadowImage != nil) { | |
originalNavBarImages[@"shadowImage"] = shadowImage; | |
} | |
self.originalNavBarImages = originalNavBarImages; | |
} | |
-(void)setStyleOnDisappear { | |
self.navBarHairlineImageView.hidden = NO; | |
if (self.navigationController != nil && self.navigationController.interactivePopGestureRecognizer != nil && self.originalInteractivePopGestureDelegate != nil) | |
{ | |
self.navigationController.interactivePopGestureRecognizer.delegate = self.originalInteractivePopGestureDelegate; | |
self.originalInteractivePopGestureDelegate = nil; | |
} | |
} | |
// only styles that can't be set on willAppear should be set here | |
- (void)setStyleOnInit | |
{ | |
NSNumber *tabBarHidden = self.navigatorStyle[@"tabBarHidden"]; | |
BOOL tabBarHiddenBool = tabBarHidden ? [tabBarHidden boolValue] : NO; | |
if (tabBarHiddenBool) { | |
self._hidesBottomBarWhenPushed = YES; | |
} else { | |
self._hidesBottomBarWhenPushed = NO; | |
} | |
NSNumber *statusBarHideWithNavBar = self.navigatorStyle[@"statusBarHideWithNavBar"]; | |
BOOL statusBarHideWithNavBarBool = statusBarHideWithNavBar ? [statusBarHideWithNavBar boolValue] : NO; | |
if (statusBarHideWithNavBarBool) { | |
self._statusBarHideWithNavBar = YES; | |
} else { | |
self._statusBarHideWithNavBar = NO; | |
} | |
NSNumber *statusBarHidden = self.navigatorStyle[@"statusBarHidden"]; | |
BOOL statusBarHiddenBool = statusBarHidden ? [statusBarHidden boolValue] : NO; | |
if (statusBarHiddenBool) { | |
self._statusBarHidden = YES; | |
} else { | |
self._statusBarHidden = NO; | |
} | |
} | |
- (BOOL)hidesBottomBarWhenPushed | |
{ | |
return YES; | |
if (!self._hidesBottomBarWhenPushed) return NO; | |
return (self.navigationController.topViewController == self); | |
} | |
- (BOOL)prefersStatusBarHidden | |
{ | |
if (self._statusBarHidden) { | |
return YES; | |
} | |
if (self._statusBarHideWithNavBar) { | |
return self.navigationController.isNavigationBarHidden; | |
} else { | |
return NO; | |
} | |
} | |
- (void)setNavBarVisibilityChange:(BOOL)animated { | |
[self.navigationController setNavigationBarHidden:YES animated:NO]; | |
} | |
- (UIStatusBarStyle)preferredStatusBarStyle | |
{ | |
if (self._statusBarTextColorSchemeLight){ | |
return UIStatusBarStyleLightContent; | |
} else { | |
return UIStatusBarStyleDefault; | |
} | |
} | |
- (UIImageView *)findHairlineImageViewUnder:(UIView *)view { | |
if ([view isKindOfClass:UIImageView.class] && view.bounds.size.height <= 1.0) { | |
return (UIImageView *)view; | |
} | |
for (UIView *subview in view.subviews) { | |
UIImageView *imageView = [self findHairlineImageViewUnder:subview]; | |
if (imageView) { | |
return imageView; | |
} | |
} | |
return nil; | |
} | |
-(void)addExternalVCIfNecessary:(NSDictionary*)props | |
{ | |
NSString *externalScreenClass = props[@"externalNativeScreenClass"]; | |
if (externalScreenClass != nil) | |
{ | |
Class class = NSClassFromString(externalScreenClass); | |
if (class != NULL) | |
{ | |
id obj = [[class alloc] init]; | |
if (obj != nil && [obj isKindOfClass:[UIViewController class]] && [obj conformsToProtocol:@protocol(RCCExternalViewControllerProtocol)]) | |
{ | |
((id <RCCExternalViewControllerProtocol>)obj).controllerDelegate = self; | |
[obj setProps:props[@"externalNativeScreenProps"]]; | |
UIViewController *viewController = (UIViewController*)obj; | |
[self addChildViewController:viewController]; | |
viewController.view.frame = self.view.bounds; | |
[self.view addSubview:viewController.view]; | |
[viewController didMoveToParentViewController:self]; | |
} | |
else | |
{ | |
NSLog(@"addExternalVCIfNecessary: could not create instance. Make sure that your class is a UIViewController whihc confirms to RCCExternalViewControllerProtocol"); | |
} | |
} | |
else | |
{ | |
NSLog(@"addExternalVCIfNecessary: could not create class from string. Check that the proper class name wass passed in ExternalNativeScreenClass"); | |
} | |
} | |
} | |
#pragma mark - NewRelic | |
- (NSString*) customNewRelicInteractionName | |
{ | |
NSString *interactionName = nil; | |
if (self.view != nil && [self.view isKindOfClass:[RCTRootView class]]) | |
{ | |
NSString *moduleName = ((RCTRootView*)self.view).moduleName; | |
if(moduleName != nil) | |
{ | |
interactionName = [NSString stringWithFormat:@"RCCViewController: %@", moduleName]; | |
} | |
} | |
if (interactionName == nil) | |
{ | |
interactionName = [NSString stringWithFormat:@"RCCViewController with title: %@", self.title]; | |
} | |
return interactionName; | |
} | |
#pragma mark - UIGestureRecognizerDelegate | |
-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { | |
NSNumber *disabledBackGesture = self.navigatorStyle[@"disabledBackGesture"]; | |
BOOL disabledBackGestureBool = disabledBackGesture ? [disabledBackGesture boolValue] : NO; | |
return !disabledBackGestureBool; | |
} | |
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{ | |
NSNumber *disabledSimultaneousGesture = self.navigatorStyle[@"disabledSimultaneousGesture"]; | |
BOOL disabledSimultaneousGestureBool = disabledSimultaneousGesture ? [disabledSimultaneousGesture boolValue] : YES; // make default value of disabledSimultaneousGesture is true | |
return !disabledSimultaneousGestureBool; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment