Three schemes for transparent and translucent NavigationBar switching

App will often need to switch between transparent and opaque NavigationBar pages. Sometimes, in transparent NavigationBar pages, you even need to dynamically adjust NavigationBar Transparency Based on scrollView’s contentOffset. There are a lot of App can’t solve the problem here (in fact, more is not care). Here’s the reverse of BAT textbooks, each cite an example: QQ, Baidu cloud, rookie wrapped. Here’s a brief analysis of their problems.

Commonly used App analysis

Because the navigation bars push and pop are very short animations, these problems are reproduced by side slip gestures. And the page doesn’t necessarily get pop when it comes back. Because the code that implements the navigation bar is mostly hidden in four ways: viewWillAppear, viewDidAppear, viewWillDisappear, and viewDidDisappear. Slipping through the glide gesture for a distance and then returning back makes the four methods called, so that the BUG becomes noticeable. If not all is carried out by the test demonstrates the sideslip gesture.

The QQ problem appears in the “dynamic Tab” process by clicking on the “buddy dynamics” page. Maybe you’ll think there’s nothing wrong with it. Indeed, this solution has at least passed all the solutions. But his problem is to drop the animation of the navigationItem in the system navigation bar, which causes the last page to coincide with the title and navigationItem of the next page. The animation of the system NavigationBar has been shifted, tapered, trimmed to avoid these problems. QQ is a lazy practice.

Baidu cloud problem will be much more serious. His problem arises when more “Tab” jumps to any one of the lower level pages. When you click on the jump, the navigation bar appears suddenly. At the end of the pop, the navigation bar suddenly disappears and looks very obtrusive. Fortunately, the lower level page and the Header of the same page are similar colors. It is difficult to notice this problem without returning by gesture.

If QQ’s program is 60 points, Baidu’s cloud approach is 40 points. That rookie wrapped up only 0 points. Whether on push or pop, or viewWillAppear, viewDidAppear, viewWillDisappear, viewDidDisappear, navigation bar all suddenly appeared suddenly disappear, and the color of the page before and after the huge difference. More exaggeration is that each of his Tab is the design of this transparent navigation bar, so this BUG runs through the entire App level page jump. (it’s fixed now, but there are still a lot of problems.)

There are so many negative examples. Talk about some positive examples. Tmall, Taobao, Alipay, Baidu Post Bar, today’s headlines, where these App have dealt with the problem. They all use system – provided solutions, just a few lines of code. The system program abandoned the animation of the system navigation bar, but it was perfect.

The last best example is WeChat. WeChat’s handling of all the details on the navigation bar is the best solution, which is rare in all App. He kept the navigation glass and animation on the navigation bar and solved the BUG on the system navigation bar. Specific reference can be WeChat chat page and red pages between the jump.

NavigationBar of system BUG

After analyzing the other App, we should talk about the three schemes of this article. But before that, point out the BUG of a system navigation bar. When pushViewController is set to hideBottomBarWhenPushed and NavigationBar at this time is a translucent effect, then the NavigationBar will be transparent to the next layer phenomenon in the process hiding and not hide tabBar two pages push and pop in. Specific phenomena are described here and here.

This BUG appears not only in hideBottomBarWhenPushed, but also in fake NavigationBar and many other scenes. But this is in the case of navigation glass with frosted glass. It was perfectly reproduced in the personal center of the Baidu post bar. The same is perfectly solved in WeChat.

Three solutions

The three scenarios provided in this article are shown below. You can download the code here.

Three schemes for transparent and translucent NavigationBar switching

Having said so much, you must be very mysterious. Don’t be on top of the gab is confused, the actual implementation is very simple. The following one by one, the next line of thinking.

Demo file name and the corresponding relation between
Hidden
Opaque: A: the corresponding scheme of two
Translucent: three
Native: corresponding to the corresponding program system does not hide the NavigationBar effect

1. system program

The system itself has designed a special API for this scenario. You just need to call viewWillAppear and viewWillDisappear on the interface that needs to hide the navigation bar, and the following API is fine.

- (void) viewWillAppear: (BOOL) animated setNavigationBarHidden:YES {[self.navigationController animated:YES];} - (void) viewWillDisappear: (BOOL) animated [self.navigationController setNavigationBarHidden:NO {animated:YES]};

The navigation bars suddenly appear and disappear because they are not called in these two methods respectively

[self.navigationController setNavigationBarHidden:YES animated:YES];

But

Self.navigationController.navigationBar.hidden = YES;

This can be achieved as Tmall, Taobao, Alipay, today’s headlines and other App effects listed above. Even those that didn’t work out, like QQ, might have been looking for the effect of the gradient in the navigation column, but others were only because the product itself (or the programmer itself) didn’t pursue detail.

One disadvantage of this effect is that the navigation bar is completely hidden. This causes animation in the navigation bar to be lost, side slip gestures fail, and if you want to add navigationItem, you can only manually put Button on the corresponding position.

This scheme in Demo also implements the gradient of navigation bar, in fact, it adds a fake NavigationBar to hide NavigationBar interface. But this will cause the NavigationBar of the system BUG to appear again. To solve this problem, you can only give up the effect of frosted glass, use fake view instead of fake NavigationBar, or give up iOS7’s effect of realizing the system’s frosted glass on view.

In addition, if you want to make the side slip, the gesture will take effect. You can add Gesture by yourself. Or, you can use the following code for
in viewController:

Self.interactivePopGestureRecognizer.delegate = self;

The use of this line of code also brings a problem that is not easily reproduced, that is, when the interface repeatedly push and pop, the gesture will be confused, resulting in the interface can not push, then sideslip becomes push. This problem is solved in Demo by LPHiddenNavigationController, presumably by adjusting the enabled of the gesture at the appropriate time, but this is no longer detailed.

Self.interactivePopGestureRecognizer.enabled = NO;

2. global replacement

The principle of the scheme is equally simple. First, you can hide the background and segmentation lines of the original NavigationBar by following the following code. This code is written in baseNavigationController, and you can also use the appearance to set the global settings.

[self.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault]; [self.navigationBar setShadowImage:[UIImage new]];

Next, add a fake NavigationBar to each controller as a background through baseViewController. This will also cause the NavigationBar of the system BUG to appear. You can still avoid this problem by giving up the effect of frosted glass, replacing fake NavigationBar with fake View, or abandoning iOS7’s effect of implementing the system’s frosted glass on the view. And using NavigationBar as a background is cumbersome, view is much more lightweight.

This scheme retains the original NavigationBar animation system, add the fake NavigationBar as a backdrop, NavigationItem also implemented by the original NavigationBar, is already very perfect solution. But the downside is that all viewController needs to be inherited from baseViewController, and if the project itself is not baseViewController, it will be troublesome.

In this Demo, the gradient effect is also done in the navigation bar. Since each page has a fake NavigationBar as the background, it is very simple to implement, and only need to adjust the transparency of the current page NavigationBar. This scheme is also very convenient for implementing color changes in NavigationBar.

3. WeChat program

This kind of plan is known in the knowledge. The engineer of the meeting found the implementation through decompile WeChat. This is, in fact, an improvement to programme two”. Scheme two requires global NavigationBar background to hide, because it involves two viewController push and pop NavigationBar are different in different colors or transparency, and in the process of push and pop in two different NavigationBar to be displayed at the same time, it is impossible to cause our unified set of NavigationBar style. So whatever the solution, we can only do it by adding fake NavigationBar.

But the scheme is more clever. Hide the background of the system NavigationBar while the push or pop animation is about to be executed. Add a fake NavigationBar to the viewController involved in the opacity effect. When the animation is finished, remove the fake NavigationBar and choose whether to reset the background of the system NavigationBar, depending on whether the current page needs to display NavigationBar. These functions are implemented in four ways: viewWillAppear, viewDidAppear, viewWillDisappear, and viewDidDisappear. The specific logic of the code is a little complicated, and the code is understood directly:

- (void) viewWillAppear: (BOOL) animated {[super viewWillAppear:animated]; self.navigationController.navigationBar.userInteractionEnabled = NO; [self removeFakeNavBar]; if (((LPTranslucentNavigationController) self.navigationController).ShouldAddFakeNavigationBar) setBackgroundImage:[UIImage new] {[self.navigationController.navigationBar forBarMetrics:UIBarMetricsDefault]; [self addFakeNavBar];}} - (void) viewDidAppear: (BOOL) animated {[super viewDidAppear:animated]; self.navigationController.navigationBar.userInteractionEnabled = YES; if ([self useTransparentNavigationBar]! {self.navigationController.navigationBar.barStyle = UINavigationBar.appearance.barStyle; self.navig) AtionController.navigationBar.translucent = YES; [self.navigationController.navigationBar setBackgroundImage:[UINavigationBar.appearance backgroundImageForBarMetrics:UIBarMetricsDefault] forBarMetrics:UIBarMetricsDefault] [self removeFakeNavBar];};} - (void) viewWillDisappear: (BOOL) animated viewWillDisappear:animated] [self {[super; removeFakeNavBar]; if (((LPTranslucentNavigationController) self.navigationController).ShouldAddFakeNavigationBar) {[self}} - addFakeNavBar]; (void) viewDidDisappear: (BOOL) animated [self removeFakeNavBar] {[super viewDidDisappear:animated];};

ShouldAddFakeNavigationBar is the method implemented in LPTranslucentNavigationController. Its value changes depending on whether the last two navigationController in the controller require transparency. If there is at least one that requires a transparent effect, just return YES, otherwise return NO.

In addition, we need to hide NavigationBar fragmentation lines (not done in Demo). You might feel that adding a line of code like the program 21 is no good.

[self.navigationController.navigationBar setShadowImage:[UIImage new]];

But in fact, this code will trigger the NavigationBar of the system BUG. So, just by removing this stubborn line all the time, the following code is implemented in Demo UIViewController+HideBottomLine.

- (void) hideBottomLineInView: (UIView * view) {UIImageView *navBarLineImageView [self = findLineImageViewUnder:view]; navBarLineImageView.hidden = YES;} - (void) showBottomLineInView: (UIView * view) {UIImageView *navBarLineImageView = [self findLineImageViewUnder:view]; navBarLineImageView.hidden = NO;} - (UIImageView *) findLineImageViewUnder: (UIView * view) {if ([view isKindOfClass:UIImageView.class] & & view.bounds.size.height; < = 1) {return (UIImageView * view);} for (UIView *subview in view.subviews *imageView [self) {UIImageView = findLineImageViewUnder:subview]; if (imageView) {return imageView}}; return nil;}

WeChat’s red pages actually aren’t hiding NavigationBar, but changing NavigationBar to red. But the train of thought is the same, just adjust the transparency into a color adjustment.

If there are many such NavigationBar different pages in the entire App, you can write them in baseViewController and baseNavigationController in reference to Demo. If you just use a single page, you just have to implement it in a single page.

But it is not without drawbacks. One of his little problems is… Because of frequent switching, true and false NavigationBar, if by side slip gestures, frequent, “return – cancel – Return – cancel”, NavigationBar may occasionally flicker. Of course, this is only a limit test, which is hard to come by in normal use. And this only occurs when an opaque page returns to a transparent page. WeChat’s red page adjusts colors rather than transparency, so that doesn’t happen.

Another problem is that if you want to implement the NavigationBar gradient on this basis, you need to add a fake NavigationBar again after the transparent NavigationBar page appears. This makes the whole process seem very tedious, and also bring the system NavigationBar BUG, not as good as direct use scheme two.

summary

In the scheme mentioned above, if there is no need for frosted glass effect, it is a perfect plan. If you need frosted glass and do not want to achieve their own, only the third program is more reasonable, but this program is only able to meet the transparent to opaque effect, want to achieve gradual effect, or will cause BUG.

If you want the easiest way to implement, or should you choose scenario two?. This solution is also a practical solution in many of its projects, because in fact, many projects do not require NavigationBar’s frosted glass effect.

If you do not bother to manually add, return, gesture, trouble, or simply do not need to slip back gestures, such as the personal center of the tabBar level page, etc., the use of program one is the easiest choice.

The ideas of using baseViewController and baseNavigationController can be implemented by Category + Method Swizzling. This invasive scheme will allow you to remove the trouble of inheritance and can be considered. For example, the Korean developer did that.

Blog: xuyafei.cn,
, Jane book: jianshu.com/users/2555924d8c6e,
, micro-blog: weibo.com/xuyafei86,
, Github:github.com/xiaofei86

Reference material

  • LeftBarButtonItem iOS7 can not achieve the perfect solution of sliding return on [CSDN]
  • The [Blog] UINavigationController returns the gesture invalidation problem
  • [fillet] perfect solution to the problem of interactivePopGestureRecognizer jam
  • [Github] shuoshi/HHNavigationController-Demo