FLEXNavigationController.m 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. //
  2. // FLEXNavigationController.m
  3. // FLEX
  4. //
  5. // Created by Tanner on 1/30/20.
  6. // Copyright © 2020 Flipboard. 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) UISwipeGestureRecognizer *navigationBarSwipeGesture;
  21. @end
  22. @implementation FLEXNavigationController
  23. + (instancetype)withRootViewController:(UIViewController *)rootVC {
  24. return [[self alloc] initWithRootViewController:rootVC];
  25. }
  26. - (void)viewDidLoad {
  27. [super viewDidLoad];
  28. self.waitingToAddTab = YES;
  29. // Add gesture to dismiss if not presented with a sheet style
  30. if (@available(iOS 13, *)) {
  31. switch (self.modalPresentationStyle) {
  32. case UIModalPresentationAutomatic:
  33. case UIModalPresentationPageSheet:
  34. case UIModalPresentationFormSheet:
  35. break;
  36. default:
  37. [self addNavigationBarSwipeGesture];
  38. break;
  39. }
  40. } else {
  41. [self addNavigationBarSwipeGesture];
  42. }
  43. }
  44. - (void)viewDidAppear:(BOOL)animated {
  45. [super viewDidAppear:animated];
  46. if (self.waitingToAddTab) {
  47. // Only add new tab if we're presented properly
  48. if ([self.presentingViewController isKindOfClass:[FLEXExplorerViewController class]]) {
  49. // New navigation controllers always add themselves as new tabs,
  50. // tabs are closed by FLEXExplorerViewController
  51. [FLEXTabList.sharedList addTab:self];
  52. self.waitingToAddTab = NO;
  53. }
  54. }
  55. }
  56. - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
  57. [super pushViewController:viewController animated:animated];
  58. [self addNavigationBarItemsToViewController:viewController.navigationItem];
  59. }
  60. - (void)dismissAnimated {
  61. // Tabs are only closed if the done button is pressed; this
  62. // allows you to leave a tab open by dragging down to dismiss
  63. [FLEXTabList.sharedList closeTab:self];
  64. [self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
  65. }
  66. - (void)addNavigationBarItemsToViewController:(UINavigationItem *)navigationItem {
  67. // Check if a done item already exists
  68. for (UIBarButtonItem *item in navigationItem.rightBarButtonItems) {
  69. if (item.style == UIBarButtonItemStyleDone) {
  70. return;
  71. }
  72. }
  73. // Give root view controllers a Done button if it does not already have one
  74. UIBarButtonItem *done = [[UIBarButtonItem alloc]
  75. initWithBarButtonSystemItem:UIBarButtonSystemItemDone
  76. target:self
  77. action:@selector(dismissAnimated)
  78. ];
  79. // Prepend the button if other buttons exist already
  80. NSArray *existingItems = navigationItem.rightBarButtonItems;
  81. if (existingItems.count) {
  82. navigationItem.rightBarButtonItems = [@[done] arrayByAddingObjectsFromArray:existingItems];
  83. } else {
  84. navigationItem.rightBarButtonItem = done;
  85. }
  86. }
  87. - (void)addNavigationBarSwipeGesture {
  88. UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizer alloc]
  89. initWithTarget:self action:@selector(handleNavigationBarSwipe:)
  90. ];
  91. swipe.direction = UISwipeGestureRecognizerDirectionDown;
  92. swipe.delegate = self;
  93. self.navigationBarSwipeGesture = swipe;
  94. [self.navigationBar addGestureRecognizer:swipe];
  95. }
  96. - (void)handleNavigationBarSwipe:(UISwipeGestureRecognizer *)sender {
  97. [self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
  98. }
  99. - (BOOL)gestureRecognizer:(UIGestureRecognizer *)g1 shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)g2 {
  100. if (g1 == self.navigationBarSwipeGesture && g2 == self.barHideOnSwipeGestureRecognizer) {
  101. return YES;
  102. }
  103. return NO;
  104. }
  105. - (void)_gestureRecognizedInteractiveHide:(UIPanGestureRecognizer *)sender {
  106. if (sender.state == UIGestureRecognizerStateRecognized) {
  107. BOOL show = self.topViewController.toolbarItems.count;
  108. CGFloat yTranslation = [sender translationInView:self.view].y;
  109. CGFloat yVelocity = [sender velocityInView:self.view].y;
  110. if (yVelocity > 2000) {
  111. [self setToolbarHidden:YES animated:YES];
  112. } else if (show && yTranslation > 20 && yVelocity > 250) {
  113. [self setToolbarHidden:NO animated:YES];
  114. } else if (yTranslation < -20) {
  115. [self setToolbarHidden:YES animated:YES];
  116. }
  117. }
  118. }
  119. @end