123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 |
- //
- // FLEXNavigationController.m
- // FLEX
- //
- // Created by Tanner on 1/30/20.
- // Copyright © 2020 FLEX Team. All rights reserved.
- //
- #import "FLEXNavigationController.h"
- #import "FLEXExplorerViewController.h"
- #import "FLEXTabList.h"
- #import "FLEXColor.h"
- #import "NSObject+FLEX_Reflection.h"
- @interface UINavigationController (Private) <UIGestureRecognizerDelegate>
- - (void)_gestureRecognizedInteractiveHide:(UIGestureRecognizer *)sender;
- @end
- @interface UIPanGestureRecognizer (Private)
- - (void)_setDelegate:(id)delegate;
- @end
- @interface FLEXNavigationController ()
- @property (nonatomic, readonly) BOOL toolbarWasHidden;
- @property (nonatomic) BOOL waitingToAddTab;
- @property (nonatomic) BOOL didSetupPendingDismissButtons;
- @property (nonatomic) UISwipeGestureRecognizer *navigationBarSwipeGesture;
- @end
- @implementation FLEXNavigationController
- + (instancetype)withRootViewController:(UIViewController *)rootVC {
- return [[self alloc] initWithRootViewController:rootVC];
- }
- - (void)viewDidLoad {
- [super viewDidLoad];
-
- self.waitingToAddTab = YES;
-
- // Add gesture to reveal toolbar if hidden
- UITapGestureRecognizer *navbarTapGesture = [[UITapGestureRecognizer alloc]
- initWithTarget:self action:@selector(handleNavigationBarTap:)
- ];
-
- // Don't cancel touches to work around bug on versions of iOS prior to 13
- navbarTapGesture.cancelsTouchesInView = NO;
- [self.navigationBar addGestureRecognizer:navbarTapGesture];
-
- // Add gesture to dismiss if not presented with a sheet style
- if (@available(iOS 13, *)) {
- switch (self.modalPresentationStyle) {
- case UIModalPresentationAutomatic:
- #if !TARGET_OS_TV
- case UIModalPresentationPageSheet:
- case UIModalPresentationFormSheet:
- #endif
- break;
-
- default:
- [self addNavigationBarSwipeGesture];
- break;
- }
- } else {
- [self addNavigationBarSwipeGesture];
- }
- }
- - (void)viewWillAppear:(BOOL)animated {
- [super viewWillAppear:animated];
-
- if (self.beingPresented && !self.didSetupPendingDismissButtons) {
- for (UIViewController *vc in self.viewControllers) {
- [self addNavigationBarItemsToViewController:vc.navigationItem];
- }
-
- self.didSetupPendingDismissButtons = YES;
- }
- #if TARGET_OS_TV
- if ([self darkMode]){
- self.view.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.8];
- } else {
- self.view.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.8];
- }
- #endif
-
- }
- - (void)viewDidAppear:(BOOL)animated {
- [super viewDidAppear:animated];
-
- if (self.waitingToAddTab) {
- // Only add new tab if we're presented properly
- if ([self.presentingViewController isKindOfClass:[FLEXExplorerViewController class]]) {
- // New navigation controllers always add themselves as new tabs,
- // tabs are closed by FLEXExplorerViewController
- [FLEXTabList.sharedList addTab:self];
- self.waitingToAddTab = NO;
- }
- }
- //the timing is janky here but its better than nothing for now.
- }
- - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
- [super pushViewController:viewController animated:animated];
- [self addNavigationBarItemsToViewController:viewController.navigationItem];
- }
- - (void)dismissAnimated {
- // Tabs are only closed if the done button is pressed; this
- // allows you to leave a tab open by dragging down to dismiss
- [FLEXTabList.sharedList closeTab:self];
- [self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
- }
- - (void)addNavigationBarItemsToViewController:(UINavigationItem *)navigationItem {
- if (!self.presentingViewController) {
- return;
- }
-
- // Check if a done item already exists
- for (UIBarButtonItem *item in navigationItem.rightBarButtonItems) {
- if (item.style == UIBarButtonItemStyleDone) {
- return;
- }
- }
-
- // Give root view controllers a Done button if it does not already have one
- UIBarButtonItem *done = [[UIBarButtonItem alloc]
- initWithBarButtonSystemItem:UIBarButtonSystemItemDone
- target:self
- action:@selector(dismissAnimated)
- ];
-
- // Prepend the button if other buttons exist already
- NSArray *existingItems = navigationItem.rightBarButtonItems;
- if (existingItems.count) {
- navigationItem.rightBarButtonItems = [@[done] arrayByAddingObjectsFromArray:existingItems];
- } else {
- navigationItem.rightBarButtonItem = done;
- }
-
- // Keeps us from calling this method again on
- // the same view controllers in -viewWillAppear:
- self.didSetupPendingDismissButtons = YES;
- }
- - (void)addNavigationBarSwipeGesture {
- #if !TARGET_OS_TV
- UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizer alloc]
- initWithTarget:self action:@selector(handleNavigationBarSwipe:)
- ];
- swipe.direction = UISwipeGestureRecognizerDirectionDown;
- swipe.delegate = self;
- self.navigationBarSwipeGesture = swipe;
- [self.navigationBar addGestureRecognizer:swipe];
- #endif
- }
- - (void)handleNavigationBarSwipe:(UISwipeGestureRecognizer *)sender {
- if (sender.state == UIGestureRecognizerStateRecognized) {
- [self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
- }
- }
-
- - (void)handleNavigationBarTap:(UIGestureRecognizer *)sender {
- if (sender.state == UIGestureRecognizerStateRecognized) {
- #if !TARGET_OS_TV
- if (self.toolbarHidden) {
- [self setToolbarHidden:NO animated:YES];
- }
- #endif
- }
- }
- #if !TARGET_OS_TV
- - (BOOL)gestureRecognizer:(UIGestureRecognizer *)g1 shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)g2 {
- if (g1 == self.navigationBarSwipeGesture && g2 == self.barHideOnSwipeGestureRecognizer) {
- return YES;
- }
-
- return NO;
- }
- #endif
- - (void)_gestureRecognizedInteractiveHide:(UIPanGestureRecognizer *)sender {
- #if !TARGET_OS_TV
- if (sender.state == UIGestureRecognizerStateRecognized) {
- BOOL show = self.topViewController.toolbarItems.count;
- CGFloat yTranslation = [sender translationInView:self.view].y;
- CGFloat yVelocity = [sender velocityInView:self.view].y;
- if (yVelocity > 2000) {
- [self setToolbarHidden:YES animated:YES];
- } else if (show && yTranslation > 20 && yVelocity > 250) {
- [self setToolbarHidden:NO animated:YES];
- } else if (yTranslation < -20) {
- [self setToolbarHidden:YES animated:YES];
- }
- }
- #endif
- }
- @end
|