react-native-navigation的迁移库

RCCNavigationController.m 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. #import "RCCNavigationController.h"
  2. #import "RCCViewController.h"
  3. #import "RCCManager.h"
  4. #import <React/RCTEventDispatcher.h>
  5. #import <React/RCTConvert.h>
  6. #import <objc/runtime.h>
  7. #import "RCCTitleViewHelper.h"
  8. #import "UIViewController+Rotation.h"
  9. @implementation RCCNavigationController
  10. {
  11. BOOL _transitioning;
  12. NSMutableArray *_queuedViewControllers;
  13. }
  14. NSString const *CALLBACK_ASSOCIATED_KEY = @"RCCNavigationController.CALLBACK_ASSOCIATED_KEY";
  15. NSString const *CALLBACK_ASSOCIATED_ID = @"RCCNavigationController.CALLBACK_ASSOCIATED_ID";
  16. -(UIInterfaceOrientationMask)supportedInterfaceOrientations {
  17. return [self supportedControllerOrientations];
  18. }
  19. - (instancetype)initWithProps:(NSDictionary *)props children:(NSArray *)children globalProps:(NSDictionary*)globalProps bridge:(RCTBridge *)bridge
  20. {
  21. _queuedViewControllers = [NSMutableArray new];
  22. NSString *component = props[@"component"];
  23. if (!component) return nil;
  24. NSDictionary *passProps = props[@"passProps"];
  25. NSDictionary *navigatorStyle = props[@"style"];
  26. RCCViewController *viewController = [[RCCViewController alloc] initWithComponent:component passProps:passProps navigatorStyle:navigatorStyle globalProps:globalProps bridge:bridge];
  27. if (!viewController) return nil;
  28. NSArray *leftButtons = props[@"leftButtons"];
  29. if (leftButtons)
  30. {
  31. [self setButtons:leftButtons viewController:viewController side:@"left" animated:NO];
  32. }
  33. NSArray *rightButtons = props[@"rightButtons"];
  34. if (rightButtons)
  35. {
  36. [self setButtons:rightButtons viewController:viewController side:@"right" animated:NO];
  37. }
  38. self = [super initWithRootViewController:viewController];
  39. if (!self) return nil;
  40. self.delegate = self;
  41. self.navigationBar.translucent = NO; // default
  42. [self processTitleView:viewController
  43. props:props
  44. style:navigatorStyle];
  45. [self setRotation:props];
  46. return self;
  47. }
  48. - (void)performAction:(NSString*)performAction actionParams:(NSDictionary*)actionParams bridge:(RCTBridge *)bridge
  49. {
  50. BOOL animated = actionParams[@"animated"] ? [actionParams[@"animated"] boolValue] : YES;
  51. // push
  52. if ([performAction isEqualToString:@"push"])
  53. {
  54. NSString *component = actionParams[@"component"];
  55. if (!component) return;
  56. NSDictionary *passProps = actionParams[@"passProps"];
  57. NSDictionary *navigatorStyle = actionParams[@"style"];
  58. // merge the navigatorStyle of our parent
  59. if ([self.topViewController isKindOfClass:[RCCViewController class]])
  60. {
  61. RCCViewController *parent = (RCCViewController*)self.topViewController;
  62. NSMutableDictionary *mergedStyle = [NSMutableDictionary dictionaryWithDictionary:parent.navigatorStyle];
  63. // there are a few styles that we don't want to remember from our parent (they should be local)
  64. [mergedStyle removeObjectForKey:@"navBarHidden"];
  65. [mergedStyle removeObjectForKey:@"statusBarHidden"];
  66. [mergedStyle removeObjectForKey:@"navBarHideOnScroll"];
  67. [mergedStyle removeObjectForKey:@"drawUnderNavBar"];
  68. [mergedStyle removeObjectForKey:@"drawUnderTabBar"];
  69. [mergedStyle removeObjectForKey:@"statusBarBlur"];
  70. [mergedStyle removeObjectForKey:@"navBarBlur"];
  71. [mergedStyle removeObjectForKey:@"navBarTranslucent"];
  72. [mergedStyle removeObjectForKey:@"statusBarHideWithNavBar"];
  73. [mergedStyle removeObjectForKey:@"autoAdjustScrollViewInsets"];
  74. [mergedStyle removeObjectForKey:@"statusBarTextColorSchemeSingleScreen"];
  75. [mergedStyle removeObjectForKey:@"disabledBackGesture"];
  76. [mergedStyle addEntriesFromDictionary:navigatorStyle];
  77. navigatorStyle = mergedStyle;
  78. }
  79. RCCViewController *viewController = [[RCCViewController alloc] initWithComponent:component passProps:passProps navigatorStyle:navigatorStyle globalProps:nil bridge:bridge];
  80. [self processTitleView:viewController
  81. props:actionParams
  82. style:navigatorStyle];
  83. NSString *backButtonTitle = actionParams[@"backButtonTitle"];
  84. if (backButtonTitle)
  85. {
  86. UIBarButtonItem *backItem = [[UIBarButtonItem alloc] initWithTitle:backButtonTitle
  87. style:UIBarButtonItemStylePlain
  88. target:nil
  89. action:nil];
  90. self.topViewController.navigationItem.backBarButtonItem = backItem;
  91. }
  92. else
  93. {
  94. self.topViewController.navigationItem.backBarButtonItem = nil;
  95. }
  96. NSNumber *backButtonHidden = actionParams[@"backButtonHidden"];
  97. BOOL backButtonHiddenBool = backButtonHidden ? [backButtonHidden boolValue] : NO;
  98. if (backButtonHiddenBool)
  99. {
  100. viewController.navigationItem.hidesBackButton = YES;
  101. }
  102. NSArray *leftButtons = actionParams[@"leftButtons"];
  103. if (leftButtons)
  104. {
  105. [self setButtons:leftButtons viewController:viewController side:@"left" animated:NO];
  106. }
  107. NSArray *rightButtons = actionParams[@"rightButtons"];
  108. if (rightButtons)
  109. {
  110. [self setButtons:rightButtons viewController:viewController side:@"right" animated:NO];
  111. }
  112. [self pushViewController:viewController animated:animated];
  113. return;
  114. }
  115. // pop
  116. if ([performAction isEqualToString:@"pop"])
  117. {
  118. [self popViewControllerAnimated:animated];
  119. return;
  120. }
  121. // popToRoot
  122. if ([performAction isEqualToString:@"popToRoot"])
  123. {
  124. [self popToRootViewControllerAnimated:animated];
  125. return;
  126. }
  127. // resetTo
  128. if ([performAction isEqualToString:@"resetTo"])
  129. {
  130. NSString *component = actionParams[@"component"];
  131. if (!component) return;
  132. NSDictionary *passProps = actionParams[@"passProps"];
  133. NSDictionary *navigatorStyle = actionParams[@"style"];
  134. RCCViewController *viewController = [[RCCViewController alloc] initWithComponent:component passProps:passProps navigatorStyle:navigatorStyle globalProps:nil bridge:bridge];
  135. [self processTitleView:viewController
  136. props:actionParams
  137. style:navigatorStyle];
  138. NSArray *leftButtons = actionParams[@"leftButtons"];
  139. if (leftButtons)
  140. {
  141. [self setButtons:leftButtons viewController:viewController side:@"left" animated:NO];
  142. }
  143. NSArray *rightButtons = actionParams[@"rightButtons"];
  144. if (rightButtons)
  145. {
  146. [self setButtons:rightButtons viewController:viewController side:@"right" animated:NO];
  147. }
  148. BOOL animated = actionParams[@"animated"] ? [actionParams[@"animated"] boolValue] : YES;
  149. [self setViewControllers:@[viewController] animated:animated];
  150. return;
  151. }
  152. // setButtons
  153. if ([performAction isEqualToString:@"setButtons"])
  154. {
  155. NSArray *buttons = actionParams[@"buttons"];
  156. BOOL animated = actionParams[@"animated"] ? [actionParams[@"animated"] boolValue] : YES;
  157. NSString *side = actionParams[@"side"] ? actionParams[@"side"] : @"left";
  158. [self setButtons:buttons viewController:self.topViewController side:side animated:animated];
  159. return;
  160. }
  161. // setTitle
  162. if ([performAction isEqualToString:@"setTitle"] || [performAction isEqualToString:@"setTitleImage"])
  163. {
  164. NSDictionary *navigatorStyle = actionParams[@"style"];
  165. [self processTitleView:self.topViewController
  166. props:actionParams
  167. style:navigatorStyle];
  168. return;
  169. }
  170. // toggleNavBar
  171. if ([performAction isEqualToString:@"setHidden"]) {
  172. NSNumber *animated = actionParams[@"animated"];
  173. BOOL animatedBool = animated ? [animated boolValue] : YES;
  174. NSNumber *setHidden = actionParams[@"hidden"];
  175. BOOL isHiddenBool = setHidden ? [setHidden boolValue] : NO;
  176. RCCViewController *topViewController = ((RCCViewController*)self.topViewController);
  177. topViewController.navigatorStyle[@"navBarHidden"] = setHidden;
  178. [topViewController setNavBarVisibilityChange:animatedBool];
  179. }
  180. // setStyle
  181. if ([performAction isEqualToString:@"setStyle"])
  182. {
  183. NSDictionary *navigatorStyle = actionParams;
  184. // merge the navigatorStyle of our parent
  185. if ([self.topViewController isKindOfClass:[RCCViewController class]])
  186. {
  187. RCCViewController *parent = (RCCViewController*)self.topViewController;
  188. NSMutableDictionary *mergedStyle = [NSMutableDictionary dictionaryWithDictionary:parent.navigatorStyle];
  189. // there are a few styles that we don't want to remember from our parent (they should be local)
  190. [mergedStyle setValuesForKeysWithDictionary:navigatorStyle];
  191. navigatorStyle = mergedStyle;
  192. parent.navigatorStyle = navigatorStyle;
  193. [parent setStyleOnInit];
  194. [parent updateStyle];
  195. }
  196. }
  197. }
  198. -(void)onButtonPress:(UIBarButtonItem*)barButtonItem
  199. {
  200. NSString *callbackId = objc_getAssociatedObject(barButtonItem, &CALLBACK_ASSOCIATED_KEY);
  201. if (!callbackId) return;
  202. NSString *buttonId = objc_getAssociatedObject(barButtonItem, &CALLBACK_ASSOCIATED_ID);
  203. [[[RCCManager sharedInstance] getBridge].eventDispatcher sendAppEventWithName:callbackId body:@
  204. {
  205. @"type": @"NavBarButtonPress",
  206. @"id": buttonId ? buttonId : [NSNull null]
  207. }];
  208. }
  209. -(void)setButtons:(NSArray*)buttons viewController:(UIViewController*)viewController side:(NSString*)side animated:(BOOL)animated
  210. {
  211. NSMutableArray *barButtonItems = [NSMutableArray new];
  212. for (NSDictionary *button in buttons)
  213. {
  214. NSString *title = button[@"title"];
  215. UIImage *iconImage = nil;
  216. id icon = button[@"icon"];
  217. if (icon) iconImage = [RCTConvert UIImage:icon];
  218. UIBarButtonItem *barButtonItem;
  219. if (iconImage)
  220. {
  221. barButtonItem = [[UIBarButtonItem alloc] initWithImage:iconImage style:UIBarButtonItemStylePlain target:self action:@selector(onButtonPress:)];
  222. }
  223. else if (title)
  224. {
  225. barButtonItem = [[UIBarButtonItem alloc] initWithTitle:title style:UIBarButtonItemStylePlain target:self action:@selector(onButtonPress:)];
  226. }
  227. else continue;
  228. objc_setAssociatedObject(barButtonItem, &CALLBACK_ASSOCIATED_KEY, button[@"onPress"], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  229. [barButtonItems addObject:barButtonItem];
  230. NSString *buttonId = button[@"id"];
  231. if (buttonId)
  232. {
  233. objc_setAssociatedObject(barButtonItem, &CALLBACK_ASSOCIATED_ID, buttonId, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  234. }
  235. NSNumber *disabled = button[@"disabled"];
  236. BOOL disabledBool = disabled ? [disabled boolValue] : NO;
  237. if (disabledBool) {
  238. [barButtonItem setEnabled:NO];
  239. }
  240. NSNumber *disableIconTintString = button[@"disableIconTint"];
  241. BOOL disableIconTint = disableIconTintString ? [disableIconTintString boolValue] : NO;
  242. if (disableIconTint) {
  243. [barButtonItem setImage:[barButtonItem.image imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]];
  244. }
  245. NSString *testID = button[@"testID"];
  246. if (testID)
  247. {
  248. barButtonItem.accessibilityIdentifier = testID;
  249. }
  250. }
  251. if ([side isEqualToString:@"left"])
  252. {
  253. [viewController.navigationItem setLeftBarButtonItems:barButtonItems animated:animated];
  254. }
  255. if ([side isEqualToString:@"right"])
  256. {
  257. [viewController.navigationItem setRightBarButtonItems:barButtonItems animated:animated];
  258. }
  259. }
  260. -(void)processTitleView:(UIViewController*)viewController
  261. props:(NSDictionary*)props
  262. style:(NSDictionary*)style
  263. {
  264. RCCTitleViewHelper *titleViewHelper = [[RCCTitleViewHelper alloc] init:viewController
  265. navigationController:self
  266. title:props[@"title"]
  267. subtitle:props[@"subtitle"]
  268. titleImageData:props[@"titleImage"]];
  269. [titleViewHelper setup:style];
  270. }
  271. - (UIStatusBarStyle)preferredStatusBarStyle {
  272. return [self.topViewController preferredStatusBarStyle];
  273. }
  274. - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
  275. {
  276. if(_transitioning)
  277. {
  278. NSDictionary *pushDetails =@{ @"viewController": viewController, @"animated": @(animated) };
  279. [_queuedViewControllers addObject:pushDetails];
  280. return;
  281. }
  282. _transitioning = YES;
  283. [super pushViewController:viewController animated:animated];
  284. }
  285. #pragma mark - UINavigationControllerDelegate
  286. -(void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
  287. [viewController setNeedsStatusBarAppearanceUpdate];
  288. }
  289. - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
  290. {
  291. dispatch_async(dispatch_get_main_queue(), ^{
  292. _transitioning = NO;
  293. if ([_queuedViewControllers count] > 0) {
  294. NSDictionary *toPushDetails = [_queuedViewControllers firstObject];
  295. [_queuedViewControllers removeObjectAtIndex:0];
  296. [self pushViewController:toPushDetails[@"viewController"] animated:[toPushDetails[@"animated"] boolValue]];
  297. }
  298. });
  299. }
  300. @end