FLEXNavigationController.m 6.1 KB

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