react-native-navigation的迁移库

RCCNavigationController.m 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
  1. #import "RCCNavigationController.h"
  2. #import "RCCViewController.h"
  3. #import "RCCManager.h"
  4. #import <React/RCTEventDispatcher.h>
  5. #import <React/RCTUIManager.h>
  6. #if __has_include(<React/RCTUIManagerUtils.h>)
  7. #import <React/RCTUIManagerUtils.h>
  8. #endif
  9. #import <React/RCTConvert.h>
  10. #import <React/RCTRootView.h>
  11. #import <objc/runtime.h>
  12. #import "RCCTitleViewHelper.h"
  13. #import "RCCCustomBarButtonItem.h"
  14. #import "UIViewController+Rotation.h"
  15. #import "RCTHelpers.h"
  16. #import "RCTConvert+UIBarButtonSystemItem.h"
  17. @implementation RCCNavigationController
  18. {
  19. BOOL _transitioning;
  20. NSMutableArray *_queuedViewControllers;
  21. }
  22. NSString const *CALLBACK_ASSOCIATED_KEY = @"RCCNavigationController.CALLBACK_ASSOCIATED_KEY";
  23. NSString const *CALLBACK_ASSOCIATED_ID = @"RCCNavigationController.CALLBACK_ASSOCIATED_ID";
  24. -(UIInterfaceOrientationMask)supportedInterfaceOrientations {
  25. return [self supportedControllerOrientations];
  26. }
  27. - (instancetype)initWithProps:(NSDictionary *)props children:(NSArray *)children globalProps:(NSDictionary*)globalProps bridge:(RCTBridge *)bridge
  28. {
  29. _queuedViewControllers = [NSMutableArray new];
  30. NSString *component = props[@"component"];
  31. if (!component) return nil;
  32. NSDictionary *passProps = props[@"passProps"];
  33. NSDictionary *navigatorStyle = props[@"style"];
  34. RCCViewController *viewController = [[RCCViewController alloc] initWithComponent:component passProps:passProps navigatorStyle:navigatorStyle globalProps:globalProps bridge:bridge];
  35. if (!viewController) return nil;
  36. viewController.controllerId = passProps[@"screenInstanceID"];
  37. NSArray *leftButtons = props[@"leftButtons"];
  38. if (leftButtons)
  39. {
  40. [self setButtons:leftButtons viewController:viewController side:@"left" animated:NO];
  41. }
  42. NSArray *rightButtons = props[@"rightButtons"];
  43. if (rightButtons)
  44. {
  45. [self setButtons:rightButtons viewController:viewController side:@"right" animated:NO];
  46. }
  47. self = [super initWithRootViewController:viewController];
  48. if (!self) return nil;
  49. self.delegate = self;
  50. self.navigationBar.translucent = NO; // default
  51. [self processTitleView:viewController
  52. props:props
  53. style:navigatorStyle];
  54. [self setRotation:props];
  55. NSArray* components = props[@"components"];
  56. if (components.count) {
  57. for (NSDictionary* component in components) {
  58. [self performAction:@"push" actionParams:@{@"animated": @(0), @"component": component[@"screen"]} bridge:bridge];
  59. }
  60. }
  61. return self;
  62. }
  63. - (void)performAction:(NSString*)performAction actionParams:(NSDictionary*)actionParams bridge:(RCTBridge *)bridge
  64. {
  65. BOOL animated = actionParams[@"animated"] ? [actionParams[@"animated"] boolValue] : YES;
  66. // push
  67. if ([performAction isEqualToString:@"push"])
  68. {
  69. NSString *component = actionParams[@"component"];
  70. if (!component) return;
  71. NSMutableDictionary *passProps = [actionParams[@"passProps"] mutableCopy];
  72. passProps[GLOBAL_SCREEN_ACTION_COMMAND_TYPE] = COMMAND_TYPE_PUSH;
  73. passProps[GLOBAL_SCREEN_ACTION_TIMESTAMP] = actionParams[GLOBAL_SCREEN_ACTION_TIMESTAMP];
  74. NSDictionary *navigatorStyle = actionParams[@"style"];
  75. NSNumber *keepStyleAcrossPush = [[RCCManager sharedInstance] getAppStyle][@"keepStyleAcrossPush"];
  76. BOOL keepStyleAcrossPushBool = keepStyleAcrossPush ? [keepStyleAcrossPush boolValue] : YES;
  77. if (keepStyleAcrossPushBool) {
  78. if ([self.topViewController isKindOfClass:[RCCViewController class]])
  79. {
  80. RCCViewController *parent = (RCCViewController*)self.topViewController;
  81. NSMutableDictionary *mergedStyle = [NSMutableDictionary dictionaryWithDictionary:parent.navigatorStyle];
  82. // there are a few styles that we don't want to remember from our parent (they should be local)
  83. [mergedStyle removeObjectForKey:@"navBarHidden"];
  84. [mergedStyle removeObjectForKey:@"statusBarHidden"];
  85. [mergedStyle removeObjectForKey:@"navBarHideOnScroll"];
  86. [mergedStyle removeObjectForKey:@"drawUnderNavBar"];
  87. [mergedStyle removeObjectForKey:@"drawUnderTabBar"];
  88. [mergedStyle removeObjectForKey:@"statusBarBlur"];
  89. [mergedStyle removeObjectForKey:@"navBarBlur"];
  90. [mergedStyle removeObjectForKey:@"navBarTranslucent"];
  91. [mergedStyle removeObjectForKey:@"statusBarHideWithNavBar"];
  92. [mergedStyle removeObjectForKey:@"autoAdjustScrollViewInsets"];
  93. [mergedStyle removeObjectForKey:@"statusBarTextColorSchemeSingleScreen"];
  94. [mergedStyle removeObjectForKey:@"disabledBackGesture"];
  95. [mergedStyle removeObjectForKey:@"disabledSimultaneousGesture"];
  96. [mergedStyle removeObjectForKey:@"navBarCustomView"];
  97. [mergedStyle removeObjectForKey:@"navBarComponentAlignment"];
  98. [mergedStyle addEntriesFromDictionary:navigatorStyle];
  99. navigatorStyle = mergedStyle;
  100. }
  101. }
  102. RCCViewController *viewController = [[RCCViewController alloc] initWithComponent:component passProps:passProps navigatorStyle:navigatorStyle globalProps:nil bridge:bridge];
  103. viewController.controllerId = passProps[@"screenInstanceID"];
  104. [self processTitleView:viewController
  105. props:actionParams
  106. style:navigatorStyle];
  107. NSString *backButtonTitle = actionParams[@"backButtonTitle"];
  108. if (!backButtonTitle) {
  109. NSNumber *hideBackButtonTitle = [[RCCManager sharedInstance] getAppStyle][@"hideBackButtonTitle"];
  110. BOOL hideBackButtonTitleBool = hideBackButtonTitle ? [hideBackButtonTitle boolValue] : NO;
  111. backButtonTitle = hideBackButtonTitleBool ? @"" : backButtonTitle;
  112. }
  113. if (backButtonTitle)
  114. {
  115. UIBarButtonItem *backItem = [[UIBarButtonItem alloc] initWithTitle:backButtonTitle
  116. style:UIBarButtonItemStylePlain
  117. target:nil
  118. action:nil];
  119. self.topViewController.navigationItem.backBarButtonItem = backItem;
  120. }
  121. else
  122. {
  123. self.topViewController.navigationItem.backBarButtonItem = nil;
  124. }
  125. NSNumber *backButtonHidden = actionParams[@"backButtonHidden"];
  126. BOOL backButtonHiddenBool = backButtonHidden ? [backButtonHidden boolValue] : NO;
  127. if (backButtonHiddenBool)
  128. {
  129. viewController.navigationItem.hidesBackButton = YES;
  130. }
  131. NSArray *leftButtons = actionParams[@"leftButtons"];
  132. if (leftButtons)
  133. {
  134. [self setButtons:leftButtons viewController:viewController side:@"left" animated:NO];
  135. }
  136. NSArray *rightButtons = actionParams[@"rightButtons"];
  137. if (rightButtons)
  138. {
  139. [self setButtons:rightButtons viewController:viewController side:@"right" animated:NO];
  140. }
  141. NSArray *previewActions = actionParams[@"previewActions"];
  142. NSString *previewViewID = actionParams[@"previewViewID"];
  143. if (previewViewID) {
  144. if ([self.topViewController isKindOfClass:[RCCViewController class]])
  145. {
  146. RCCViewController *topViewController = ((RCCViewController*)self.topViewController);
  147. topViewController.previewController = nil;
  148. [topViewController.navigationController unregisterForPreviewingWithContext:topViewController.previewContext];
  149. viewController.previewActions = previewActions;
  150. viewController.previewCommit = actionParams[@"previewCommit"] ? [actionParams[@"previewCommit"] boolValue] : YES;
  151. NSNumber *previewHeight = actionParams[@"previewHeight"];
  152. if (previewHeight) {
  153. viewController.preferredContentSize = CGSizeMake(viewController.view.frame.size.width, [previewHeight floatValue]);
  154. }
  155. if (topViewController.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable)
  156. {
  157. dispatch_async(RCTGetUIManagerQueue(), ^{
  158. [bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
  159. UIView *view = viewRegistry[previewViewID];
  160. topViewController.previewView = view;
  161. topViewController.previewContext = [topViewController registerForPreviewingWithDelegate:(id)topViewController sourceView:view];
  162. }];
  163. });
  164. topViewController.previewController = viewController;
  165. }
  166. return;
  167. }
  168. }
  169. NSString *animationType = actionParams[@"animationType"];
  170. if ([animationType isEqualToString:@"fade"])
  171. {
  172. CATransition *transition = [CATransition animation];
  173. transition.duration = 0.25;
  174. transition.type = kCATransitionFade;
  175. [self.view.layer addAnimation:transition forKey:kCATransition];
  176. [self pushViewController:viewController animated:NO];
  177. }
  178. else
  179. {
  180. [self pushViewController:viewController animated:animated];
  181. }
  182. return;
  183. }
  184. // pop
  185. if ([performAction isEqualToString:@"pop"])
  186. {
  187. NSString *animationType = actionParams[@"animationType"];
  188. if ([animationType isEqualToString:@"fade"])
  189. {
  190. CATransition *transition = [CATransition animation];
  191. transition.duration = 0.25;
  192. transition.type = kCATransitionFade;
  193. [self.view.layer addAnimation:transition forKey:kCATransition];
  194. [self popViewControllerAnimated:NO];
  195. }
  196. else
  197. {
  198. [self popViewControllerAnimated:animated];
  199. }
  200. return;
  201. }
  202. // popToRoot
  203. if ([performAction isEqualToString:@"popToRoot"])
  204. {
  205. NSString *animationType = actionParams[@"animationType"];
  206. if ([animationType isEqualToString:@"fade"])
  207. {
  208. CATransition *transition = [CATransition animation];
  209. transition.duration = 0.25;
  210. transition.type = kCATransitionFade;
  211. [self.view.layer addAnimation:transition forKey:kCATransition];
  212. [self popToRootViewControllerAnimated:NO];
  213. }
  214. else
  215. {
  216. [self popToRootViewControllerAnimated:animated];
  217. }
  218. return;
  219. }
  220. // resetTo
  221. if ([performAction isEqualToString:@"resetTo"])
  222. {
  223. NSArray<UIViewController *> *viewControllers;
  224. NSString *component = actionParams[@"component"];
  225. NSArray<NSDictionary *> *componentConfigs = actionParams[@"components"];
  226. if (component) {
  227. NSMutableDictionary *passProps = [actionParams[@"passProps"] mutableCopy];
  228. passProps[@"commandType"] = @"resetTo";
  229. NSDictionary *style = actionParams[@"style"];
  230. NSArray *leftButtons = actionParams[@"leftButtons"];
  231. NSArray *rightButtons = actionParams[@"rightButtons"];
  232. UIViewController *viewController = [self viewControllerWithComponent:component
  233. props:[passProps copy]
  234. style:style
  235. leftButtons:leftButtons
  236. rightButtons:rightButtons
  237. bridge:bridge];
  238. viewControllers = @[viewController];
  239. } else if (componentConfigs) {
  240. NSMutableArray *mutableViewControllers = [NSMutableArray arrayWithCapacity:[componentConfigs count]];
  241. [componentConfigs enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull config, NSUInteger idx, BOOL * _Nonnull stop) {
  242. NSString *component = config[@"component"];
  243. NSMutableDictionary *props = [config[@"passProps"] mutableCopy];
  244. props[@"commandType"] = @"resetTo";
  245. NSDictionary *style = config[@"style"];
  246. NSArray *leftButtons = config[@"leftButtons"];
  247. NSArray *rightButtons = config[@"rightButtons"];
  248. UIViewController *viewController = [self viewControllerWithComponent:component
  249. props:[props copy]
  250. style:style
  251. leftButtons:leftButtons
  252. rightButtons:rightButtons
  253. bridge:bridge];
  254. [mutableViewControllers addObject:viewController];
  255. }];
  256. viewControllers = [mutableViewControllers copy];
  257. }
  258. if (!viewControllers) return;
  259. BOOL animated = actionParams[@"animated"] ? [actionParams[@"animated"] boolValue] : YES;
  260. NSString *animationType = actionParams[@"animationType"];
  261. if ([animationType isEqualToString:@"fade"])
  262. {
  263. CATransition *transition = [CATransition animation];
  264. transition.duration = 0.25;
  265. transition.type = kCATransitionFade;
  266. [self.view.layer addAnimation:transition forKey:kCATransition];
  267. [self setViewControllers:viewControllers animated:NO];
  268. }
  269. else
  270. {
  271. [self setViewControllers:viewControllers animated:animated];
  272. }
  273. return;
  274. }
  275. // setButtons
  276. if ([performAction isEqualToString:@"setButtons"])
  277. {
  278. NSArray *buttons = actionParams[@"buttons"];
  279. BOOL animated = actionParams[@"animated"] ? [actionParams[@"animated"] boolValue] : YES;
  280. NSString *side = actionParams[@"side"] ? actionParams[@"side"] : @"left";
  281. [self setButtons:buttons viewController:self.topViewController side:side animated:animated];
  282. return;
  283. }
  284. // setTitle
  285. if ([performAction isEqualToString:@"setTitle"] || [performAction isEqualToString:@"setTitleImage"])
  286. {
  287. NSDictionary *navigatorStyle = actionParams[@"style"];
  288. [self processTitleView:self.topViewController
  289. props:actionParams
  290. style:navigatorStyle];
  291. return;
  292. }
  293. // toggleNavBar
  294. if ([performAction isEqualToString:@"setHidden"]) {
  295. NSNumber *animated = actionParams[@"animated"];
  296. BOOL animatedBool = animated ? [animated boolValue] : YES;
  297. NSNumber *setHidden = actionParams[@"hidden"];
  298. BOOL isHiddenBool = setHidden ? [setHidden boolValue] : NO;
  299. RCCViewController *topViewController = ((RCCViewController*)self.topViewController);
  300. topViewController.navigatorStyle[@"navBarHidden"] = setHidden;
  301. [topViewController setNavBarVisibilityChange:animatedBool];
  302. }
  303. // setStyle
  304. if ([performAction isEqualToString:@"setStyle"])
  305. {
  306. NSDictionary *navigatorStyle = actionParams;
  307. // merge the navigatorStyle of our parent
  308. if ([self.topViewController isKindOfClass:[RCCViewController class]])
  309. {
  310. RCCViewController *parent = (RCCViewController*)self.topViewController;
  311. NSMutableDictionary *mergedStyle = [NSMutableDictionary dictionaryWithDictionary:parent.navigatorStyle];
  312. // there are a few styles that we don't want to remember from our parent (they should be local)
  313. [mergedStyle setValuesForKeysWithDictionary:navigatorStyle];
  314. navigatorStyle = mergedStyle;
  315. parent.navigatorStyle = navigatorStyle;
  316. [parent setStyleOnInit];
  317. [parent updateStyle];
  318. }
  319. }
  320. }
  321. - (UIViewController *)viewControllerWithComponent:(NSString *)component
  322. props:(NSDictionary *)props
  323. style:(NSDictionary *)style
  324. leftButtons:(NSArray<NSDictionary *> *)leftButtons
  325. rightButtons:(NSArray<NSDictionary *> *)rightButtons
  326. bridge:(RCTBridge *)bridge
  327. {
  328. RCCViewController *viewController = [[RCCViewController alloc] initWithComponent:component
  329. passProps:props
  330. navigatorStyle:style
  331. globalProps:nil
  332. bridge:bridge];
  333. viewController.controllerId = props[@"screenInstanceID"];
  334. viewController.navigationItem.hidesBackButton = YES;
  335. [self processTitleView:viewController
  336. props:props
  337. style:style];
  338. if (leftButtons)
  339. {
  340. [self setButtons:leftButtons viewController:viewController side:@"left" animated:NO];
  341. }
  342. if (rightButtons)
  343. {
  344. [self setButtons:rightButtons viewController:viewController side:@"right" animated:NO];
  345. }
  346. return viewController;
  347. }
  348. -(void)onButtonPress:(UIBarButtonItem*)barButtonItem
  349. {
  350. NSString *callbackId = objc_getAssociatedObject(barButtonItem, &CALLBACK_ASSOCIATED_KEY);
  351. if (!callbackId) return;
  352. NSString *buttonId = objc_getAssociatedObject(barButtonItem, &CALLBACK_ASSOCIATED_ID);
  353. [[[RCCManager sharedInstance] getBridge].eventDispatcher sendAppEventWithName:callbackId body:@
  354. {
  355. @"type": @"NavBarButtonPress",
  356. @"id": buttonId ? buttonId : [NSNull null]
  357. }];
  358. }
  359. -(void)setButtons:(NSArray*)buttons viewController:(UIViewController*)viewController side:(NSString*)side animated:(BOOL)animated
  360. {
  361. NSMutableArray *barButtonItems = [NSMutableArray new];
  362. for (NSDictionary *button in buttons)
  363. {
  364. NSString *title = button[@"title"];
  365. UIImage *iconImage = nil;
  366. id icon = button[@"icon"];
  367. if (icon) iconImage = [RCTConvert UIImage:icon];
  368. NSString *__nullable component = button[@"component"];
  369. NSString *__nullable systemItemName = button[@"systemItem"];
  370. UIBarButtonSystemItem systemItem = [RCTConvert UIBarButtonSystemItem:systemItemName];
  371. UIBarButtonItem *barButtonItem;
  372. if (iconImage)
  373. {
  374. barButtonItem = [[UIBarButtonItem alloc] initWithImage:iconImage style:UIBarButtonItemStylePlain target:self action:@selector(onButtonPress:)];
  375. }
  376. else if (title)
  377. {
  378. barButtonItem = [[UIBarButtonItem alloc] initWithTitle:title style:UIBarButtonItemStylePlain target:self action:@selector(onButtonPress:)];
  379. NSMutableDictionary *buttonTextAttributes = [RCTHelpers textAttributesFromDictionary:button withPrefix:@"button"];
  380. if (buttonTextAttributes.allKeys.count > 0) {
  381. [barButtonItem setTitleTextAttributes:buttonTextAttributes forState:UIControlStateNormal];
  382. [barButtonItem setTitleTextAttributes:buttonTextAttributes forState:UIControlStateHighlighted];
  383. }
  384. }
  385. else if (component) {
  386. RCTBridge *bridge = [[RCCManager sharedInstance] getBridge];
  387. barButtonItem = [[RCCCustomBarButtonItem alloc] initWithComponentName:component passProps:button[@"passProps"] bridge:bridge];
  388. }
  389. else if (systemItemName) {
  390. barButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:systemItem target:self action:@selector(onButtonPress:)];
  391. }
  392. else continue;
  393. objc_setAssociatedObject(barButtonItem, &CALLBACK_ASSOCIATED_KEY, button[@"onPress"], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  394. [barButtonItems addObject:barButtonItem];
  395. NSString *buttonId = button[@"id"];
  396. if (buttonId)
  397. {
  398. objc_setAssociatedObject(barButtonItem, &CALLBACK_ASSOCIATED_ID, buttonId, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  399. }
  400. NSNumber *disabled = button[@"disabled"];
  401. BOOL disabledBool = disabled ? [disabled boolValue] : NO;
  402. if (disabledBool) {
  403. [barButtonItem setEnabled:NO];
  404. }
  405. NSNumber *disableIconTintString = button[@"disableIconTint"];
  406. BOOL disableIconTint = disableIconTintString ? [disableIconTintString boolValue] : NO;
  407. if (disableIconTint) {
  408. [barButtonItem setImage:[barButtonItem.image imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]];
  409. }
  410. if ([viewController isKindOfClass:[RCCViewController class]]) {
  411. RCCViewController *rccViewController = ((RCCViewController*)viewController);
  412. NSDictionary *navigatorStyle = rccViewController.navigatorStyle;
  413. id disabledButtonColor = navigatorStyle[@"disabledButtonColor"];
  414. if (disabledButtonColor) {
  415. UIColor *color = [RCTConvert UIColor:disabledButtonColor];
  416. [barButtonItem setTitleTextAttributes:@{NSForegroundColorAttributeName : color} forState:UIControlStateDisabled];
  417. }
  418. }
  419. NSString *testID = button[@"testID"];
  420. if (testID)
  421. {
  422. barButtonItem.accessibilityIdentifier = testID;
  423. }
  424. }
  425. if ([side isEqualToString:@"left"])
  426. {
  427. [viewController.navigationItem setLeftBarButtonItems:barButtonItems animated:animated];
  428. }
  429. if ([side isEqualToString:@"right"])
  430. {
  431. [viewController.navigationItem setRightBarButtonItems:barButtonItems animated:animated];
  432. }
  433. }
  434. -(void)processTitleView:(UIViewController*)viewController
  435. props:(NSDictionary*)props
  436. style:(NSDictionary*)style
  437. {
  438. BOOL isSetSubtitleBool = props[@"isSetSubtitle"] ? [props[@"isSetSubtitle"] boolValue] : NO;
  439. RCCTitleViewHelper *titleViewHelper = [[RCCTitleViewHelper alloc] init:viewController
  440. navigationController:self
  441. title:props[@"title"]
  442. subtitle:props[@"subtitle"]
  443. titleImageData:props[@"titleImage"]
  444. isSetSubtitle:isSetSubtitleBool];
  445. [titleViewHelper setup:style];
  446. }
  447. - (UIStatusBarStyle)preferredStatusBarStyle {
  448. return [self.topViewController preferredStatusBarStyle];
  449. }
  450. - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
  451. {
  452. if(_transitioning)
  453. {
  454. NSDictionary *pushDetails =@{ @"viewController": viewController, @"animated": @(animated) };
  455. [_queuedViewControllers addObject:pushDetails];
  456. return;
  457. }
  458. _transitioning = YES;
  459. [super pushViewController:viewController animated:animated];
  460. }
  461. #pragma mark - UINavigationControllerDelegate
  462. -(void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
  463. [viewController setNeedsStatusBarAppearanceUpdate];
  464. }
  465. - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
  466. {
  467. dispatch_async(dispatch_get_main_queue(), ^{
  468. _transitioning = NO;
  469. if ([_queuedViewControllers count] > 0) {
  470. NSDictionary *toPushDetails = [_queuedViewControllers firstObject];
  471. [_queuedViewControllers removeObjectAtIndex:0];
  472. [self pushViewController:toPushDetails[@"viewController"] animated:[toPushDetails[@"animated"] boolValue]];
  473. }
  474. });
  475. }
  476. @end