diff --git a/lib/ios/RNNBasePresenter.m b/lib/ios/RNNBasePresenter.m index dc9425ede5a..281a69dc5e3 100644 --- a/lib/ios/RNNBasePresenter.m +++ b/lib/ios/RNNBasePresenter.m @@ -1,9 +1,21 @@ #import "RNNBasePresenter.h" #import "UIViewController+RNNOptions.h" #import "RNNTabBarItemCreator.h" +#import "RNNReactComponentManager.h" + +@interface RNNBasePresenter () +@property (nonatomic, strong) RNNReactComponentManager* componentManager; +@end + @implementation RNNBasePresenter +- (instancetype)initWithComponentManager:(RNNReactComponentManager *)componentManager { + self = [super init]; + self.componentManager = componentManager; + return self; +} + - (void)bindViewController:(UIViewController *)bindedViewController { _bindedViewController = bindedViewController; } diff --git a/lib/ios/RNNBridgeManager.m b/lib/ios/RNNBridgeManager.m index 3184835ad21..d9386c6e5ca 100644 --- a/lib/ios/RNNBridgeManager.m +++ b/lib/ios/RNNBridgeManager.m @@ -8,11 +8,13 @@ #import "RNNBridgeModule.h" #import "RNNRootViewCreator.h" #import "RNNReactRootViewCreator.h" +#import "RNNReactComponentManager.h" @interface RNNBridgeManager() @property (nonatomic, strong, readwrite) RCTBridge *bridge; @property (nonatomic, strong, readwrite) RNNStore *store; +@property (nonatomic, strong, readwrite) RNNReactComponentManager *componentManager; @end @@ -77,7 +79,8 @@ - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { RNNEventEmitter *eventEmitter = [[RNNEventEmitter alloc] init]; id rootViewCreator = [[RNNReactRootViewCreator alloc] initWithBridge:bridge]; - RNNControllerFactory *controllerFactory = [[RNNControllerFactory alloc] initWithRootViewCreator:rootViewCreator eventEmitter:eventEmitter andBridge:bridge]; + _componentManager = [[RNNReactComponentManager alloc] initWithCreator:rootViewCreator]; + RNNControllerFactory *controllerFactory = [[RNNControllerFactory alloc] initWithRootViewCreator:rootViewCreator eventEmitter:eventEmitter store:_store componentManager:_componentManager andBridge:bridge]; _commandsHandler = [[RNNCommandsHandler alloc] initWithStore:_store controllerFactory:controllerFactory eventEmitter:eventEmitter stackManager:[RNNNavigationStackManager new] modalManager:[RNNModalManager new] overlayManager:[RNNOverlayManager new] mainWindow:_mainWindow]; RNNBridgeModule *bridgeModule = [[RNNBridgeModule alloc] initWithCommandsHandler:_commandsHandler]; @@ -89,6 +92,7 @@ - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { - (void)onJavaScriptWillLoad { [_store clean]; + [_componentManager clean]; } - (void)onJavaScriptLoaded { diff --git a/lib/ios/RNNCommandsHandler.m b/lib/ios/RNNCommandsHandler.m index d9d47de4cda..63cc759eccd 100644 --- a/lib/ios/RNNCommandsHandler.m +++ b/lib/ios/RNNCommandsHandler.m @@ -58,12 +58,13 @@ - (void)setRoot:(NSDictionary*)layout completion:(RNNTransitionCompletionBlock)c [_modalManager dismissAllModalsAnimated:NO]; [_store removeAllComponentsFromWindow:_mainWindow]; - UIViewController *vc = [_controllerFactory createLayout:layout[@"root"] saveToStore:_store]; + UIViewController *vc = [_controllerFactory createLayout:layout[@"root"]]; - _mainWindow.rootViewController = vc; - - [_eventEmitter sendOnNavigationCommandCompletion:setRoot params:@{@"layout": layout}]; - completion(); + [vc renderTreeAndWait:[vc.resolveOptions.animations.setRoot.waitForRender getWithDefaultValue:NO] perform:^{ + _mainWindow.rootViewController = vc; + [_eventEmitter sendOnNavigationCommandCompletion:setRoot params:@{@"layout": layout}]; + completion() ; + }]; } - (void)mergeOptions:(NSString*)componentId options:(NSDictionary*)mergeOptions completion:(RNNTransitionCompletionBlock)completion { @@ -96,7 +97,7 @@ - (void)setDefaultOptions:(NSDictionary*)optionsDict completion:(RNNTransitionCo - (void)push:(NSString*)componentId layout:(NSDictionary*)layout completion:(RNNTransitionCompletionBlock)completion rejection:(RCTPromiseRejectBlock)rejection { [self assertReady]; - UIViewController *newVc = [_controllerFactory createLayout:layout saveToStore:_store]; + UIViewController *newVc = [_controllerFactory createLayout:layout]; UIViewController *fromVC = [_store findComponentForId:componentId]; if ([[newVc.resolveOptions.preview.reactTag getWithDefaultValue:@(0)] floatValue] > 0) { @@ -141,8 +142,8 @@ - (void)push:(NSString*)componentId layout:(NSDictionary*)layout completion:(RNN } } else { id animationDelegate = (newVc.resolveOptions.animations.push.hasCustomAnimation || newVc.getCurrentLeaf.isCustomTransitioned) ? newVc : nil; - [newVc.getCurrentLeaf waitForReactViewRender:(newVc.resolveOptions.animations.push.waitForRender || animationDelegate) perform:^{ - [_stackManager push:newVc onTop:fromVC animated:newVc.resolveOptions.animations.push.enable animationDelegate:animationDelegate completion:^{ + [newVc renderTreeAndWait:([newVc.resolveOptions.animations.push.waitForRender getWithDefaultValue:NO] || animationDelegate) perform:^{ + [_stackManager push:newVc onTop:fromVC animated:[newVc.resolveOptions.animations.push.enable getWithDefaultValue:YES] animationDelegate:animationDelegate completion:^{ [_eventEmitter sendOnNavigationCommandCompletion:push params:@{@"componentId": componentId}]; completion(); } rejection:rejection]; @@ -153,11 +154,14 @@ - (void)push:(NSString*)componentId layout:(NSDictionary*)layout completion:(RNN - (void)setStackRoot:(NSString*)componentId children:(NSArray*)children completion:(RNNTransitionCompletionBlock)completion rejection:(RCTPromiseRejectBlock)rejection { [self assertReady]; - NSArray *childViewControllers = [_controllerFactory createChildrenLayout:children saveToStore:_store]; + NSArray *childViewControllers = [_controllerFactory createChildrenLayout:children]; + for (UIViewController* viewController in childViewControllers) { + [viewController renderTreeAndWait:NO perform:nil]; + } RNNNavigationOptions* options = [childViewControllers.lastObject getCurrentChild].resolveOptions; UIViewController *fromVC = [_store findComponentForId:componentId]; __weak typeof(RNNEventEmitter*) weakEventEmitter = _eventEmitter; - [_stackManager setStackChildren:childViewControllers fromViewController:fromVC animated:options.animations.setStackRoot.enable completion:^{ + [_stackManager setStackChildren:childViewControllers fromViewController:fromVC animated:[options.animations.setStackRoot.enable getWithDefaultValue:YES] completion:^{ [weakEventEmitter sendOnNavigationCommandCompletion:setStackRoot params:@{@"componentId": componentId}]; completion(); } rejection:rejection]; @@ -181,10 +185,10 @@ - (void)pop:(NSString*)componentId mergeOptions:(NSDictionary*)mergeOptions comp } else { NSMutableArray * vcs = nvc.viewControllers.mutableCopy; [vcs removeObject:vc]; - [nvc setViewControllers:vcs animated:vc.resolveOptions.animations.pop.enable]; + [nvc setViewControllers:vcs animated:[vc.resolveOptions.animations.pop.enable getWithDefaultValue:YES]]; } - [_stackManager pop:vc animated:vc.resolveOptions.animations.pop.enable completion:^{ + [_stackManager pop:vc animated:[vc.resolveOptions.animations.pop.enable getWithDefaultValue:YES] completion:^{ [_store removeComponent:componentId]; [_eventEmitter sendOnNavigationCommandCompletion:pop params:@{@"componentId": componentId}]; completion(); @@ -197,7 +201,7 @@ - (void)popTo:(NSString*)componentId mergeOptions:(NSDictionary *)mergeOptions c RNNNavigationOptions *options = [[RNNNavigationOptions alloc] initWithDict:mergeOptions]; [vc overrideOptions:options]; - [_stackManager popTo:vc animated:vc.resolveOptions.animations.pop.enable completion:^(NSArray *poppedViewControllers) { + [_stackManager popTo:vc animated:[vc.resolveOptions.animations.pop.enable getWithDefaultValue:YES] completion:^(NSArray *poppedViewControllers) { [_eventEmitter sendOnNavigationCommandCompletion:popTo params:@{@"componentId": componentId}]; [self removePopedViewControllers:poppedViewControllers]; completion(); @@ -216,7 +220,7 @@ - (void)popToRoot:(NSString*)componentId mergeOptions:(NSDictionary *)mergeOptio completion(); }]; - [_stackManager popToRoot:vc animated:vc.resolveOptions.animations.pop.enable completion:^(NSArray *poppedViewControllers) { + [_stackManager popToRoot:vc animated:[vc.resolveOptions.animations.pop.enable getWithDefaultValue:YES] completion:^(NSArray *poppedViewControllers) { [self removePopedViewControllers:poppedViewControllers]; } rejection:^(NSString *code, NSString *message, NSError *error) { @@ -228,10 +232,10 @@ - (void)popToRoot:(NSString*)componentId mergeOptions:(NSDictionary *)mergeOptio - (void)showModal:(NSDictionary*)layout completion:(RNNTransitionWithComponentIdCompletionBlock)completion { [self assertReady]; - UIViewController *newVc = [_controllerFactory createLayout:layout saveToStore:_store]; + UIViewController *newVc = [_controllerFactory createLayout:layout]; - [newVc.getCurrentLeaf waitForReactViewRender:newVc.getCurrentLeaf.resolveOptions.animations.showModal.waitForRender perform:^{ - [_modalManager showModal:newVc animated:newVc.getCurrentChild.resolveOptions.animations.showModal.enable hasCustomAnimation:newVc.getCurrentChild.resolveOptions.animations.showModal.hasCustomAnimation completion:^(NSString *componentId) { + [newVc renderTreeAndWait:[newVc.getCurrentLeaf.resolveOptions.animations.showModal.waitForRender getWithDefaultValue:NO] perform:^{ + [_modalManager showModal:newVc animated:[newVc.getCurrentChild.resolveOptions.animations.showModal.enable getWithDefaultValue:YES] hasCustomAnimation:newVc.getCurrentChild.resolveOptions.animations.showModal.hasCustomAnimation completion:^(NSString *componentId) { [_eventEmitter sendOnNavigationCommandCompletion:showModal params:@{@"layout": layout}]; completion(componentId); }]; @@ -272,7 +276,7 @@ - (void)dismissAllModals:(NSDictionary *)mergeOptions completion:(RNNTransitionC completion(); }]; RNNNavigationOptions* options = [[RNNNavigationOptions alloc] initWithDict:mergeOptions]; - [_modalManager dismissAllModalsAnimated:options.animations.dismissModal.enable]; + [_modalManager dismissAllModalsAnimated:[options.animations.dismissModal.enable getWithDefaultValue:YES]]; [CATransaction commit]; } @@ -280,12 +284,14 @@ - (void)dismissAllModals:(NSDictionary *)mergeOptions completion:(RNNTransitionC - (void)showOverlay:(NSDictionary *)layout completion:(RNNTransitionCompletionBlock)completion { [self assertReady]; - UIViewController* overlayVC = [_controllerFactory createLayout:layout saveToStore:_store]; - UIWindow* overlayWindow = [[RNNOverlayWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; - overlayWindow.rootViewController = overlayVC; - [_overlayManager showOverlayWindow:overlayWindow]; - [_eventEmitter sendOnNavigationCommandCompletion:showOverlay params:@{@"layout": layout}]; - completion(); + UIViewController* overlayVC = [_controllerFactory createLayout:layout]; + [overlayVC renderTreeAndWait:[overlayVC.options.animations.showOverlay.waitForRender getWithDefaultValue:NO] perform:^{ + UIWindow* overlayWindow = [[RNNOverlayWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + overlayWindow.rootViewController = overlayVC; + [_overlayManager showOverlayWindow:overlayWindow]; + [_eventEmitter sendOnNavigationCommandCompletion:showOverlay params:@{@"layout": layout}]; + completion(); + }]; } - (void)dismissOverlay:(NSString*)componentId completion:(RNNTransitionCompletionBlock)completion rejection:(RNNTransitionRejectionBlock)reject { diff --git a/lib/ios/RNNComponentOptions.h b/lib/ios/RNNComponentOptions.h index 658e4a37ca6..4826aed2369 100644 --- a/lib/ios/RNNComponentOptions.h +++ b/lib/ios/RNNComponentOptions.h @@ -5,5 +5,6 @@ @property (nonatomic, strong) Text* name; @property (nonatomic, strong) Text* componentId; @property (nonatomic, strong) Text* alignment; +@property (nonatomic, strong) Bool* waitForRender; @end diff --git a/lib/ios/RNNComponentOptions.m b/lib/ios/RNNComponentOptions.m index bc39e83a99f..0dd8e042dd1 100644 --- a/lib/ios/RNNComponentOptions.m +++ b/lib/ios/RNNComponentOptions.m @@ -8,6 +8,7 @@ - (instancetype)initWithDict:(NSDictionary *)dict { self.name = [TextParser parse:dict key:@"name"]; self.componentId = [TextParser parse:dict key:@"componentId"]; self.alignment = [TextParser parse:dict key:@"alignment"]; + self.waitForRender = [BoolParser parse:dict key:@"waitForRender"]; return self; } diff --git a/lib/ios/RNNControllerFactory.h b/lib/ios/RNNControllerFactory.h index c025881c1bf..2138b3291a8 100644 --- a/lib/ios/RNNControllerFactory.h +++ b/lib/ios/RNNControllerFactory.h @@ -5,16 +5,19 @@ #import "RNNStore.h" #import "RNNEventEmitter.h" #import "RNNParentProtocol.h" +#import "RNNReactComponentManager.h" @interface RNNControllerFactory : NSObject -(instancetype)initWithRootViewCreator:(id )creator eventEmitter:(RNNEventEmitter*)eventEmitter + store:(RNNStore *)store + componentManager:(RNNReactComponentManager *)componentManager andBridge:(RCTBridge*)bridge; -- (UIViewController *)createLayout:(NSDictionary*)layout saveToStore:(RNNStore *)store; +- (UIViewController *)createLayout:(NSDictionary*)layout; -- (NSArray *)createChildrenLayout:(NSArray*)children saveToStore:(RNNStore *)store; +- (NSArray *)createChildrenLayout:(NSArray*)children; @property (nonatomic, strong) RNNEventEmitter *eventEmitter; diff --git a/lib/ios/RNNControllerFactory.m b/lib/ios/RNNControllerFactory.m index 54c18e79fe1..916bc47dd9a 100644 --- a/lib/ios/RNNControllerFactory.m +++ b/lib/ios/RNNControllerFactory.m @@ -20,6 +20,7 @@ @implementation RNNControllerFactory { id _creator; RNNStore *_store; RCTBridge *_bridge; + RNNReactComponentManager* _componentManager; } # pragma mark public @@ -27,6 +28,8 @@ @implementation RNNControllerFactory { - (instancetype)initWithRootViewCreator:(id )creator eventEmitter:(RNNEventEmitter*)eventEmitter + store:(RNNStore *)store + componentManager:(RNNReactComponentManager *)componentManager andBridge:(RCTBridge *)bridge { self = [super init]; @@ -34,24 +37,22 @@ - (instancetype)initWithRootViewCreator:(id )creator _creator = creator; _eventEmitter = eventEmitter; _bridge = bridge; + _store = store; + _componentManager = componentManager; return self; } -- (UIViewController *)createLayout:(NSDictionary*)layout saveToStore:(RNNStore *)store { - _store = store; +- (UIViewController *)createLayout:(NSDictionary*)layout { UIViewController* layoutViewController = [self fromTree:layout]; - _store = nil; return layoutViewController; } -- (NSArray *)createChildrenLayout:(NSArray*)children saveToStore:(RNNStore *)store { - _store = store; +- (NSArray *)createChildrenLayout:(NSArray*)children { NSMutableArray* childViewControllers = [NSMutableArray new]; for (NSDictionary* layout in children) { [childViewControllers addObject:[self fromTree:layout]]; } - _store = nil; return childViewControllers; } @@ -114,17 +115,10 @@ - (instancetype)initWithRootViewCreator:(id )creator - (UIViewController *)createComponent:(RNNLayoutNode*)node { RNNLayoutInfo* layoutInfo = [[RNNLayoutInfo alloc] initWithNode:node]; RNNNavigationOptions* options = [[RNNNavigationOptions alloc] initWithDict:node.data[@"options"]];; - - RNNViewControllerPresenter* presenter = [[RNNViewControllerPresenter alloc] init]; - + RNNViewControllerPresenter* presenter = [[RNNViewControllerPresenter alloc] initWithComponentManager:_componentManager]; RNNRootViewController* component = [[RNNRootViewController alloc] initWithLayoutInfo:layoutInfo rootViewCreator:_creator eventEmitter:_eventEmitter presenter:presenter options:options defaultOptions:_defaultOptions]; - if (!component.isExternalViewController) { - CGSize availableSize = UIApplication.sharedApplication.delegate.window.bounds.size; - [_bridge.uiManager setAvailableSize:availableSize forRootView:component.view]; - } - return (UIViewController *)component; } @@ -143,14 +137,13 @@ - (instancetype)initWithRootViewCreator:(id )creator - (UIViewController *)createStack:(RNNLayoutNode*)node { - RNNNavigationControllerPresenter* presenter = [[RNNNavigationControllerPresenter alloc] init]; - + RNNNavigationControllerPresenter* presenter = [[RNNNavigationControllerPresenter alloc] initWithComponentManager:_componentManager]; RNNLayoutInfo* layoutInfo = [[RNNLayoutInfo alloc] initWithNode:node]; RNNNavigationOptions* options = [[RNNNavigationOptions alloc] initWithDict:node.data[@"options"]];; NSArray *childViewControllers = [self extractChildrenViewControllersFromNode:node]; - RNNNavigationController* stack = [[RNNNavigationController alloc] initWithLayoutInfo:layoutInfo childViewControllers:childViewControllers options:options defaultOptions:_defaultOptions presenter:presenter]; + RNNNavigationController* stack = [[RNNNavigationController alloc] initWithLayoutInfo:layoutInfo creator:_creator childViewControllers:childViewControllers options:options defaultOptions:_defaultOptions presenter:presenter]; return stack; } diff --git a/lib/ios/RNNLayoutProtocol.h b/lib/ios/RNNLayoutProtocol.h index 24be7d6c122..dc3956a16ab 100644 --- a/lib/ios/RNNLayoutProtocol.h +++ b/lib/ios/RNNLayoutProtocol.h @@ -1,6 +1,8 @@ #import "RNNLayoutInfo.h" -#import "RNNViewControllerPresenter.h" #import "RNNLeafProtocol.h" +#import "RNNBasePresenter.h" + +typedef void (^RNNReactViewReadyCompletionBlock)(void); @protocol RNNLayoutProtocol @@ -11,6 +13,8 @@ @property (nonatomic, strong) RNNNavigationOptions* options; @property (nonatomic, strong) RNNNavigationOptions* defaultOptions; +- (void)renderTreeAndWait:(BOOL)wait perform:(RNNReactViewReadyCompletionBlock)readyBlock; + - (UIViewController *)getCurrentChild; - (UIViewController *)getCurrentLeaf; diff --git a/lib/ios/RNNLeafProtocol.h b/lib/ios/RNNLeafProtocol.h index 145fc2da335..e7875dcd963 100644 --- a/lib/ios/RNNLeafProtocol.h +++ b/lib/ios/RNNLeafProtocol.h @@ -1,15 +1,8 @@ -#import "RNNRootViewCreator.h" - -typedef void (^RNNReactViewReadyCompletionBlock)(void); @protocol RNNLeafProtocol -- (void)waitForReactViewRender:(BOOL)wait perform:(RNNReactViewReadyCompletionBlock)readyBlock; - - (void)bindViewController:(UIViewController *)viewController; - (BOOL)isCustomTransitioned; -- (id)creator; - @end diff --git a/lib/ios/RNNModalManager.m b/lib/ios/RNNModalManager.m index cfcfdb0b020..bedf8ae2173 100644 --- a/lib/ios/RNNModalManager.m +++ b/lib/ios/RNNModalManager.m @@ -73,7 +73,7 @@ -(void)removePendingNextModalIfOnTop:(RNNTransitionCompletionBlock)completion { } if (modalToDismiss == topPresentedVC || [[topPresentedVC childViewControllers] containsObject:modalToDismiss]) { - [modalToDismiss dismissViewControllerAnimated:options.animations.dismissModal.enable completion:^{ + [modalToDismiss dismissViewControllerAnimated:[options.animations.dismissModal.enable getWithDefaultValue:YES] completion:^{ [_pendingModalIdsToDismiss removeObject:modalToDismiss]; if (modalToDismiss.view) { [self dismissedModal:modalToDismiss]; @@ -88,7 +88,7 @@ -(void)removePendingNextModalIfOnTop:(RNNTransitionCompletionBlock)completion { } else { [modalToDismiss.view removeFromSuperview]; modalToDismiss.view = nil; - modalToDismiss.getCurrentChild.resolveOptions.animations.dismissModal.enable = NO; + modalToDismiss.getCurrentChild.resolveOptions.animations.dismissModal.enable = [[Bool alloc] initWithBOOL:NO]; [self dismissedModal:modalToDismiss]; if (completion) { diff --git a/lib/ios/RNNNavigationButtons.h b/lib/ios/RNNNavigationButtons.h index 56882a4bde7..1fb87aefbea 100644 --- a/lib/ios/RNNNavigationButtons.h +++ b/lib/ios/RNNNavigationButtons.h @@ -2,10 +2,11 @@ #import #import "RNNButtonOptions.h" #import "RNNRootViewCreator.h" +#import "RNNReactComponentManager.h" @interface RNNNavigationButtons : NSObject --(instancetype)initWithViewController:(UIViewController*)viewController rootViewCreator:(id)creator; +-(instancetype)initWithViewController:(UIViewController*)viewController componentManager:(RNNReactComponentManager *)componentManager; -(void)applyLeftButtons:(NSArray*)leftButtons rightButtons:(NSArray*)rightButtons defaultLeftButtonStyle:(RNNButtonOptions *)defaultLeftButtonStyle defaultRightButtonStyle:(RNNButtonOptions *)defaultRightButtonStyle; diff --git a/lib/ios/RNNNavigationButtons.m b/lib/ios/RNNNavigationButtons.m index 37604d19f0f..15f3f7e306d 100644 --- a/lib/ios/RNNNavigationButtons.m +++ b/lib/ios/RNNNavigationButtons.m @@ -7,19 +7,19 @@ @interface RNNNavigationButtons() -@property (weak, nonatomic) UIViewController* viewController; +@property (weak, nonatomic) UIViewController* viewController; @property (strong, nonatomic) RNNButtonOptions* defaultLeftButtonStyle; @property (strong, nonatomic) RNNButtonOptions* defaultRightButtonStyle; -@property (nonatomic) id creator; +@property (strong, nonatomic) RNNReactComponentManager* componentManager; @end @implementation RNNNavigationButtons --(instancetype)initWithViewController:(UIViewController*)viewController rootViewCreator:(id)creator { +-(instancetype)initWithViewController:(UIViewController*)viewController componentManager:(id)componentManager { self = [super init]; self.viewController = viewController; - self.creator = creator; + self.componentManager = componentManager; return self; } @@ -85,7 +85,11 @@ -(RNNUIBarButtonItem*)buildButton: (NSDictionary*)dictionary defaultStyle:(RNNBu RNNUIBarButtonItem *barButtonItem; if (component) { - RCTRootView *view = (RCTRootView*)[self.creator createCustomReactView:component[@"name"] rootViewId:component[@"componentId"]]; + RNNComponentOptions* componentOptions = [RNNComponentOptions new]; + componentOptions.componentId = [[Text alloc] initWithValue:component[@"componentId"]]; + componentOptions.name = [[Text alloc] initWithValue:component[@"name"]]; + + RNNReactView *view = [_componentManager createComponentIfNotExists:componentOptions parentComponentId:self.viewController.layoutInfo.componentId reactViewReadyBlock:nil]; barButtonItem = [[RNNUIBarButtonItem alloc] init:buttonId withCustomView:view]; } else if (iconImage) { barButtonItem = [[RNNUIBarButtonItem alloc] init:buttonId withIcon:iconImage]; diff --git a/lib/ios/RNNNavigationController.h b/lib/ios/RNNNavigationController.h index 9478a1cef8c..2ac9fb095a2 100644 --- a/lib/ios/RNNNavigationController.h +++ b/lib/ios/RNNNavigationController.h @@ -10,6 +10,9 @@ @property (nonatomic, strong) RNNNavigationOptions* options; @property (nonatomic, strong) RNNNavigationOptions* defaultOptions; + +- (instancetype)initWithLayoutInfo:(RNNLayoutInfo *)layoutInfo creator:(id)creator childViewControllers:(NSArray *)childViewControllers options:(RNNNavigationOptions *)options defaultOptions:(RNNNavigationOptions *)defaultOptions presenter:(RNNBasePresenter *)presenter; + - (void)setTopBarBackgroundColor:(UIColor *)backgroundColor; @end diff --git a/lib/ios/RNNNavigationController.m b/lib/ios/RNNNavigationController.m index 9bcfbd8051f..bf711d15624 100644 --- a/lib/ios/RNNNavigationController.m +++ b/lib/ios/RNNNavigationController.m @@ -13,7 +13,7 @@ @interface RNNNavigationController() @implementation RNNNavigationController -- (instancetype)initWithLayoutInfo:(RNNLayoutInfo *)layoutInfo childViewControllers:(NSArray *)childViewControllers options:(RNNNavigationOptions *)options defaultOptions:(RNNNavigationOptions *)defaultOptions presenter:(RNNNavigationControllerPresenter *)presenter { +- (instancetype)initWithLayoutInfo:(RNNLayoutInfo *)layoutInfo creator:(id)creator childViewControllers:(NSArray *)childViewControllers options:(RNNNavigationOptions *)options defaultOptions:(RNNNavigationOptions *)defaultOptions presenter:(RNNNavigationControllerPresenter *)presenter { self = [super init]; self.presenter = presenter; @@ -37,6 +37,7 @@ - (void)willMoveToParentViewController:(UIViewController *)parent { - (void)onChildWillAppear { [_presenter applyOptions:self.resolveOptions]; + [_presenter renderComponents:self.resolveOptions perform:nil]; [((UIViewController *)self.parentViewController) onChildWillAppear]; } @@ -53,6 +54,30 @@ - (void)overrideOptions:(RNNNavigationOptions *)options { [self.options overrideOptions:options]; } +- (void)renderTreeAndWait:(BOOL)wait perform:(RNNReactViewReadyCompletionBlock)readyBlock { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ + dispatch_group_t group = dispatch_group_create(); + for (UIViewController* childViewController in self.childViewControllers) { + dispatch_group_enter(group); + dispatch_async(dispatch_get_main_queue(), ^{ + [childViewController renderTreeAndWait:wait perform:^{ + dispatch_group_leave(group); + }]; + }); + } + + dispatch_group_enter(group); + [self.presenter renderComponents:self.resolveOptions perform:^{ + dispatch_group_leave(group); + }]; + dispatch_group_wait(group, DISPATCH_TIME_FOREVER); + + dispatch_async(dispatch_get_main_queue(), ^{ + readyBlock(); + }); + }); +} + - (UIInterfaceOrientationMask)supportedInterfaceOrientations { return self.getCurrentChild.supportedInterfaceOrientations; } diff --git a/lib/ios/RNNNavigationControllerPresenter.h b/lib/ios/RNNNavigationControllerPresenter.h index a47143b2c54..18a473709a3 100644 --- a/lib/ios/RNNNavigationControllerPresenter.h +++ b/lib/ios/RNNNavigationControllerPresenter.h @@ -1,7 +1,13 @@ #import "RNNBasePresenter.h" +#import "RNNRootViewCreator.h" +#import "RNNReactComponentManager.h" @interface RNNNavigationControllerPresenter : RNNBasePresenter +- (instancetype)initWithComponentManager:(RNNReactComponentManager *)componentManager; + - (void)applyOptionsBeforePopping:(RNNNavigationOptions *)options; +- (void)renderComponents:(RNNNavigationOptions *)options perform:(RNNReactViewReadyCompletionBlock)readyBlock; + @end diff --git a/lib/ios/RNNNavigationControllerPresenter.m b/lib/ios/RNNNavigationControllerPresenter.m index 0961833feab..9498497fc69 100644 --- a/lib/ios/RNNNavigationControllerPresenter.m +++ b/lib/ios/RNNNavigationControllerPresenter.m @@ -2,9 +2,27 @@ #import "UINavigationController+RNNOptions.h" #import "RNNNavigationController.h" #import +#import "RNNCustomTitleView.h" +@interface RNNNavigationControllerPresenter() { + RNNReactComponentManager* _componentManager; + UIView* _customTopBar; + UIView* _customTopBarBackground; +} + +@end @implementation RNNNavigationControllerPresenter +- (instancetype)initWithComponentManager:(RNNReactComponentManager *)componentManager { + self = [super init]; + _componentManager = componentManager; + return self; +} + +- (void)bindViewController:(UIViewController *)bindedViewController { + self.bindedViewController = bindedViewController; +} + - (void)applyOptions:(RNNNavigationOptions *)options { [super applyOptions:options]; @@ -112,6 +130,86 @@ - (void)mergeOptions:(RNNNavigationOptions *)newOptions currentOptions:(RNNNavig [navigationController rnn_setNavigationBarFontFamily:[newOptions.topBar.title.fontFamily getWithDefaultValue:nil] fontSize:[newOptions.topBar.title.fontSize getWithDefaultValue:nil] color:[newOptions.topBar.title.color getWithDefaultValue:nil]]; + if (newOptions.topBar.component.name.hasValue) { + [self setCustomNavigationBarView:newOptions perform:nil]; + } + + if (newOptions.topBar.background.component.name.hasValue) { + [self setCustomNavigationComponentBackground:newOptions perform:nil]; + } +} + +- (void)renderComponents:(RNNNavigationOptions *)options perform:(RNNReactViewReadyCompletionBlock)readyBlock { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ + dispatch_group_t group = dispatch_group_create(); + + dispatch_group_enter(group); + dispatch_async(dispatch_get_main_queue(), ^{ + [self setCustomNavigationBarView:options perform:^{ + dispatch_group_leave(group); + }]; + }); + + dispatch_group_enter(group); + dispatch_async(dispatch_get_main_queue(), ^{ + [self setCustomNavigationComponentBackground:options perform:^{ + dispatch_group_leave(group); + }]; + }); + + dispatch_group_wait(group, DISPATCH_TIME_FOREVER); + + dispatch_async(dispatch_get_main_queue(), ^{ + if (readyBlock) { + readyBlock(); + } + }); + }); +} + +- (void)setCustomNavigationBarView:(RNNNavigationOptions *)options perform:(RNNReactViewReadyCompletionBlock)readyBlock { + RNNNavigationController* navigationController = self.bindedViewController; + if (![options.topBar.component.waitForRender getWithDefaultValue:NO] && readyBlock) { + readyBlock(); + readyBlock = nil; + } + if (options.topBar.component.name.hasValue) { + RCTRootView *reactView = [_componentManager createComponentIfNotExists:options.topBar.component parentComponentId:navigationController.layoutInfo.componentId reactViewReadyBlock:readyBlock]; + + _customTopBar = [[RNNCustomTitleView alloc] initWithFrame:navigationController.navigationBar.bounds subView:reactView alignment:@"fill"]; + reactView.backgroundColor = UIColor.clearColor; + _customTopBar.backgroundColor = UIColor.clearColor; + [navigationController.navigationBar addSubview:_customTopBar]; + } else { + [_customTopBar removeFromSuperview]; + if (readyBlock) { + readyBlock(); + } + } +} + +- (void)setCustomNavigationComponentBackground:(RNNNavigationOptions *)options perform:(RNNReactViewReadyCompletionBlock)readyBlock { + RNNNavigationController* navigationController = self.bindedViewController; + if (![options.topBar.background.component.waitForRender getWithDefaultValue:NO] && readyBlock) { + readyBlock(); + readyBlock = nil; + } + if (options.topBar.background.component.name.hasValue) { + RCTRootView *reactView = [_componentManager createComponentIfNotExists:options.topBar.background.component parentComponentId:navigationController.layoutInfo.componentId reactViewReadyBlock:readyBlock]; + + _customTopBarBackground = [[RNNCustomTitleView alloc] initWithFrame:navigationController.navigationBar.bounds subView:reactView alignment:@"fill"]; + [navigationController.navigationBar insertSubview:_customTopBarBackground atIndex:1]; + } else { + [_customTopBarBackground removeFromSuperview]; + if (readyBlock) { + readyBlock(); + } + } +} + +- (void)dealloc { + RNNNavigationController* navigationController = self.bindedViewController; + [_componentManager removeComponent:navigationController.layoutInfo.componentId]; } @end diff --git a/lib/ios/RNNParentProtocol.h b/lib/ios/RNNParentProtocol.h index d0bd661cb14..2e08597ad5c 100644 --- a/lib/ios/RNNParentProtocol.h +++ b/lib/ios/RNNParentProtocol.h @@ -3,10 +3,11 @@ @protocol RNNParentProtocol -@required +@optional - (instancetype)initWithLayoutInfo:(RNNLayoutInfo *)layoutInfo childViewControllers:(NSArray *)childViewControllers options:(RNNNavigationOptions *)options defaultOptions:(RNNNavigationOptions *)defaultOptions presenter:(RNNBasePresenter *)presenter; +@required - (void)onChildWillAppear; @end diff --git a/lib/ios/RNNReactComponentManager.h b/lib/ios/RNNReactComponentManager.h new file mode 100644 index 00000000000..241e434e11e --- /dev/null +++ b/lib/ios/RNNReactComponentManager.h @@ -0,0 +1,17 @@ +#import +#import "RNNReactView.h" +#import "RNNComponentOptions.h" +#import "RNNStore.h" +#import "RNNRootViewCreator.h" + +@interface RNNReactComponentManager : NSObject + +- (instancetype)initWithCreator:(id)creator; + +- (RNNReactView *)createComponentIfNotExists:(RNNComponentOptions *)component parentComponentId:(NSString *)parentComponentId reactViewReadyBlock:(RNNReactViewReadyCompletionBlock)reactViewReadyBlock; + +- (void)removeComponent:(NSString *)componentId; + +- (void)clean; + +@end diff --git a/lib/ios/RNNReactComponentManager.m b/lib/ios/RNNReactComponentManager.m new file mode 100644 index 00000000000..0c218c8bc19 --- /dev/null +++ b/lib/ios/RNNReactComponentManager.m @@ -0,0 +1,52 @@ +#import "RNNReactComponentManager.h" + +@interface RNNReactComponentManager () { + id _creator; + NSMutableDictionary* _componentStore; +} + +@end + +@implementation RNNReactComponentManager + +- (instancetype)initWithCreator:(id)creator { + self = [super init]; + _creator = creator; + _componentStore = [NSMutableDictionary new]; + return self; +} + +- (RNNReactView *)createComponentIfNotExists:(RNNComponentOptions *)component parentComponentId:(NSString *)parentComponentId reactViewReadyBlock:(RNNReactViewReadyCompletionBlock)reactViewReadyBlock { + NSMutableDictionary* parentComponentDict = [self componentsForParentId:parentComponentId]; + + RNNReactView* reactView = [parentComponentDict objectForKey:component.componentId.get]; + if (!reactView) { + reactView = (RNNReactView *)[_creator createRootViewFromComponentOptions:component reactViewReadyBlock:reactViewReadyBlock]; + [parentComponentDict setObject:reactView forKey:component.componentId.get]; + } else if (reactViewReadyBlock) { + reactViewReadyBlock(); + } + + return reactView; +} + +- (NSMutableDictionary *)componentsForParentId:(NSString *)parentComponentId { + if (!_componentStore[parentComponentId]) { + [_componentStore setObject:[NSMutableDictionary new] forKey:parentComponentId];; + } + + return [_componentStore objectForKey:parentComponentId];; +} + +- (void)removeComponent:(NSString *)componentId { + if ([_componentStore objectForKey:componentId]) { + [_componentStore removeObjectForKey:componentId]; + } +} + +- (void)clean { + [_componentStore removeAllObjects]; +} + + +@end diff --git a/lib/ios/RNNReactRootView.h b/lib/ios/RNNReactRootView.h deleted file mode 100644 index 6522eaf000f..00000000000 --- a/lib/ios/RNNReactRootView.h +++ /dev/null @@ -1,6 +0,0 @@ -#import -#import "RNNReactView.h" - -@interface RNNReactRootView : RNNReactView - -@end diff --git a/lib/ios/RNNReactRootView.m b/lib/ios/RNNReactRootView.m deleted file mode 100644 index d85898365be..00000000000 --- a/lib/ios/RNNReactRootView.m +++ /dev/null @@ -1,16 +0,0 @@ -#import "RNNReactRootView.h" -#import "RCTHelpers.h" -#import - -@implementation RNNReactRootView - -- (void)layoutSubviews { - [super layoutSubviews]; - [self.bridge.uiManager setSize:self.bounds.size forView:self]; -} - -- (void)contentDidAppear:(NSNotification *)notification { - -} - -@end diff --git a/lib/ios/RNNReactRootViewCreator.m b/lib/ios/RNNReactRootViewCreator.m index 13b2fd1333d..a931849a5b0 100644 --- a/lib/ios/RNNReactRootViewCreator.m +++ b/lib/ios/RNNReactRootViewCreator.m @@ -1,45 +1,37 @@ #import "RNNReactRootViewCreator.h" -#import "RNNReactRootView.h" #import "RNNReactView.h" @implementation RNNReactRootViewCreator { RCTBridge *_bridge; } --(instancetype)initWithBridge:(RCTBridge*)bridge { +- (instancetype)initWithBridge:(RCTBridge*)bridge { self = [super init]; _bridge = bridge; return self; - } -- (UIView*)createRootView:(NSString*)name rootViewId:(NSString*)rootViewId { +- (RNNReactView*)createRootView:(NSString*)name rootViewId:(NSString*)rootViewId reactViewReadyBlock:(RNNReactViewReadyCompletionBlock)reactViewReadyBlock { if (!rootViewId) { @throw [NSException exceptionWithName:@"MissingViewId" reason:@"Missing view id" userInfo:nil]; } - UIView *view = [[RNNReactRootView alloc] initWithBridge:_bridge - moduleName:name - initialProperties:@{@"componentId": rootViewId}]; + RNNReactView *view = [[RNNReactView alloc] initWithBridge:_bridge + moduleName:name + initialProperties:@{@"componentId": rootViewId} + reactViewReadyBlock:reactViewReadyBlock]; return view; } -- (UIView*)createCustomReactView:(NSString*)name rootViewId:(NSString*)rootViewId { - if (!rootViewId) { - @throw [NSException exceptionWithName:@"MissingViewId" reason:@"Missing view id" userInfo:nil]; - } - - UIView *view = [[RNNReactView alloc] initWithBridge:_bridge - moduleName:name - initialProperties:@{@"componentId": rootViewId}]; - return view; +- (UIView*)createRootViewFromComponentOptions:(RNNComponentOptions*)componentOptions { + return [self createRootView:componentOptions.name.get rootViewId:componentOptions.componentId.get reactViewReadyBlock:nil]; } --(UIView*)createRootViewFromComponentOptions:(RNNComponentOptions*)componentOptions { - return [self createCustomReactView:componentOptions.name.get rootViewId:componentOptions.componentId.get]; +- (UIView*)createRootViewFromComponentOptions:(RNNComponentOptions*)componentOptions reactViewReadyBlock:(RNNReactViewReadyCompletionBlock)reactViewReadyBlock { + return [self createRootView:componentOptions.name.get rootViewId:componentOptions.componentId.get reactViewReadyBlock:reactViewReadyBlock]; } @end diff --git a/lib/ios/RNNReactView.h b/lib/ios/RNNReactView.h index 00db5a7fb53..f542517df91 100644 --- a/lib/ios/RNNReactView.h +++ b/lib/ios/RNNReactView.h @@ -1,9 +1,14 @@ #import #import +typedef void (^RNNReactViewReadyCompletionBlock)(void); + @interface RNNReactView : RCTRootView +- (instancetype)initWithBridge:(RCTBridge *)bridge moduleName:(NSString *)moduleName initialProperties:(NSDictionary *)initialProperties reactViewReadyBlock:(RNNReactViewReadyCompletionBlock)reactViewReadyBlock; + @property (nonatomic, copy) void (^rootViewDidChangeIntrinsicSize)(CGSize intrinsicSize); +@property (nonatomic, copy) RNNReactViewReadyCompletionBlock reactViewReadyBlock; - (void)setAlignment:(NSString *)alignment; diff --git a/lib/ios/RNNReactView.m b/lib/ios/RNNReactView.m index 035c0825975..6e6e97cc49a 100644 --- a/lib/ios/RNNReactView.m +++ b/lib/ios/RNNReactView.m @@ -1,23 +1,31 @@ #import "RNNReactView.h" #import "RCTHelpers.h" +#import @implementation RNNReactView -- (instancetype)initWithBridge:(RCTBridge *)bridge moduleName:(NSString *)moduleName initialProperties:(NSDictionary *)initialProperties { +- (instancetype)initWithBridge:(RCTBridge *)bridge moduleName:(NSString *)moduleName initialProperties:(NSDictionary *)initialProperties reactViewReadyBlock:(RNNReactViewReadyCompletionBlock)reactViewReadyBlock { self = [super initWithBridge:bridge moduleName:moduleName initialProperties:initialProperties]; - -#ifdef DEBUG [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contentDidAppear:) name:RCTContentDidAppearNotification object:nil]; -#endif + _reactViewReadyBlock = reactViewReadyBlock; return self; } - (void)contentDidAppear:(NSNotification *)notification { +#ifdef DEBUG if ([((RNNReactView *)notification.object).moduleName isEqualToString:self.moduleName]) { [RCTHelpers removeYellowBox:self]; - [[NSNotificationCenter defaultCenter] removeObserver:self]; } +#endif + + RNNReactView* appearedView = notification.object; + + if (_reactViewReadyBlock && [appearedView.appProperties[@"componentId"] isEqual:self.appProperties[@"componentId"]]) { + _reactViewReadyBlock(); + _reactViewReadyBlock = nil; + [[NSNotificationCenter defaultCenter] removeObserver:self]; + } } - (void)setRootViewDidChangeIntrinsicSize:(void (^)(CGSize))rootViewDidChangeIntrinsicSize { diff --git a/lib/ios/RNNRootViewController.m b/lib/ios/RNNRootViewController.m index 73ce9b48e68..1865f3fbfed 100644 --- a/lib/ios/RNNRootViewController.m +++ b/lib/ios/RNNRootViewController.m @@ -1,22 +1,10 @@ #import "RNNRootViewController.h" #import #import "RNNAnimator.h" -#import "RNNCustomTitleView.h" #import "RNNPushAnimation.h" #import "RNNReactView.h" #import "RNNParentProtocol.h" -#import "RNNTitleViewHelper.h" -@interface RNNRootViewController() { - RNNReactView* _customTitleView; - UIView* _customTopBar; - UIView* _customTopBarBackground; - BOOL _isBeingPresented; -} - -@property (nonatomic, copy) RNNReactViewReadyCompletionBlock reactViewReadyBlock; - -@end @implementation RNNRootViewController @@ -27,17 +15,10 @@ - (instancetype)initWithLayoutInfo:(RNNLayoutInfo *)layoutInfo rootViewCreator:( self.layoutInfo = layoutInfo; self.creator = creator; - if (self.creator) { - self.view = [creator createRootView:self.layoutInfo.name rootViewId:self.layoutInfo.componentId]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(reactViewReady) - name: @"RCTContentDidAppearNotification" - object:nil]; - } self.eventEmitter = eventEmitter; self.presenter = presenter; - [self.presenter bindViewController:self viewCreator:self.creator]; + [self.presenter bindViewController:self]; self.options = options; self.defaultOptions = defaultOptions; @@ -74,8 +55,6 @@ - (void)willMoveToParentViewController:(UIViewController *)parent { - (void)mergeOptions:(RNNNavigationOptions *)options { [_presenter mergeOptions:options currentOptions:self.options defaultOptions:self.defaultOptions]; [((UIViewController *)self.parentViewController) mergeOptions:options]; - - [self initCustomViews]; } - (void)overrideOptions:(RNNNavigationOptions *)options { @@ -84,12 +63,11 @@ - (void)overrideOptions:(RNNNavigationOptions *)options { - (void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; - _isBeingPresented = YES; [_presenter applyOptions:self.resolveOptions]; - [((UIViewController *)self.parentViewController) onChildWillAppear]; + [_presenter renderComponents:self.resolveOptions perform:nil]; - [self initCustomViews]; + [((UIViewController *)self.parentViewController) onChildWillAppear]; } - (RNNNavigationOptions *)resolveOptions { @@ -103,7 +81,6 @@ -(void)viewDidAppear:(BOOL)animated { - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; - _isBeingPresented = NO; } - (void)viewDidDisappear:(BOOL)animated { @@ -111,21 +88,29 @@ - (void)viewDidDisappear:(BOOL)animated { [self.eventEmitter sendComponentDidDisappear:self.layoutInfo.componentId componentName:self.layoutInfo.name]; } -- (void)reactViewReady { - if (_reactViewReadyBlock) { - _reactViewReadyBlock(); - _reactViewReadyBlock = nil; +- (void)renderTreeAndWait:(BOOL)wait perform:(RNNReactViewReadyCompletionBlock)readyBlock { + if (self.isExternalViewController) { + if (readyBlock) { + readyBlock(); + } + return; } - [[NSNotificationCenter defaultCenter] removeObserver:self name:@"RCTContentDidAppearNotification" object:nil]; -} - - -- (void)waitForReactViewRender:(BOOL)wait perform:(RNNReactViewReadyCompletionBlock)readyBlock { - if (wait && !self.isExternalViewController) { - [self onReactViewReady:readyBlock]; - } else { - readyBlock(); + __block RNNReactViewReadyCompletionBlock readyBlockCopy = readyBlock; + UIView* reactView = [_creator createRootView:self.layoutInfo.name rootViewId:self.layoutInfo.componentId reactViewReadyBlock:^{ + [_presenter renderComponents:self.resolveOptions perform:^{ + if (readyBlockCopy) { + readyBlockCopy(); + readyBlockCopy = nil; + } + }]; + }]; + reactView.backgroundColor = [UIColor clearColor]; + self.view = reactView; + + if (!wait && readyBlock) { + readyBlockCopy(); + readyBlockCopy = nil; } } @@ -137,14 +122,6 @@ - (UIViewController *)getCurrentChild { return self; } -- (void)onReactViewReady:(RNNReactViewReadyCompletionBlock)readyBlock { - if (self.isExternalViewController) { - readyBlock(); - } else { - self.reactViewReadyBlock = readyBlock; - } -} - -(void)updateSearchResultsForSearchController:(UISearchController *)searchController { [self.eventEmitter sendOnSearchBarUpdated:self.layoutInfo.componentId text:searchController.searchBar.text @@ -155,91 +132,6 @@ - (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar { [self.eventEmitter sendOnSearchBarCancelPressed:self.layoutInfo.componentId]; } -- (void)initCustomViews { - [self setCustomNavigationTitleView]; - [self setCustomNavigationBarView]; - [self setCustomNavigationComponentBackground]; - - if (!_customTitleView) { - [self setTitleViewWithSubtitle]; - } -} - -- (void)setTitleViewWithSubtitle { - if (self.resolveOptions.topBar.subtitle.text.hasValue) { - RNNTitleViewHelper* titleViewHelper = [[RNNTitleViewHelper alloc] initWithTitleViewOptions:self.resolveOptions.topBar.title subTitleOptions:self.resolveOptions.topBar.subtitle viewController:self]; - [titleViewHelper setup]; - } -} - -- (void)setCustomNavigationTitleView { - if (!_customTitleView && _isBeingPresented) { - if (self.resolveOptions.topBar.title.component.name.hasValue) { - _customTitleView = (RNNReactView*)[_creator createRootViewFromComponentOptions:self.resolveOptions.topBar.title.component]; - _customTitleView.backgroundColor = UIColor.clearColor; - NSString* alignment = [self.resolveOptions.topBar.title.component.alignment getWithDefaultValue:@""]; - [_customTitleView setAlignment:alignment]; - BOOL isCenter = [alignment isEqualToString:@"center"]; - __weak RNNReactView *weakTitleView = _customTitleView; - CGRect frame = self.navigationController.navigationBar.bounds; - [_customTitleView setFrame:frame]; - [_customTitleView setRootViewDidChangeIntrinsicSize:^(CGSize intrinsicContentSize) { - if (isCenter) { - [weakTitleView setFrame:CGRectMake(0, 0, intrinsicContentSize.width, intrinsicContentSize.height)]; - } else { - [weakTitleView setFrame:frame]; - } - }]; - - self.navigationItem.titleView = _customTitleView; - } - } else if (_customTitleView && _customTitleView.superview == nil) { - if ([self.navigationItem.title isKindOfClass:[RNNCustomTitleView class]] && !_customTitleView) { - self.navigationItem.title = nil; - } - self.navigationItem.titleView = _customTitleView; - } -} - -- (void)setCustomNavigationBarView { - if (!_customTopBar) { - if (self.resolveOptions.topBar.component.name.hasValue) { - RCTRootView *reactView = (RCTRootView*)[_creator createRootViewFromComponentOptions:self.resolveOptions.topBar.component]; - - _customTopBar = [[RNNCustomTitleView alloc] initWithFrame:self.navigationController.navigationBar.bounds subView:reactView alignment:@"fill"]; - reactView.backgroundColor = UIColor.clearColor; - _customTopBar.backgroundColor = UIColor.clearColor; - [self.navigationController.navigationBar addSubview:_customTopBar]; - } else if ([[self.navigationController.navigationBar.subviews lastObject] isKindOfClass:[RNNCustomTitleView class]] && !_customTopBar) { - [[self.navigationController.navigationBar.subviews lastObject] removeFromSuperview]; - } - } else if (_customTopBar && _customTopBar.superview == nil) { - if ([[self.navigationController.navigationBar.subviews lastObject] isKindOfClass:[RNNCustomTitleView class]] && !_customTopBar) { - [[self.navigationController.navigationBar.subviews lastObject] removeFromSuperview]; - } - [self.navigationController.navigationBar addSubview:_customTopBar]; - } -} - -- (void)setCustomNavigationComponentBackground { - if (!_customTopBarBackground) { - if (self.resolveOptions.topBar.background.component.name.hasValue) { - RCTRootView *reactView = (RCTRootView*)[_creator createRootViewFromComponentOptions:self.resolveOptions.topBar.background.component]; - - _customTopBarBackground = [[RNNCustomTitleView alloc] initWithFrame:self.navigationController.navigationBar.bounds subView:reactView alignment:@"fill"]; - [self.navigationController.navigationBar insertSubview:_customTopBarBackground atIndex:1]; - } else if (self.navigationController.navigationBar.subviews.count > 1 && [[self.navigationController.navigationBar.subviews objectAtIndex:1] isKindOfClass:[RNNCustomTitleView class]]) { - [[self.navigationController.navigationBar.subviews objectAtIndex:1] removeFromSuperview]; - } - } if (_customTopBarBackground && _customTopBarBackground.superview == nil) { - if (self.navigationController.navigationBar.subviews.count && [[self.navigationController.navigationBar.subviews objectAtIndex:1] isKindOfClass:[RNNCustomTitleView class]]) { - [[self.navigationController.navigationBar.subviews objectAtIndex:1] removeFromSuperview]; - } - [self.navigationController.navigationBar insertSubview:_customTopBarBackground atIndex:1]; - self.navigationController.navigationBar.clipsToBounds = YES; - } -} - -(BOOL)isCustomTransitioned { return self.resolveOptions.customTransition.animations != nil; } @@ -376,9 +268,6 @@ -(void)cleanReactLeftovers { self.navigationItem.titleView = nil; self.navigationItem.rightBarButtonItems = nil; self.navigationItem.leftBarButtonItems = nil; - _customTopBar = nil; - _customTitleView = nil; - _customTopBarBackground = nil; } @end diff --git a/lib/ios/RNNRootViewCreator.h b/lib/ios/RNNRootViewCreator.h index 95ce63062e6..e6c59e5e2d7 100644 --- a/lib/ios/RNNRootViewCreator.h +++ b/lib/ios/RNNRootViewCreator.h @@ -1,14 +1,15 @@ #import #import "RNNComponentOptions.h" +#import "RNNReactView.h" @protocol RNNRootViewCreator -- (UIView*)createRootView:(NSString*)name rootViewId:(NSString*)rootViewId; +- (RNNReactView*)createRootView:(NSString*)name rootViewId:(NSString*)rootViewId reactViewReadyBlock:(RNNReactViewReadyCompletionBlock)reactViewReadyBlock; - (UIView*)createRootViewFromComponentOptions:(RNNComponentOptions*)componentOptions; -- (UIView*)createCustomReactView:(NSString*)name rootViewId:(NSString*)rootViewId; +- (UIView*)createRootViewFromComponentOptions:(RNNComponentOptions*)componentOptions reactViewReadyBlock:(RNNReactViewReadyCompletionBlock)reactViewReadyBlock; @end diff --git a/lib/ios/RNNScreenTransition.h b/lib/ios/RNNScreenTransition.h index 727d4452e72..0e2b408ef20 100644 --- a/lib/ios/RNNScreenTransition.h +++ b/lib/ios/RNNScreenTransition.h @@ -7,8 +7,8 @@ @property (nonatomic, strong) RNNTransitionStateHolder* content; @property (nonatomic, strong) RNNTransitionStateHolder* bottomTabs; -@property (nonatomic) BOOL enable; -@property (nonatomic) BOOL waitForRender; +@property (nonatomic, strong) Bool* enable; +@property (nonatomic, strong) Bool* waitForRender; - (BOOL)hasCustomAnimation; diff --git a/lib/ios/RNNScreenTransition.m b/lib/ios/RNNScreenTransition.m index 3ca2c1c725b..8bdff883052 100644 --- a/lib/ios/RNNScreenTransition.m +++ b/lib/ios/RNNScreenTransition.m @@ -8,9 +8,8 @@ - (instancetype)initWithDict:(NSDictionary *)dict { self.topBar = dict[@"topBar"] ? [[RNNTransitionStateHolder alloc] initWithDict:dict[@"topBar"]] : nil; self.content = dict[@"content"] ? [[RNNTransitionStateHolder alloc] initWithDict:dict[@"content"]] : nil; self.bottomTabs = dict[@"bottomTabs"] ? [[RNNTransitionStateHolder alloc] initWithDict:dict[@"bottomTabs"]] : nil; - - self.enable = dict[@"enabled"] ? [dict[@"enabled"] boolValue] : YES; - self.waitForRender = dict[@"waitForRender"] ? [dict[@"waitForRender"] boolValue] : NO; + self.enable = [BoolParser parse:dict key:@"enabled"]; + self.waitForRender = [BoolParser parse:dict key:@"waitForRender"]; return self; } diff --git a/lib/ios/RNNSideMenuChildVC.h b/lib/ios/RNNSideMenuChildVC.h index 29417245065..a6ad9c7260c 100644 --- a/lib/ios/RNNSideMenuChildVC.h +++ b/lib/ios/RNNSideMenuChildVC.h @@ -1,5 +1,6 @@ #import #import "RNNParentProtocol.h" +#import "RNNViewControllerPresenter.h" typedef NS_ENUM(NSInteger, RNNSideMenuChildType) { RNNSideMenuChildTypeCenter, diff --git a/lib/ios/RNNSideMenuChildVC.m b/lib/ios/RNNSideMenuChildVC.m index 4a7e3c55c4d..11c56135b5a 100644 --- a/lib/ios/RNNSideMenuChildVC.m +++ b/lib/ios/RNNSideMenuChildVC.m @@ -52,6 +52,10 @@ - (void)overrideOptions:(RNNNavigationOptions *)options { [self.options overrideOptions:options]; } +- (void)renderTreeAndWait:(BOOL)wait perform:(RNNReactViewReadyCompletionBlock)readyBlock { + [self.getCurrentChild renderTreeAndWait:wait perform:readyBlock]; +} + - (void)bindChildViewController:(UIViewController*)child { self.child = child; [self addChildViewController:self.child]; diff --git a/lib/ios/RNNSideMenuController.h b/lib/ios/RNNSideMenuController.h index 0f1e927e2c6..b5bd80a2182 100644 --- a/lib/ios/RNNSideMenuController.h +++ b/lib/ios/RNNSideMenuController.h @@ -2,6 +2,7 @@ #import "RNNSideMenuChildVC.h" #import "MMDrawerController.h" #import "RNNParentProtocol.h" +#import "RNNViewControllerPresenter.h" @interface RNNSideMenuController : MMDrawerController diff --git a/lib/ios/RNNSideMenuController.m b/lib/ios/RNNSideMenuController.m index 5927f21c45a..f9c938d3cc5 100644 --- a/lib/ios/RNNSideMenuController.m +++ b/lib/ios/RNNSideMenuController.m @@ -60,6 +60,26 @@ - (void)overrideOptions:(RNNNavigationOptions *)options { [self.options overrideOptions:options]; } +- (void)renderTreeAndWait:(BOOL)wait perform:(RNNReactViewReadyCompletionBlock)readyBlock { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ + dispatch_group_t group = dispatch_group_create(); + for (UIViewController* childViewController in self.childViewControllers) { + dispatch_group_enter(group); + dispatch_async(dispatch_get_main_queue(), ^{ + [childViewController renderTreeAndWait:wait perform:^{ + dispatch_group_leave(group); + }]; + }); + } + + dispatch_group_wait(group, DISPATCH_TIME_FOREVER); + + dispatch_async(dispatch_get_main_queue(), ^{ + readyBlock(); + }); + }); +} + - (void)setAnimationType:(NSString *)animationType { MMDrawerControllerDrawerVisualStateBlock animationTypeStateBlock = nil; if ([animationType isEqualToString:@"door"]) animationTypeStateBlock = [MMDrawerVisualState swingingDoorVisualStateBlock]; @@ -129,6 +149,8 @@ -(void)setControllers:(NSArray*)controllers { else if(child.type == RNNSideMenuChildTypeRight) { self.right = child; } + + [self addChildViewController:child]; } else { diff --git a/lib/ios/RNNSplitViewController.m b/lib/ios/RNNSplitViewController.m index 04a8b443ef9..683b210efe1 100644 --- a/lib/ios/RNNSplitViewController.m +++ b/lib/ios/RNNSplitViewController.m @@ -45,6 +45,26 @@ - (void)overrideOptions:(RNNNavigationOptions *)options { [self.options overrideOptions:options]; } +- (void)renderTreeAndWait:(BOOL)wait perform:(RNNReactViewReadyCompletionBlock)readyBlock { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ + dispatch_group_t group = dispatch_group_create(); + for (UIViewController* childViewController in self.childViewControllers) { + dispatch_group_enter(group); + dispatch_async(dispatch_get_main_queue(), ^{ + [childViewController renderTreeAndWait:wait perform:^{ + dispatch_group_leave(group); + }]; + }); + } + + dispatch_group_wait(group, DISPATCH_TIME_FOREVER); + + dispatch_async(dispatch_get_main_queue(), ^{ + readyBlock(); + }); + }); +} + - (void)bindChildViewControllers:(NSArray *> *)viewControllers { [self setViewControllers:viewControllers]; UIViewController* masterViewController = viewControllers[0]; diff --git a/lib/ios/RNNTabBarController.m b/lib/ios/RNNTabBarController.m index 8443418081a..a9f7f71f493 100644 --- a/lib/ios/RNNTabBarController.m +++ b/lib/ios/RNNTabBarController.m @@ -60,6 +60,26 @@ - (void)overrideOptions:(RNNNavigationOptions *)options { [self.options overrideOptions:options]; } +- (void)renderTreeAndWait:(BOOL)wait perform:(RNNReactViewReadyCompletionBlock)readyBlock { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ + dispatch_group_t group = dispatch_group_create(); + for (UIViewController* childViewController in self.childViewControllers) { + dispatch_group_enter(group); + dispatch_async(dispatch_get_main_queue(), ^{ + [childViewController renderTreeAndWait:wait perform:^{ + dispatch_group_leave(group); + }]; + }); + } + + dispatch_group_wait(group, DISPATCH_TIME_FOREVER); + + dispatch_async(dispatch_get_main_queue(), ^{ + readyBlock(); + }); + }); +} + - (UIInterfaceOrientationMask)supportedInterfaceOrientations { return self.selectedViewController.supportedInterfaceOrientations; } diff --git a/lib/ios/RNNTitleViewHelper.h b/lib/ios/RNNTitleViewHelper.h index 077056ff103..2cf00d40551 100644 --- a/lib/ios/RNNTitleViewHelper.h +++ b/lib/ios/RNNTitleViewHelper.h @@ -12,6 +12,8 @@ @interface RNNTitleViewHelper : NSObject +@property (nonatomic, strong) RNNTitleOptions *titleOptions; +@property (nonatomic, strong) RNNSubtitleOptions *subtitleOptions; - (instancetype)initWithTitleViewOptions:(RNNOptions*)titleOptions subTitleOptions:(RNNOptions*)subtitleOptions diff --git a/lib/ios/RNNTitleViewHelper.m b/lib/ios/RNNTitleViewHelper.m index 33c5833a1d0..766b270decd 100644 --- a/lib/ios/RNNTitleViewHelper.m +++ b/lib/ios/RNNTitleViewHelper.m @@ -13,12 +13,7 @@ @interface RNNTitleViewHelper () @property (nonatomic, weak) UIViewController *viewController; -@property (nonatomic, strong) NSString *title; -@property (nonatomic, strong) NSString *subtitle; - @property (nonatomic, strong) RNNTitleView *titleView; -@property (nonatomic, strong) RNNTitleOptions *titleOptions; -@property (nonatomic, strong) RNNSubtitleOptions *subtitleOptions; @end @@ -31,8 +26,6 @@ - (instancetype)initWithTitleViewOptions:(RNNTitleOptions*)titleOptions self = [super init]; if (self) { self.viewController = viewController; - self.title = [titleOptions.text getWithDefaultValue:nil]; - self.subtitle = [subtitleOptions.text getWithDefaultValue:nil]; self.titleOptions = titleOptions; self.subtitleOptions = subtitleOptions; @@ -40,6 +33,14 @@ - (instancetype)initWithTitleViewOptions:(RNNTitleOptions*)titleOptions return self; } +- (NSString *)title { + return [self.titleOptions.text getWithDefaultValue:nil]; +} + +- (NSString *)subtitle { + return [self.subtitleOptions.text getWithDefaultValue:nil]; +} + +(NSString*)validateString:(NSString*)string { if ([string isEqual:[NSNull null]]) { return nil; diff --git a/lib/ios/RNNTopTabOptions.m b/lib/ios/RNNTopTabOptions.m index 3cf7583554a..fd0ea23725b 100644 --- a/lib/ios/RNNTopTabOptions.m +++ b/lib/ios/RNNTopTabOptions.m @@ -1,5 +1,4 @@ #import "RNNTopTabOptions.h" -#import "RNNRootViewController.h" @implementation RNNTopTabOptions diff --git a/lib/ios/RNNTopTabsViewController.h b/lib/ios/RNNTopTabsViewController.h index 9a2cd0c8586..08c8e7c4fbf 100644 --- a/lib/ios/RNNTopTabsViewController.h +++ b/lib/ios/RNNTopTabsViewController.h @@ -6,7 +6,7 @@ @property (nonatomic, retain) UIView* contentView; @property (nonatomic, retain) RNNLayoutInfo* layoutInfo; -@property (nonatomic, retain) RNNViewControllerPresenter* presenter; +@property (nonatomic, retain) RNNBasePresenter* presenter; @property (nonatomic, strong) RNNNavigationOptions* options; @property (nonatomic, strong) RNNNavigationOptions* defaultOptions; diff --git a/lib/ios/RNNTopTabsViewController.m b/lib/ios/RNNTopTabsViewController.m index 28544760563..9a4e8bf1791 100644 --- a/lib/ios/RNNTopTabsViewController.m +++ b/lib/ios/RNNTopTabsViewController.m @@ -58,6 +58,26 @@ - (void)overrideOptions:(RNNNavigationOptions *)options { [self.options overrideOptions:options]; } +- (void)renderTreeAndWait:(BOOL)wait perform:(RNNReactViewReadyCompletionBlock)readyBlock { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ + dispatch_group_t group = dispatch_group_create(); + for (UIViewController* childViewController in self.childViewControllers) { + dispatch_group_enter(group); + dispatch_async(dispatch_get_main_queue(), ^{ + [childViewController renderTreeAndWait:wait perform:^{ + dispatch_group_leave(group); + }]; + }); + } + + dispatch_group_wait(group, DISPATCH_TIME_FOREVER); + + dispatch_async(dispatch_get_main_queue(), ^{ + readyBlock(); + }); + }); +} + - (void)createTabBar { _segmentedControl = [[RNNSegmentedControl alloc] initWithSectionTitles:@[@"", @"", @""]]; _segmentedControl.frame = CGRectMake(0, 0, self.view.bounds.size.width, 50); @@ -91,6 +111,7 @@ - (void)setViewControllers:(NSArray *)viewControllers { for (RNNRootViewController* childVc in viewControllers) { [childVc.view setFrame:_contentView.bounds]; // [childVc.options.topTab applyOn:childVc]; + [self addChildViewController:childVc]; } [self setSelectedViewControllerIndex:0]; diff --git a/lib/ios/RNNTransitionsOptions.h b/lib/ios/RNNTransitionsOptions.h index 996d0171f3e..b5d96531d9c 100644 --- a/lib/ios/RNNTransitionsOptions.h +++ b/lib/ios/RNNTransitionsOptions.h @@ -7,7 +7,9 @@ @property (nonatomic, strong) RNNScreenTransition* push; @property (nonatomic, strong) RNNScreenTransition* pop; @property (nonatomic, strong) RNNScreenTransition* showModal; +@property (nonatomic, strong) RNNScreenTransition* showOverlay; @property (nonatomic, strong) RNNScreenTransition* dismissModal; @property (nonatomic, strong) RNNScreenTransition* setStackRoot; +@property (nonatomic, strong) RNNScreenTransition* setRoot; @end diff --git a/lib/ios/RNNTransitionsOptions.m b/lib/ios/RNNTransitionsOptions.m index ee1ba5b43d4..91e668b3794 100644 --- a/lib/ios/RNNTransitionsOptions.m +++ b/lib/ios/RNNTransitionsOptions.m @@ -8,8 +8,10 @@ - (instancetype)initWithDict:(NSDictionary *)dict { self.push = [[RNNScreenTransition alloc] initWithDict:dict[@"push"]]; self.pop = [[RNNScreenTransition alloc] initWithDict:dict[@"pop"]]; self.showModal = [[RNNScreenTransition alloc] initWithDict:dict[@"showModal"]]; + self.showOverlay = [[RNNScreenTransition alloc] initWithDict:dict[@"showOverlay"]]; self.dismissModal = [[RNNScreenTransition alloc] initWithDict:dict[@"dismissModal"]]; self.setStackRoot = [[RNNScreenTransition alloc] initWithDict:dict[@"setStackRoot"]]; + self.setRoot = [[RNNScreenTransition alloc] initWithDict:dict[@"setRoot"]]; return self; } diff --git a/lib/ios/RNNViewControllerPresenter.h b/lib/ios/RNNViewControllerPresenter.h index cb498e4ccf5..92f0d4d95ef 100644 --- a/lib/ios/RNNViewControllerPresenter.h +++ b/lib/ios/RNNViewControllerPresenter.h @@ -1,10 +1,13 @@ #import "RNNBasePresenter.h" #import "RNNNavigationButtons.h" +#import "RNNReactComponentManager.h" @interface RNNViewControllerPresenter : RNNBasePresenter -@property (nonatomic, strong) RNNNavigationButtons* navigationButtons; +- (instancetype)initWithComponentManager:(RNNReactComponentManager *)componentManager; + +- (void)renderComponents:(RNNNavigationOptions *)options perform:(RNNReactViewReadyCompletionBlock)readyBlock; -- (void)bindViewController:(UIViewController *)bindedViewController viewCreator:(id)creator; +@property (nonatomic, strong) RNNNavigationButtons* navigationButtons; @end diff --git a/lib/ios/RNNViewControllerPresenter.m b/lib/ios/RNNViewControllerPresenter.m index 0d7bbc0d53f..04c37f13dc7 100644 --- a/lib/ios/RNNViewControllerPresenter.m +++ b/lib/ios/RNNViewControllerPresenter.m @@ -3,12 +3,38 @@ #import "UITabBarController+RNNOptions.h" #import "RCTConvert+Modal.h" #import "RNNReactView.h" +#import "RNNCustomTitleView.h" +#import "RNNTitleViewHelper.h" + +@interface RNNViewControllerPresenter() { + RNNReactView* _customTitleView; + RNNTitleViewHelper* _titleViewHelper; + RNNReactComponentManager* _componentManager; +} + +@end @implementation RNNViewControllerPresenter -- (void)bindViewController:(UIViewController *)bindedViewController viewCreator:(id)creator { +- (instancetype)init { + self = [super init]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(cleanReactLeftovers) + name:RCTJavaScriptWillStartLoadingNotification + object:nil]; + + return self; +} + +- (instancetype)initWithComponentManager:(RNNReactComponentManager *)componentManager { + self = [self init]; + _componentManager = componentManager; + return self; +} + +- (void)bindViewController:(UIViewController *)bindedViewController { self.bindedViewController = bindedViewController; - _navigationButtons = [[RNNNavigationButtons alloc] initWithViewController:self.bindedViewController rootViewCreator:creator]; + _navigationButtons = [[RNNNavigationButtons alloc] initWithViewController:self.bindedViewController componentManager:_componentManager]; } - (void)applyOptions:(RNNNavigationOptions *)options { @@ -23,7 +49,7 @@ - (void)applyOptions:(RNNNavigationOptions *)options { [viewController rnn_setStatusBarStyle:[options.statusBar.style getWithDefaultValue:@"default"] animated:[options.statusBar.animate getWithDefaultValue:YES]]; [viewController rnn_setBackButtonVisible:[options.topBar.backButton.visible getWithDefaultValue:YES]]; [viewController rnn_setInterceptTouchOutside:[options.overlay.interceptTouchOutside getWithDefaultValue:YES]]; - + if (options.layout.backgroundColor.hasValue) { [viewController rnn_setBackgroundColor:options.layout.backgroundColor.get]; } @@ -35,6 +61,8 @@ - (void)applyOptions:(RNNNavigationOptions *)options { } [viewController rnn_setSearchBarWithPlaceholder:[options.topBar.searchBarPlaceholder getWithDefaultValue:@""] hideNavBarOnFocusSearchBar: hideNavBarOnFocusSearchBar]; } + + [self setTitleViewWithSubtitle:options]; } - (void)applyOptionsOnInit:(RNNNavigationOptions *)options { @@ -125,6 +153,69 @@ - (void)mergeOptions:(RNNNavigationOptions *)newOptions currentOptions:(RNNNavig RCTRootView* rootView = (RCTRootView*)viewController.view; rootView.passThroughTouches = !newOptions.overlay.interceptTouchOutside.get; } + + [self setTitleViewWithSubtitle:newOptions]; + + if (newOptions.topBar.title.component.name.hasValue) { + [self setCustomNavigationTitleView:newOptions perform:nil]; + } +} + +- (void)renderComponents:(RNNNavigationOptions *)options perform:(RNNReactViewReadyCompletionBlock)readyBlock { + [self setCustomNavigationTitleView:options perform:readyBlock]; +} + +- (void)setCustomNavigationTitleView:(RNNNavigationOptions *)options perform:(RNNReactViewReadyCompletionBlock)readyBlock { + UIViewController* viewController = self.bindedViewController; + if (![options.topBar.title.component.waitForRender getWithDefaultValue:NO] && readyBlock) { + readyBlock(); + readyBlock = nil; + } + + if (options.topBar.title.component.name.hasValue) { + _customTitleView = (RNNReactView*)[_componentManager createComponentIfNotExists:options.topBar.title.component parentComponentId:viewController.layoutInfo.componentId reactViewReadyBlock:readyBlock]; + _customTitleView.backgroundColor = UIColor.clearColor; + NSString* alignment = [options.topBar.title.component.alignment getWithDefaultValue:@""]; + [_customTitleView setAlignment:alignment]; + BOOL isCenter = [alignment isEqualToString:@"center"]; + __weak RNNReactView *weakTitleView = _customTitleView; + CGRect frame = viewController.navigationController.navigationBar.bounds; + [_customTitleView setFrame:frame]; + [_customTitleView setRootViewDidChangeIntrinsicSize:^(CGSize intrinsicContentSize) { + if (isCenter) { + [weakTitleView setFrame:CGRectMake(0, 0, intrinsicContentSize.width, intrinsicContentSize.height)]; + } else { + [weakTitleView setFrame:frame]; + } + }]; + + viewController.navigationItem.titleView = _customTitleView; + } else { + [_customTitleView removeFromSuperview]; + if (readyBlock) { + readyBlock(); + } + } +} + +- (void)setTitleViewWithSubtitle:(RNNNavigationOptions *)options { + if (!_customTitleView && options.topBar.subtitle.text.hasValue) { + _titleViewHelper = [[RNNTitleViewHelper alloc] initWithTitleViewOptions:options.topBar.title subTitleOptions:options.topBar.subtitle viewController:self.bindedViewController]; + [_titleViewHelper setup]; + } else if (_titleViewHelper) { + if (options.topBar.title.text.hasValue) { + [_titleViewHelper setTitleOptions:options.topBar.title]; + } + if (options.topBar.subtitle.text.hasValue) { + [_titleViewHelper setSubtitleOptions:options.topBar.subtitle]; + } + + [_titleViewHelper setup]; + } +} + +- (void)cleanReactLeftovers { + _customTitleView = nil; } @end diff --git a/lib/ios/ReactNativeNavigation.xcodeproj/project.pbxproj b/lib/ios/ReactNativeNavigation.xcodeproj/project.pbxproj index ba34535d33a..a45b789132a 100644 --- a/lib/ios/ReactNativeNavigation.xcodeproj/project.pbxproj +++ b/lib/ios/ReactNativeNavigation.xcodeproj/project.pbxproj @@ -93,8 +93,6 @@ 501E0217213E7EA3003365C5 /* RNNReactView.h in Headers */ = {isa = PBXBuildFile; fileRef = 501E0215213E7EA3003365C5 /* RNNReactView.h */; }; 501E0218213E7EA3003365C5 /* RNNReactView.m in Sources */ = {isa = PBXBuildFile; fileRef = 501E0216213E7EA3003365C5 /* RNNReactView.m */; }; 50206A6D21AFE75400B7BB1A /* RNNSideMenuParserTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 50206A6C21AFE75400B7BB1A /* RNNSideMenuParserTest.m */; }; - 50220F48212ABDFD004C2B0A /* RNNReactRootView.h in Headers */ = {isa = PBXBuildFile; fileRef = 50220F46212ABDFD004C2B0A /* RNNReactRootView.h */; }; - 50220F49212ABDFD004C2B0A /* RNNReactRootView.m in Sources */ = {isa = PBXBuildFile; fileRef = 50220F47212ABDFD004C2B0A /* RNNReactRootView.m */; }; 502CB46E20CD1DDA0019B2FE /* RNNBackButtonOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 502CB46C20CD1DDA0019B2FE /* RNNBackButtonOptions.h */; }; 502CB46F20CD1DDA0019B2FE /* RNNBackButtonOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 502CB46D20CD1DDA0019B2FE /* RNNBackButtonOptions.m */; }; 502F0E142178CF8200367CC3 /* UIViewController+RNNOptionsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 502F0E132178CF8200367CC3 /* UIViewController+RNNOptionsTest.m */; }; @@ -168,6 +166,8 @@ 504AFE751FFFF0540076E904 /* RNNTopTabsOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 504AFE731FFFF0540076E904 /* RNNTopTabsOptions.m */; }; 504AFE761FFFF1E00076E904 /* RNNNavigationOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = E83BAD691F27362500A9F3DD /* RNNNavigationOptions.h */; }; 504AFE771FFFF1E20076E904 /* RNNTopBarOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = A7626BFE1FC2FB6700492FB8 /* RNNTopBarOptions.h */; }; + 5050465421F8F4490035497A /* RNNReactComponentManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 5050465221F8F4490035497A /* RNNReactComponentManager.h */; }; + 5050465521F8F4490035497A /* RNNReactComponentManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5050465321F8F4490035497A /* RNNReactComponentManager.m */; }; 5053CE7F2175FB1900D0386B /* RNNDefaultOptionsHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = 5053CE7D2175FB1900D0386B /* RNNDefaultOptionsHelper.h */; }; 5053CE802175FB1900D0386B /* RNNDefaultOptionsHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 5053CE7E2175FB1900D0386B /* RNNDefaultOptionsHelper.m */; }; 50570B262061473D006A1B5C /* RNNTitleOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 50570B242061473D006A1B5C /* RNNTitleOptions.h */; }; @@ -421,8 +421,6 @@ 501E0215213E7EA3003365C5 /* RNNReactView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNReactView.h; sourceTree = ""; }; 501E0216213E7EA3003365C5 /* RNNReactView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNReactView.m; sourceTree = ""; }; 50206A6C21AFE75400B7BB1A /* RNNSideMenuParserTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNSideMenuParserTest.m; sourceTree = ""; }; - 50220F46212ABDFD004C2B0A /* RNNReactRootView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNReactRootView.h; sourceTree = ""; }; - 50220F47212ABDFD004C2B0A /* RNNReactRootView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNReactRootView.m; sourceTree = ""; }; 502CB43920CBCA140019B2FE /* RNNBridgeManagerDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNBridgeManagerDelegate.h; sourceTree = ""; }; 502CB46C20CD1DDA0019B2FE /* RNNBackButtonOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNBackButtonOptions.h; sourceTree = ""; }; 502CB46D20CD1DDA0019B2FE /* RNNBackButtonOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNBackButtonOptions.m; sourceTree = ""; }; @@ -496,6 +494,8 @@ 504AFE631FFE53070076E904 /* RNNOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNOptions.m; sourceTree = ""; }; 504AFE721FFFF0540076E904 /* RNNTopTabsOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNTopTabsOptions.h; sourceTree = ""; }; 504AFE731FFFF0540076E904 /* RNNTopTabsOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNTopTabsOptions.m; sourceTree = ""; }; + 5050465221F8F4490035497A /* RNNReactComponentManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNReactComponentManager.h; sourceTree = ""; }; + 5050465321F8F4490035497A /* RNNReactComponentManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNReactComponentManager.m; sourceTree = ""; }; 5053CE7D2175FB1900D0386B /* RNNDefaultOptionsHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNDefaultOptionsHelper.h; sourceTree = ""; }; 5053CE7E2175FB1900D0386B /* RNNDefaultOptionsHelper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNDefaultOptionsHelper.m; sourceTree = ""; }; 50570B242061473D006A1B5C /* RNNTitleOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNTitleOptions.h; sourceTree = ""; }; @@ -972,6 +972,8 @@ 261F0E691E6F028A00989DE2 /* RNNNavigationStackManager.m */, 50D031322005149000386B3D /* RNNOverlayManager.h */, 50D031332005149000386B3D /* RNNOverlayManager.m */, + 5050465221F8F4490035497A /* RNNReactComponentManager.h */, + 5050465321F8F4490035497A /* RNNReactComponentManager.m */, ); name = Managers; sourceTree = ""; @@ -990,8 +992,6 @@ 50570BE82063E09B006A1B5C /* RNNTitleViewHelper.h */, 501CD31D214A5B6900A6E225 /* RNNLayoutInfo.h */, 501CD31E214A5B6900A6E225 /* RNNLayoutInfo.m */, - 50220F46212ABDFD004C2B0A /* RNNReactRootView.h */, - 50220F47212ABDFD004C2B0A /* RNNReactRootView.m */, 501E0215213E7EA3003365C5 /* RNNReactView.h */, 501E0216213E7EA3003365C5 /* RNNReactView.m */, 50570BE92063E09B006A1B5C /* RNNTitleViewHelper.m */, @@ -1279,6 +1279,7 @@ 5012242221736883000F5F98 /* NullColor.h in Headers */, 50570B262061473D006A1B5C /* RNNTitleOptions.h in Headers */, 50395593217485B000B0A663 /* Double.h in Headers */, + 5050465421F8F4490035497A /* RNNReactComponentManager.h in Headers */, 504AFE741FFFF0540076E904 /* RNNTopTabsOptions.h in Headers */, E8E5182E1F83A48B000467AC /* RNNTransitionStateHolder.h in Headers */, 5049594A216F5FE6006D2B81 /* NullText.h in Headers */, @@ -1286,7 +1287,6 @@ 5038A3CA216E328A009280BC /* Param.h in Headers */, 50887CAA20F26BFE00D06111 /* RNNOverlayWindow.h in Headers */, 507E7D57201DDD3000444E6C /* RNNAnimationOptions.h in Headers */, - 50220F48212ABDFD004C2B0A /* RNNReactRootView.h in Headers */, 5053CE7F2175FB1900D0386B /* RNNDefaultOptionsHelper.h in Headers */, 2DCD9195200014A900EDC75D /* RNNBridgeManager.h in Headers */, 7B1126A91E2D2B6C00F9B03B /* RNNControllerFactory.h in Headers */, @@ -1462,6 +1462,7 @@ 5012242721737278000F5F98 /* NullImage.m in Sources */, 7B1126A01E2D263F00F9B03B /* RNNEventEmitter.m in Sources */, A7626BFD1FC2FB2C00492FB8 /* RNNTopBarOptions.m in Sources */, + 5050465521F8F4490035497A /* RNNReactComponentManager.m in Sources */, 263905CB1E4C6F440023D7D3 /* SidebarLuvocracyAnimation.m in Sources */, 50570BEB2063E09B006A1B5C /* RNNTitleViewHelper.m in Sources */, 263905E71E4CAC950023D7D3 /* RNNSideMenuChildVC.m in Sources */, @@ -1509,7 +1510,6 @@ 50EB4ED82068EBE000D6ED34 /* RNNBackgroundOptions.m in Sources */, 507F43CA1FF4F9CC00D9425B /* RNNTopTabOptions.m in Sources */, 26916C991E4B9E7700D13680 /* RNNReactRootViewCreator.m in Sources */, - 50220F49212ABDFD004C2B0A /* RNNReactRootView.m in Sources */, 5064495E20DC62B90026709C /* RNNSideMenuSideOptions.m in Sources */, 214545251F4DC125006E8DA1 /* RNNUIBarButtonItem.m in Sources */, 263905B81E4C6F440023D7D3 /* UIViewController+MMDrawerController.m in Sources */, diff --git a/lib/ios/ReactNativeNavigationTests/RNNCommandsHandlerTest.m b/lib/ios/ReactNativeNavigationTests/RNNCommandsHandlerTest.m index da45466826c..8400279199d 100644 --- a/lib/ios/ReactNativeNavigationTests/RNNCommandsHandlerTest.m +++ b/lib/ios/ReactNativeNavigationTests/RNNCommandsHandlerTest.m @@ -62,7 +62,7 @@ - (void)setUp { self.store = [OCMockObject partialMockForObject:[[RNNStore alloc] init]]; self.eventEmmiter = [OCMockObject partialMockForObject:[RNNEventEmitter new]]; self.overlayManager = [OCMockObject partialMockForObject:[RNNOverlayManager new]]; - self.controllerFactory = [OCMockObject partialMockForObject:[[RNNControllerFactory alloc] initWithRootViewCreator:nil eventEmitter:self.eventEmmiter andBridge:nil]]; + self.controllerFactory = [OCMockObject partialMockForObject:[[RNNControllerFactory alloc] initWithRootViewCreator:nil eventEmitter:self.eventEmmiter store:self.store componentManager:nil andBridge:nil]]; self.uut = [[RNNCommandsHandler alloc] initWithStore:self.store controllerFactory:self.controllerFactory eventEmitter:self.eventEmmiter stackManager:[RNNNavigationStackManager new] modalManager:[RNNModalManager new] overlayManager:self.overlayManager mainWindow:_mainWindow]; self.vc1 = [RNNRootViewController new]; self.vc2 = [RNNRootViewController new]; @@ -126,7 +126,7 @@ -(void)testDynamicStylesMergeWithStaticStyles { RNNViewControllerPresenter* presenter = [[RNNViewControllerPresenter alloc] init]; RNNRootViewController* vc = [[RNNRootViewController alloc] initWithLayoutInfo:layoutInfo rootViewCreator:creator eventEmitter:nil presenter:presenter options:initialOptions defaultOptions:nil]; - RNNNavigationController* nav = [[RNNNavigationController alloc] initWithLayoutInfo:nil childViewControllers:@[vc] options:[[RNNNavigationOptions alloc] initEmptyOptions] defaultOptions:nil presenter:[[RNNNavigationControllerPresenter alloc] init]]; + RNNNavigationController* nav = [[RNNNavigationController alloc] initWithLayoutInfo:nil creator:creator childViewControllers:@[vc] options:[[RNNNavigationOptions alloc] initEmptyOptions] defaultOptions:nil presenter:[[RNNNavigationControllerPresenter alloc] init]]; [vc viewWillAppear:false]; XCTAssertTrue([vc.navigationItem.title isEqual:@"the title"]); @@ -213,7 +213,7 @@ - (void)testShowOverlay_createLayout { OCMStub([self.overlayManager showOverlayWindow:[OCMArg any]]); NSDictionary* layout = @{}; - [[self.controllerFactory expect] createLayout:layout saveToStore:self.store]; + [[self.controllerFactory expect] createLayout:layout]; [self.uut showOverlay:layout completion:^{}]; [self.controllerFactory verify]; } @@ -221,9 +221,9 @@ - (void)testShowOverlay_createLayout { - (void)testShowOverlay_saveToStore { [self.store setReadyToReceiveCommands:true]; OCMStub([self.overlayManager showOverlayWindow:[OCMArg any]]); - OCMStub([self.controllerFactory createLayout:[OCMArg any] saveToStore:[OCMArg any]]); + OCMStub([self.controllerFactory createLayout:[OCMArg any]]); - [[self.controllerFactory expect] createLayout:[OCMArg any] saveToStore:self.store]; + [[self.controllerFactory expect] createLayout:[OCMArg any]]; [self.uut showOverlay:@{} completion:^{}]; [self.overlayManager verify]; } @@ -231,7 +231,7 @@ - (void)testShowOverlay_saveToStore { - (void)testShowOverlay_withCreatedLayout { [self.store setReadyToReceiveCommands:true]; UIViewController* layoutVC = [RNNRootViewController new]; - OCMStub([self.controllerFactory createLayout:[OCMArg any] saveToStore:[OCMArg any]]).andReturn(layoutVC); + OCMStub([self.controllerFactory createLayout:[OCMArg any]]).andReturn(layoutVC); [[self.overlayManager expect] showOverlayWindow:[OCMArg any]]; [self.uut showOverlay:@{} completion:^{}]; @@ -241,7 +241,8 @@ - (void)testShowOverlay_withCreatedLayout { - (void)testShowOverlay_invokeNavigationCommandEventWithLayout { [self.store setReadyToReceiveCommands:true]; OCMStub([self.overlayManager showOverlayWindow:[OCMArg any]]); - OCMStub([self.controllerFactory createLayout:[OCMArg any] saveToStore:[OCMArg any]]); + id mockedVC = [OCMockObject partialMockForObject:self.vc1]; + OCMStub([self.controllerFactory createLayout:[OCMArg any]]).andReturn(mockedVC); NSDictionary* layout = @{}; @@ -294,7 +295,7 @@ - (void)testDismissOverlay_invokeNavigationCommandEvent { - (void)testSetRoot_setRootViewControllerOnMainWindow { [self.store setReadyToReceiveCommands:true]; - OCMStub([self.controllerFactory createLayout:[OCMArg any] saveToStore:self.store]).andReturn(self.vc1); + OCMStub([self.controllerFactory createLayout:[OCMArg any]]).andReturn(self.vc1); [[self.mainWindow expect] setRootViewController:self.vc1]; [self.uut setRoot:@{} completion:^{}]; @@ -303,7 +304,7 @@ - (void)testSetRoot_setRootViewControllerOnMainWindow { - (void)testSetRoot_removeAllComponentsFromMainWindow { [self.store setReadyToReceiveCommands:true]; - OCMStub([self.controllerFactory createLayout:[OCMArg any] saveToStore:self.store]).andReturn(self.vc1); + OCMStub([self.controllerFactory createLayout:[OCMArg any]]).andReturn(self.vc1); [[self.store expect] removeAllComponentsFromWindow:self.mainWindow]; [self.uut setRoot:@{} completion:^{}]; @@ -311,7 +312,7 @@ - (void)testSetRoot_removeAllComponentsFromMainWindow { } - (void)testSetStackRoot_resetStackWithSingleComponent { - OCMStub([self.controllerFactory createChildrenLayout:[OCMArg any] saveToStore:self.store]).andReturn(@[self.vc2]); + OCMStub([self.controllerFactory createChildrenLayout:[OCMArg any]]).andReturn(@[self.vc2]); [self.store setReadyToReceiveCommands:true]; [self.uut setStackRoot:@"vc1" children:nil completion:^{ @@ -324,7 +325,7 @@ - (void)testSetStackRoot_resetStackWithSingleComponent { - (void)testSetStackRoot_setMultipleChildren { NSArray* newViewControllers = @[_vc1, _vc3]; - OCMStub([self.controllerFactory createChildrenLayout:[OCMArg any] saveToStore:self.store]).andReturn(newViewControllers); + OCMStub([self.controllerFactory createChildrenLayout:[OCMArg any]]).andReturn(newViewControllers); [self.store setReadyToReceiveCommands:true]; [self.uut setStackRoot:@"vc1" children:nil completion:^{ @@ -334,4 +335,30 @@ - (void)testSetStackRoot_setMultipleChildren { XCTAssertTrue([_nvc.viewControllers isEqual:newViewControllers]); } +- (void)testSetRoot_waitForRenderTrue { + [self.store setReadyToReceiveCommands:true]; + self.vc1.options = [[RNNNavigationOptions alloc] initEmptyOptions]; + self.vc1.options.animations.setRoot.waitForRender = [[Bool alloc] initWithBOOL:YES]; + + id mockedVC = [OCMockObject partialMockForObject:self.vc1]; + OCMStub([self.controllerFactory createLayout:[OCMArg any]]).andReturn(mockedVC); + + [[mockedVC expect] renderTreeAndWait:YES perform:[OCMArg any]]; + [self.uut setRoot:@{} completion:^{}]; + [mockedVC verify]; +} + +- (void)testSetRoot_waitForRenderFalse { + [self.store setReadyToReceiveCommands:true]; + self.vc1.options = [[RNNNavigationOptions alloc] initEmptyOptions]; + self.vc1.options.animations.setRoot.waitForRender = [[Bool alloc] initWithBOOL:NO]; + + id mockedVC = [OCMockObject partialMockForObject:self.vc1]; + OCMStub([self.controllerFactory createLayout:[OCMArg any]]).andReturn(mockedVC); + + [[mockedVC expect] renderTreeAndWait:NO perform:[OCMArg any]]; + [self.uut setRoot:@{} completion:^{}]; + [mockedVC verify]; +} + @end diff --git a/lib/ios/ReactNativeNavigationTests/RNNControllerFactoryTest.m b/lib/ios/ReactNativeNavigationTests/RNNControllerFactoryTest.m index a56e0634fc3..0e7d2a74b01 100644 --- a/lib/ios/ReactNativeNavigationTests/RNNControllerFactoryTest.m +++ b/lib/ios/ReactNativeNavigationTests/RNNControllerFactoryTest.m @@ -23,7 +23,7 @@ - (void)setUp { [super setUp]; self.creator = nil; self.store = [RNNStore new]; - self.factory = [[RNNControllerFactory alloc] initWithRootViewCreator:self.creator eventEmitter:nil andBridge:nil]; + self.factory = [[RNNControllerFactory alloc] initWithRootViewCreator:self.creator eventEmitter:nil store:self.store componentManager:nil andBridge:nil]; } - (void)tearDown { @@ -31,7 +31,7 @@ - (void)tearDown { } - (void)testCreateLayout_EmptyLayout { - XCTAssertThrows([self.factory createLayout:@{} saveToStore:self.store]); + XCTAssertThrows([self.factory createLayout:@{}]); } - (void)testCreateLayout_ComponentLayout { @@ -39,7 +39,7 @@ - (void)testCreateLayout_ComponentLayout { @"type": @"Component", @"data": @{}, @"children": @[]}; - id ans = [self.factory createLayout:layout saveToStore:self.store]; + id ans = [self.factory createLayout:layout]; XCTAssertTrue([ans isMemberOfClass:[RNNRootViewController class]]); } @@ -52,7 +52,7 @@ - (void)testCreateLayout_ExternalComponentLayout { @"type": @"ExternalComponent", @"data": @{@"name": @"externalComponent"}, @"children": @[]}; - id ans = [self.factory createLayout:layout saveToStore:self.store]; + id ans = [self.factory createLayout:layout]; XCTAssertTrue([ans isMemberOfClass:[RNNRootViewController class]]); } @@ -61,7 +61,7 @@ - (void)testCreateLayout_ComponentStackLayout { @"type": @"Stack", @"data": @{}, @"children": @[]}; - id ans = [self.factory createLayout:layout saveToStore:self.store]; + id ans = [self.factory createLayout:layout]; XCTAssertTrue([ans isMemberOfClass:[RNNNavigationController class]]); } @@ -78,7 +78,7 @@ - (void)testCreateLayout_SplitViewLayout { @"type": @"Component", @"data": @{}, @"children": @[]}]}; - id ans = [self.factory createLayout:layout saveToStore:self.store]; + id ans = [self.factory createLayout:layout]; XCTAssertTrue([ans isMemberOfClass:[RNNSplitViewController class]]); } @@ -91,7 +91,7 @@ - (void)testCreateLayout_ComponentStackLayoutRecursive { @"type": @"Component", @"data": @{}, @"children": @[]}]}; - RNNNavigationController* ans = (RNNNavigationController*) [self.factory createLayout:layout saveToStore:self.store]; + RNNNavigationController* ans = (RNNNavigationController*) [self.factory createLayout:layout]; XCTAssertTrue([ans isMemberOfClass:[RNNNavigationController class]]); XCTAssertTrue(ans.childViewControllers.count == 1); @@ -112,7 +112,7 @@ - (void)testCreateLayout_BottomTabsLayout { @"type": @"Component", @"data": @{}, @"children": @[]}]}]}; - RNNTabBarController* tabBar = (RNNTabBarController*) [self.factory createLayout:layout saveToStore:self.store]; + RNNTabBarController* tabBar = (RNNTabBarController*) [self.factory createLayout:layout]; XCTAssertTrue([tabBar isMemberOfClass:[RNNTabBarController class]]); XCTAssertTrue(tabBar.childViewControllers.count == 1); @@ -137,7 +137,7 @@ - (void)testCreateLayout_TopTabsLayout { @"type": @"Component", @"data": @{}, @"children": @[]}]}]}; - RNNTopTabsViewController* tabBar = (RNNTopTabsViewController*) [self.factory createLayout:layout saveToStore:self.store]; + RNNTopTabsViewController* tabBar = (RNNTopTabsViewController*) [self.factory createLayout:layout]; XCTAssertTrue([tabBar isMemberOfClass:[RNNTopTabsViewController class]]); } @@ -171,7 +171,7 @@ - (void)testCreateLayout_ComponentSideMenuLayoutCenterLeftRight { @"type": @"Component", @"data": @{}, @"children": @[]}]}]}; - RNNSideMenuController *ans = (RNNSideMenuController*) [self.factory createLayout:layout saveToStore:self.store]; + RNNSideMenuController *ans = (RNNSideMenuController*) [self.factory createLayout:layout]; XCTAssertTrue([ans isMemberOfClass:[RNNSideMenuController class]]); XCTAssertTrue([ans isKindOfClass:[UIViewController class]]); XCTAssertTrue([ans.center isMemberOfClass:[RNNSideMenuChildVC class]]); @@ -194,7 +194,7 @@ - (void)testCreateLayout_addComponentToStore { @"type": @"Component", @"data": @{}, @"children": @[]}; - UIViewController *ans = [self.factory createLayout:layout saveToStore:self.store]; + UIViewController *ans = [self.factory createLayout:layout]; UIViewController *storeAns = [self.store findComponentForId:componentId]; XCTAssertEqualObjects(ans, storeAns); diff --git a/lib/ios/ReactNativeNavigationTests/RNNNavigationControllerTest.m b/lib/ios/ReactNativeNavigationTests/RNNNavigationControllerTest.m index b1c71aa7fef..5d6ee315241 100644 --- a/lib/ios/ReactNativeNavigationTests/RNNNavigationControllerTest.m +++ b/lib/ios/ReactNativeNavigationTests/RNNNavigationControllerTest.m @@ -2,6 +2,7 @@ #import #import "RNNNavigationController.h" #import "RNNRootViewController.h" +#import "RNNTestRootViewCreator.h" @interface RNNNavigationControllerTest : XCTestCase @@ -15,17 +16,18 @@ @implementation RNNNavigationControllerTest { RNNRootViewController* _vc2; UIViewController* _vc3; RNNNavigationOptions* _options; + RNNTestRootViewCreator* _creator; } - (void)setUp { [super setUp]; - + _creator = [[RNNTestRootViewCreator alloc] init]; _vc1 = [[RNNRootViewController alloc] initWithLayoutInfo:nil rootViewCreator:nil eventEmitter:nil presenter:[OCMockObject partialMockForObject:[[RNNViewControllerPresenter alloc] init]] options:[[RNNNavigationOptions alloc] initEmptyOptions] defaultOptions:[[RNNNavigationOptions alloc] initEmptyOptions]]; _vc2 = [[RNNRootViewController alloc] initWithLayoutInfo:nil rootViewCreator:nil eventEmitter:nil presenter:[[RNNViewControllerPresenter alloc] init] options:[[RNNNavigationOptions alloc] initEmptyOptions] defaultOptions:[[RNNNavigationOptions alloc] initEmptyOptions]]; _vc2Mock = [OCMockObject partialMockForObject:_vc2]; _vc3 = [UIViewController new]; _options = [OCMockObject partialMockForObject:[[RNNNavigationOptions alloc] initEmptyOptions]]; - self.uut = [[RNNNavigationController alloc] initWithLayoutInfo:nil childViewControllers:@[_vc1, _vc2] options:_options defaultOptions:nil presenter:[OCMockObject partialMockForObject:[[RNNNavigationControllerPresenter alloc] init]]]; + self.uut = [[RNNNavigationController alloc] initWithLayoutInfo:nil creator:_creator childViewControllers:@[_vc1, _vc2] options:_options defaultOptions:nil presenter:[OCMockObject partialMockForObject:[[RNNNavigationControllerPresenter alloc] init]]]; } - (void)testInitWithLayoutInfo_shouldBindPresenter { @@ -33,7 +35,7 @@ - (void)testInitWithLayoutInfo_shouldBindPresenter { } - (void)testInitWithLayoutInfo_shouldSetMultipleViewControllers { - self.uut = [[RNNNavigationController alloc] initWithLayoutInfo:nil childViewControllers:@[_vc1, _vc2] options:[[RNNNavigationOptions alloc] initWithDict:@{}] defaultOptions:nil presenter:[[RNNViewControllerPresenter alloc] init]]; + self.uut = [[RNNNavigationController alloc] initWithLayoutInfo:nil creator:_creator childViewControllers:@[_vc1, _vc2] options:[[RNNNavigationOptions alloc] initWithDict:@{}] defaultOptions:nil presenter:[[RNNViewControllerPresenter alloc] init]]; XCTAssertTrue(self.uut.viewControllers.count == 2); } @@ -157,7 +159,7 @@ - (void)testOverrideOptionsShouldOverrideOptionsState { } - (RNNNavigationController *)createNavigationControllerWithOptions:(RNNNavigationOptions *)options { - return [[RNNNavigationController alloc] initWithLayoutInfo:nil childViewControllers:@[_vc1] options:options defaultOptions:nil presenter:[[RNNNavigationControllerPresenter alloc] init]]; + return [[RNNNavigationController alloc] initWithLayoutInfo:nil creator:_creator childViewControllers:@[_vc1] options:options defaultOptions:nil presenter:[[RNNNavigationControllerPresenter alloc] init]]; } @end diff --git a/lib/ios/ReactNativeNavigationTests/RNNRootViewControllerTest.m b/lib/ios/ReactNativeNavigationTests/RNNRootViewControllerTest.m index 34368b84ae3..c7737a2b15d 100644 --- a/lib/ios/ReactNativeNavigationTests/RNNRootViewControllerTest.m +++ b/lib/ios/ReactNativeNavigationTests/RNNRootViewControllerTest.m @@ -415,7 +415,7 @@ -(void)testOrientationTabsController_all { -(void)testRightButtonsWithTitle_withoutStyle { self.options.topBar.rightButtons = @[@{@"id": @"testId", @"text": @"test"}]; self.uut = [[RNNRootViewController alloc] initWithLayoutInfo:nil rootViewCreator:nil eventEmitter:nil presenter:[RNNViewControllerPresenter new] options:self.options defaultOptions:nil]; - RNNNavigationController* nav = [[RNNNavigationController alloc] initWithLayoutInfo:nil childViewControllers:@[self.uut] options:nil defaultOptions:nil presenter:nil]; + RNNNavigationController* nav = [[RNNNavigationController alloc] initWithLayoutInfo:nil creator:_creator childViewControllers:@[self.uut] options:nil defaultOptions:nil presenter:nil]; RNNUIBarButtonItem* button = (RNNUIBarButtonItem*)[nav.topViewController.navigationItem.rightBarButtonItems objectAtIndex:0]; NSString* expectedButtonId = @"testId"; @@ -430,7 +430,7 @@ -(void)testRightButtonsWithTitle_withStyle { self.options.topBar.rightButtons = @[@{@"id": @"testId", @"text": @"test", @"enabled": @false, @"buttonColor": inputColor, @"buttonFontSize": @22, @"buttonFontWeight": @"800"}]; self.uut = [[RNNRootViewController alloc] initWithLayoutInfo:nil rootViewCreator:nil eventEmitter:nil presenter:[RNNViewControllerPresenter new] options:self.options defaultOptions:nil]; - RNNNavigationController* nav = [[RNNNavigationController alloc] initWithLayoutInfo:nil childViewControllers:@[self.uut] options:nil defaultOptions:nil presenter:nil]; + RNNNavigationController* nav = [[RNNNavigationController alloc] initWithLayoutInfo:nil creator:_creator childViewControllers:@[self.uut] options:nil defaultOptions:nil presenter:nil]; RNNUIBarButtonItem* button = (RNNUIBarButtonItem*)[nav.topViewController.navigationItem.rightBarButtonItems objectAtIndex:0]; NSString* expectedButtonId = @"testId"; @@ -446,7 +446,7 @@ -(void)testRightButtonsWithTitle_withStyle { -(void)testLeftButtonsWithTitle_withoutStyle { self.options.topBar.leftButtons = @[@{@"id": @"testId", @"text": @"test"}]; self.uut = [[RNNRootViewController alloc] initWithLayoutInfo:nil rootViewCreator:nil eventEmitter:nil presenter:[RNNViewControllerPresenter new] options:self.options defaultOptions:nil]; - RNNNavigationController* nav = [[RNNNavigationController alloc] initWithLayoutInfo:nil childViewControllers:@[self.uut] options:nil defaultOptions:nil presenter:nil]; + RNNNavigationController* nav = [[RNNNavigationController alloc] initWithLayoutInfo:nil creator:_creator childViewControllers:@[self.uut] options:nil defaultOptions:nil presenter:nil]; RNNUIBarButtonItem* button = (RNNUIBarButtonItem*)[nav.topViewController.navigationItem.leftBarButtonItems objectAtIndex:0]; NSString* expectedButtonId = @"testId"; @@ -461,7 +461,7 @@ -(void)testLeftButtonsWithTitle_withStyle { self.options.topBar.leftButtons = @[@{@"id": @"testId", @"text": @"test", @"enabled": @false, @"buttonColor": inputColor, @"buttonFontSize": @22, @"buttonFontWeight": @"800"}]; self.uut = [[RNNRootViewController alloc] initWithLayoutInfo:nil rootViewCreator:nil eventEmitter:nil presenter:[RNNViewControllerPresenter new] options:self.options defaultOptions:nil]; - RNNNavigationController* nav = [[RNNNavigationController alloc] initWithLayoutInfo:nil childViewControllers:@[self.uut] options:nil defaultOptions:nil presenter:nil]; + RNNNavigationController* nav = [[RNNNavigationController alloc] initWithLayoutInfo:nil creator:_creator childViewControllers:@[self.uut] options:nil defaultOptions:nil presenter:nil]; RNNUIBarButtonItem* button = (RNNUIBarButtonItem*)[nav.topViewController.navigationItem.leftBarButtonItems objectAtIndex:0]; NSString* expectedButtonId = @"testId"; @@ -565,7 +565,7 @@ - (void)testOverrideOptions { - (RNNNavigationController *)createNavigationController { - RNNNavigationController* nav = [[RNNNavigationController alloc] initWithLayoutInfo:nil childViewControllers:@[self.uut] options:[[RNNNavigationOptions alloc] initEmptyOptions] defaultOptions:nil presenter:[[RNNNavigationControllerPresenter alloc] init]]; + RNNNavigationController* nav = [[RNNNavigationController alloc] initWithLayoutInfo:nil creator:nil childViewControllers:@[self.uut] options:[[RNNNavigationOptions alloc] initEmptyOptions] defaultOptions:nil presenter:[[RNNNavigationControllerPresenter alloc] init]]; return nav; } diff --git a/lib/ios/ReactNativeNavigationTests/RNNTestRootViewCreator.m b/lib/ios/ReactNativeNavigationTests/RNNTestRootViewCreator.m index 1f951018345..31081fc0e37 100644 --- a/lib/ios/ReactNativeNavigationTests/RNNTestRootViewCreator.m +++ b/lib/ios/ReactNativeNavigationTests/RNNTestRootViewCreator.m @@ -2,7 +2,7 @@ @implementation RNNTestRootViewCreator -- (UIView*)createRootView:(NSString*)name rootViewId:(NSString*)rootViewId { +- (UIView*)createRootView:(NSString*)name rootViewId:(NSString*)rootViewId reactViewReadyBlock:(RNNReactViewReadyCompletionBlock)reactViewReadyBlock { UIView *view = [[UIView alloc] init]; return view; } diff --git a/lib/ios/ReactNativeNavigationTests/RNNViewControllerPresenterTest.m b/lib/ios/ReactNativeNavigationTests/RNNViewControllerPresenterTest.m index a2bf80a310e..377ef1bf389 100644 --- a/lib/ios/ReactNativeNavigationTests/RNNViewControllerPresenterTest.m +++ b/lib/ios/ReactNativeNavigationTests/RNNViewControllerPresenterTest.m @@ -70,7 +70,7 @@ - (void)testApplyOptions_setOverlayTouchOutsideIfHasValue { - (void)testBindViewControllerShouldCreateNavigationButtonsCreator { RNNViewControllerPresenter* presenter = [[RNNViewControllerPresenter alloc] init]; - [presenter bindViewController:self.bindedViewController viewCreator:nil]; + [presenter bindViewController:self.bindedViewController]; XCTAssertNotNil(presenter.navigationButtons); } diff --git a/lib/ios/UIViewController+RNNOptions.m b/lib/ios/UIViewController+RNNOptions.m index 427acd9a26c..d63ea188a77 100644 --- a/lib/ios/UIViewController+RNNOptions.m +++ b/lib/ios/UIViewController+RNNOptions.m @@ -43,6 +43,7 @@ - (void)rnn_setSearchBarWithPlaceholder:(NSString *)placeholder } search.hidesNavigationBarDuringPresentation = hideNavBarOnFocusSearchBar; self.navigationItem.searchController = search; + [self.navigationItem setHidesSearchBarWhenScrolling:NO]; // Fixes #3450, otherwise, UIKit will infer the presentation context to be the root most view controller self.definesPresentationContext = YES; diff --git a/playground/src/app.js b/playground/src/app.js index 20acb251e62..9b742d75430 100644 --- a/playground/src/app.js +++ b/playground/src/app.js @@ -50,7 +50,8 @@ function start() { from: 0, to: 1, duration: 300 - } + }, + waitForRender: true }, _push: { topBar: {