Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Navigationbar coloring in ViewWillAppear happens too late in iOS 10

I am facing a weird bug, that happens only on iOS 10.

I have a application with several screens, and each screen colors the navigationBar in viewWillAppear. So when you go to the next screen, it will be properly colored.

However, when testing on iOS 10 I suddenly see the following behaviour when going back to a previous screen: When the previous screen appears the navigationBar still has the color of the previous screen and then flashes to the proper color. It almost looks like viewWillAppear somehow behaves as viewDidAppear.

Relevant code:

ViewController:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [ViewControllerPainter paint:self withBackground:[UIColor whiteColor] andForeground:[UIColor blackColor] andIsLight:true];

}

Painter:

+ (void)paint:(UIViewController *)controller withBackground:(UIColor *)backgroundColor andForeground:(UIColor *)foregroundColor andIsLight:(bool)isLight
{
    controller.navigationController.navigationBar.opaque = true;
    controller.navigationController.navigationBar.translucent = false;
    controller.navigationController.navigationBar.tintColor = foregroundColor;
    controller.navigationController.navigationBar.barTintColor = backgroundColor;
    controller.navigationController.navigationBar.backgroundColor = backgroundColor;
    controller.navigationController.navigationBar.barStyle = isLight ? UIBarStyleDefault : UIBarStyleBlack;
    controller.navigationController.navigationBar.titleTextAttributes = @{NSForegroundColorAttributeName: foregroundColor};
}

Is this a bug? Is there something I can do about to fix this? It's very frustrating.

like image 339
Peterdk Avatar asked Sep 15 '16 12:09

Peterdk


Video Answer


3 Answers

Here's what changed according to the iOS 10 SDK Release Notes:

In iOS 10, UIKit has updated and unified background management for UINavigationBar, UITabBar, and UIToolbar. In particular, changes to background properties of these views (such as background or shadow images, or setting the bar style) may kick off a layout pass for the bar to resolve the new background appearance.
In particular, this means that attempts to change the background appearance of these bars inside of -[UIView layoutSubviews], -[UIView updateConstraints], -[UIViewController willLayoutSubviews], -[UIViewController didLayoutSubviews], - [UIViewController updateViewConstraints], or any other method that is called in response to layout may result in a layout loop.

So the problem seems to be that viewWillAppear is triggering the mentioned layout loop, since it's called as a result of a layout change: viewWillAppear stack trace

The quick fix for me was overriding popViewControllerAnimated and pushViewController and updating the navigationBar background on my subclass of UINavigationController. Here's how it looks like:

override func popViewControllerAnimated(animated: Bool) -> UIViewController? {
    let poppedViewController = super.popViewControllerAnimated(animated)

    // Updates the navigation bar appearance
    updateAppearanceForViewController(nextViewController)

    return poppedViewController
}

override func pushViewController(viewController: UIViewController, animated: Bool) {
    super.pushViewController(viewController, animated: animated)

    // Updates the navigation bar appearance
    updateAppearanceForViewController(viewController)
}

My guess is that it works because popViewControllerAnimated and pushViewController are not called by the OS as a result of a layout change, but by a touch event. So keep that in mind if you want to find another place to update your navigationBar background.

like image 177
3 revs, 3 users 57% Avatar answered Oct 02 '22 07:10

3 revs, 3 users 57%


I had to fix this with:

self.navigationController.navigationBarHidden = YES;
self.navigationController.navigationBarHidden = NO;

This way you don't have to override popviewcontroller or pushviewcontroller. It's basically triggering the navigationbar to redraw.

It's still annoying how they can just push out a new version of OS that breaks something this significant.

like image 29
Vlad Avatar answered Oct 02 '22 07:10

Vlad


Try using willMoveToParentViewController, gives the same effect as overriding UINavigationController methods but without the hassle.

like image 20
Usman Raza Avatar answered Oct 02 '22 05:10

Usman Raza