As the UI of UINavigationController becomes more and more diverse, the navigationBar is no longer a single color, and there is also a need to hide the navigationBar, so the navigationBar of UINavigationController is not smooth enough to switch between different colors or hidden Applications have also begun to use similar switching effects such as netease News, netease Cloud Music and Taobao.

Introduce a,

There are roughly three ways to achieve the effect

  • Using customnavigationBarTaobao, netease news, Darling and other use this kind of

High flexibility, good effect. The downside is that the custom navigationBar is not as easy to use as the native one. If it is an existing project that needs to be converted, it can be a bit troublesome.

  • This can also be achieved by capturing the screen as you push to the next page and seeing the screenshot behind when you pop it with the edgePan. Jingdong, Tmall and so on use this

This is relatively simple to implement, but the drawback is that the screenshots are static and you don’t see real-time content when you swipe back. And this method only works for pop, not push, which is the gradient effect of a native push.

  • The use of a more special, more ingenious way to achieve, that is, netease cloud music implementation method, will be analyzed after this implementation

Set the advantages of the first two, easy integration, the effect is also good characteristics. And shall not affect the original navigation navigationItem and other navigationBar property, including setting barTintColor, backgroundImage, etc.

There is a problem: hiding the TabBar is done by pushing the controller’s own NavigationController, not the root controller’s NavigationController.

Now you can open it and see a general, when I see this, I know that netease Cloud music originally used a more clever method, in a nutshell, the specific process of this method is:

  • First, the root controller is a navigation controller, which manages all the push and pop operations on the page, and the navigation bar of the navigation controllernavigationBarIt’s hidden, and it’s usedsetNavigationBarHidden:The way to hide, notnavgationBar.hidden = YESBecause thesetNavigationBarHidden:In fact, the way is directly willnavigationBarTo be removed, andnavgationBar.hidden = YESJust let thenavigationBarIt becomes transparent.
  • The root of theUINavigationControlerThere’s another one down hereUIViewControllerBecause if we need to display the interface itself is also oneUINavigationControlerThat would be a problem.UINavigationControlernestedUINavigationControlerIs not allowed, similar to article 3 below. So it has to be packaged.
  • And then every time you push oneViewControlerWhen, give thisViewControllerCarried on the packaging, the first bread outside aUINavigationControllerAnd then inUINavigationControlerOutsourcing aUIViewControllerAnd the reason for that isUINavigationControlerYou can’t push another oneUINavigationControler, so you need to wrap another layerUIViewController.
  • Finally, the navigation bar that we are really seeing on the interface belongs to this packageUIViewControlerOutside of theUINavigationControlerBecause each page has its own navigation controller, and then the outermost oneUINavigationControlerTo manage the page’s push and pop operations, and that’s what we want.
                              UIWindow
                                 |
                        UINavigationController
                                 |
                          UIViewController
                                 |
                         UITabBarController
                                 |
                        UINavigationController
                                 |
                          UIViewController
                                 |
                        UINavigationController
                                 |
                          UIViewController
Copy the code

The outermost UINavigationControler is the fixed UINavigationControler for push and POP operations. The viewControllers of UITabBarController also hold the UINavigationControler The tionControler rootViewController needs to be wrapped once as uiViewController-UinavGationController. And that’s what we have today.

Second, the analysis

1. Global POP

UIPanGestureRecognizer to replace the original interactivePopGestureRecognizer gestures, In method didShowViewController UINavigationControllerDelegate agent self. The view to add gestures.

#import "JTNavigationController.h"

@interface JTNavigationController ()<UINavigationControllerDelegate>
@property (nonatomic, strong) UIPanGestureRecognizer *popPanGesture;
@property (nonatomic, strong) id popGestureDelegate;
@end

@implementation JTNavigationController
- (instancetype)initWithRootViewController:(UIViewController *)rootViewController {
    if (self = [super initWithRootViewController:rootViewController]) {
        self.viewControllers = @[rootViewController];
    }
    return self;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    self.delegate = self;
    self.popGestureDelegate = self.interactivePopGestureRecognizer.delegate;
    SEL action = NSSelectorFromString(@"handleNavigationTransition:");
    self.popPanGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self.popGestureDelegate action:action];
    self.popPanGesture.maximumNumberOfTouches = 1;
}

- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
    BOOL isRootVC = viewController == navigationController.viewControllers.firstObject;
    if (isRootVC) {
        [self.view removeGestureRecognizer:self.popPanGesture];
    } else {
        [self.view addGestureRecognizer:self.popPanGesture];
    }
}
@end
Copy the code

Push and POP operate JTNavigationController

- (instancetype)initWithRootViewController:(UIViewController *)rootViewController { if (self = [super init]) { // Specify rootViewController as JTNavigationController rootViewController.jt_navigationController = self; self.viewControllers = @[[JTWrapViewController wrapViewControllerWithViewController:rootViewController]]; } return self; }Copy the code

Bind attributes by VC classification

#import "UIViewController+JTNavigationExtension.h"

#import <objc/runtime.h>

@implementation UIViewController (JTNavigationExtension)

- (BOOL)jt_fullScreenPopGestureEnabled {
    return [objc_getAssociatedObject(self, _cmd) boolValue];
}

- (void)setJt_fullScreenPopGestureEnabled:(BOOL)fullScreenPopGestureEnabled {
    objc_setAssociatedObject(self, @selector(jt_fullScreenPopGestureEnabled), @(fullScreenPopGestureEnabled), OBJC_ASSOCIATION_RETAIN);
}

- (JTNavigationController *)jt_navigationController {
    return objc_getAssociatedObject(self, _cmd);
}

- (void)setJt_navigationController:(JTNavigationController *)navigationController {
    objc_setAssociatedObject(self, @selector(jt_navigationController), navigationController, OBJC_ASSOCIATION_ASSIGN);
}
@end
Copy the code