FLEXNavigationController.m 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. //
  2. // FLEXNavigationController.m
  3. // FLEX
  4. //
  5. // Created by Tanner on 1/30/20.
  6. // Copyright © 2020 FLEX Team. All rights reserved.
  7. //
  8. #import "FLEXNavigationController.h"
  9. #import "FLEXExplorerViewController.h"
  10. #import "FLEXTabList.h"
  11. #import "FLEXColor.h"
  12. #import "NSObject+FLEX_Reflection.h"
  13. @interface UINavigationController (Private) <UIGestureRecognizerDelegate>
  14. - (void)_gestureRecognizedInteractiveHide:(UIGestureRecognizer *)sender;
  15. @end
  16. @interface UIPanGestureRecognizer (Private)
  17. - (void)_setDelegate:(id)delegate;
  18. @end
  19. @interface FLEXNavigationController ()
  20. @property (nonatomic, readonly) BOOL toolbarWasHidden;
  21. @property (nonatomic) BOOL waitingToAddTab;
  22. @property (nonatomic) BOOL didSetupPendingDismissButtons;
  23. @property (nonatomic) UISwipeGestureRecognizer *navigationBarSwipeGesture;
  24. @end
  25. @implementation FLEXNavigationController
  26. + (instancetype)withRootViewController:(UIViewController *)rootVC {
  27. return [[self alloc] initWithRootViewController:rootVC];
  28. }
  29. - (void)viewDidLoad {
  30. [super viewDidLoad];
  31. self.waitingToAddTab = YES;
  32. // Add gesture to reveal toolbar if hidden
  33. UITapGestureRecognizer *navbarTapGesture = [[UITapGestureRecognizer alloc]
  34. initWithTarget:self action:@selector(handleNavigationBarTap:)
  35. ];
  36. // Don't cancel touches to work around bug on versions of iOS prior to 13
  37. navbarTapGesture.cancelsTouchesInView = NO;
  38. [self.navigationBar addGestureRecognizer:navbarTapGesture];
  39. // Add gesture to dismiss if not presented with a sheet style
  40. if (@available(iOS 13, *)) {
  41. switch (self.modalPresentationStyle) {
  42. case UIModalPresentationAutomatic:
  43. #if !TARGET_OS_TV
  44. case UIModalPresentationPageSheet:
  45. case UIModalPresentationFormSheet:
  46. #endif
  47. break;
  48. default:
  49. [self addNavigationBarSwipeGesture];
  50. break;
  51. }
  52. } else {
  53. [self addNavigationBarSwipeGesture];
  54. }
  55. }
  56. - (void)viewWillAppear:(BOOL)animated {
  57. [super viewWillAppear:animated];
  58. if (self.beingPresented && !self.didSetupPendingDismissButtons) {
  59. for (UIViewController *vc in self.viewControllers) {
  60. [self addNavigationBarItemsToViewController:vc.navigationItem];
  61. }
  62. self.didSetupPendingDismissButtons = YES;
  63. }
  64. #if TARGET_OS_TV
  65. if ([self darkMode]){
  66. self.view.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.8];
  67. } else {
  68. self.view.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.8];
  69. }
  70. #endif
  71. }
  72. - (void)viewDidAppear:(BOOL)animated {
  73. [super viewDidAppear:animated];
  74. if (self.waitingToAddTab) {
  75. // Only add new tab if we're presented properly
  76. if ([self.presentingViewController isKindOfClass:[FLEXExplorerViewController class]]) {
  77. // New navigation controllers always add themselves as new tabs,
  78. // tabs are closed by FLEXExplorerViewController
  79. [FLEXTabList.sharedList addTab:self];
  80. self.waitingToAddTab = NO;
  81. }
  82. }
  83. //the timing is janky here but its better than nothing for now.
  84. }
  85. - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
  86. [super pushViewController:viewController animated:animated];
  87. [self addNavigationBarItemsToViewController:viewController.navigationItem];
  88. }
  89. - (void)dismissAnimated {
  90. // Tabs are only closed if the done button is pressed; this
  91. // allows you to leave a tab open by dragging down to dismiss
  92. [FLEXTabList.sharedList closeTab:self];
  93. [self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
  94. }
  95. - (void)addNavigationBarItemsToViewController:(UINavigationItem *)navigationItem {
  96. if (!self.presentingViewController) {
  97. return;
  98. }
  99. // Check if a done item already exists
  100. for (UIBarButtonItem *item in navigationItem.rightBarButtonItems) {
  101. if (item.style == UIBarButtonItemStyleDone) {
  102. return;
  103. }
  104. }
  105. // Give root view controllers a Done button if it does not already have one
  106. UIBarButtonItem *done = [[UIBarButtonItem alloc]
  107. initWithBarButtonSystemItem:UIBarButtonSystemItemDone
  108. target:self
  109. action:@selector(dismissAnimated)
  110. ];
  111. // Prepend the button if other buttons exist already
  112. NSArray *existingItems = navigationItem.rightBarButtonItems;
  113. if (existingItems.count) {
  114. navigationItem.rightBarButtonItems = [@[done] arrayByAddingObjectsFromArray:existingItems];
  115. } else {
  116. navigationItem.rightBarButtonItem = done;
  117. }
  118. // Keeps us from calling this method again on
  119. // the same view controllers in -viewWillAppear:
  120. self.didSetupPendingDismissButtons = YES;
  121. }
  122. - (void)addNavigationBarSwipeGesture {
  123. #if !TARGET_OS_TV
  124. UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizer alloc]
  125. initWithTarget:self action:@selector(handleNavigationBarSwipe:)
  126. ];
  127. swipe.direction = UISwipeGestureRecognizerDirectionDown;
  128. swipe.delegate = self;
  129. self.navigationBarSwipeGesture = swipe;
  130. [self.navigationBar addGestureRecognizer:swipe];
  131. #endif
  132. }
  133. - (void)handleNavigationBarSwipe:(UISwipeGestureRecognizer *)sender {
  134. if (sender.state == UIGestureRecognizerStateRecognized) {
  135. [self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
  136. }
  137. }
  138. - (void)handleNavigationBarTap:(UIGestureRecognizer *)sender {
  139. if (sender.state == UIGestureRecognizerStateRecognized) {
  140. #if !TARGET_OS_TV
  141. if (self.toolbarHidden) {
  142. [self setToolbarHidden:NO animated:YES];
  143. }
  144. #endif
  145. }
  146. }
  147. #if !TARGET_OS_TV
  148. - (BOOL)gestureRecognizer:(UIGestureRecognizer *)g1 shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)g2 {
  149. if (g1 == self.navigationBarSwipeGesture && g2 == self.barHideOnSwipeGestureRecognizer) {
  150. return YES;
  151. }
  152. return NO;
  153. }
  154. #endif
  155. - (void)_gestureRecognizedInteractiveHide:(UIPanGestureRecognizer *)sender {
  156. #if !TARGET_OS_TV
  157. if (sender.state == UIGestureRecognizerStateRecognized) {
  158. BOOL show = self.topViewController.toolbarItems.count;
  159. CGFloat yTranslation = [sender translationInView:self.view].y;
  160. CGFloat yVelocity = [sender velocityInView:self.view].y;
  161. if (yVelocity > 2000) {
  162. [self setToolbarHidden:YES animated:YES];
  163. } else if (show && yTranslation > 20 && yVelocity > 250) {
  164. [self setToolbarHidden:NO animated:YES];
  165. } else if (yTranslation < -20) {
  166. [self setToolbarHidden:YES animated:YES];
  167. }
  168. }
  169. #endif
  170. }
  171. @end