20

Context:

Let's say we have one NavigationController and 2 viewControllers. ViewControllerA has a blue navigationBar, while ViewControllerB has a green one.

I set them up like so :

override func viewWillAppear(_ animated: Bool) {
        self.navigationController?.navigationBar.barTintColor = UIColor.blue    // Green if ViewController B
    }

It works well when I got from A to B, but when I return, the navigationBar teint is updated after. Like if it was set up in the viewDidAppear.

Expected:

The navigation bar should have its navigation bar color updated without delay.

Solutions tried:

  • I've seen this SO post, and I tried the solution. It worked, but would make the management of the navigation bar a lot more complex and painful. (in a real app)

  • I've tried to change the navbar teint in the ViewWillDisappear method. Didn't work.

More:

This is caused by a change on the ios10 API. I've read the release notes, but it's still unclear what should be done in this case.

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 layoutSubviews, -[UIView updateConstraints], viewWillLayoutSubviews, viewDidLayoutSubviews, updateViewConstraints, or any other method that is called in response to layout may result in a layout loop.

In some cases you can break these layout loops by ensuring that you always use the same object instance when objects (such as UIImage or UIColor) are required. But in general you should avoid doing this.

Question:

What would be the best way to handle the navigation bar changes between different navigation controllers in iOS 10 ?

Community
  • 1
  • 1
tsnkff
  • 786
  • 1
  • 7
  • 20
  • have you found the solution. – Joe Oct 16 '16 at 05:35
  • @Joe I ended up using the solution linked in my post. Still, I believe it'll make management of the navbar more complicated – tsnkff Oct 16 '16 at 09:36
  • I have done a test project similar to your problem.In that project, I have a 3vc connected with no segue and have a different navigation bar and status bar colour. I don't see any delay on navBar colour while transition.let me know is this the answer you after... – Joe Oct 16 '16 at 11:13
  • @Joe, are you using a `navigationController` or not ? Because I made a test project (as described in the question), and I could reproduce the same thing. But using segues .. – tsnkff Oct 16 '16 at 16:43
  • 1
    Yes,To achieve this.you have to tweak the navigationBar settings and programmically put the status bar...I will try to upload the project in GitHub sometime today.... – Joe Oct 17 '16 at 00:06
  • @joe hv u posted any solution for this issue? – jayant rawat Oct 24 '16 at 12:36
  • @jayantrawat As mentioned in the question, I used this solution : http://stackoverflow.com/a/39518148/4013333 – tsnkff Oct 24 '16 at 12:38
  • @tsnkff i am using tab bar controller & there are no specific methods for push or pop to view controllers as given in solution.. – jayant rawat Oct 24 '16 at 12:41
  • sorry buddy. i forgot about all... can you update ur post with storyboard screenshot and we start all over again...thanks – Joe Oct 24 '16 at 12:50
  • r you using segue to connect your second VC...let me know r u passing any data between controller.. – Joe Oct 24 '16 at 13:16
  • i am trying to achieve using smile tweak. but, i am getting the same delay issue. i can update my answer.but, you won't probably understand..if you wish i can update my test project.you may be check my answer in this post http://stackoverflow.com/questions/40176803/swift-navigation-bar-background-color-white-does-not-work/40178657#40178657 if you like the post give me a up vote ..cheers – Joe Oct 24 '16 at 13:23
  • @ joe i am using [ [self.navigationController pushViewController:@"vc2"animated:YES];. while navigating to 2nd view controller i am not getting delay issue but while coming back from 2nd view controller i am getting delay issue... – jayant rawat Oct 25 '16 at 07:53
  • I found the solution for your problem.still you looking for the answer. let me know... – Joe Oct 26 '16 at 06:03
  • @joe i am still looking for solution...it will be helpful if u tell me actual solution.. – jayant rawat Oct 26 '16 at 08:37
  • Did you look at my answer... – Joe Oct 26 '16 at 10:17
  • i looked into it but it is not working for me..i am setting navigation bar color as [self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"PlainTabbarOS7@2x.png"] forBarMetrics:UIBarMetricsDefault]... i am not setting tint color as u hv mentioned.. – jayant rawat Oct 26 '16 at 10:37
  • You can Set your image inside your tincolor using pattern image....still my code fix ur problem.let me know.i will update my code – Joe Oct 26 '16 at 23:13
  • check my update – Joe Oct 27 '16 at 06:15
  • @joe it does not fix my problem – jayant rawat Oct 31 '16 at 09:28
  • What you mean.did you tried my answer.tell me what problem are you facing now – Joe Oct 31 '16 at 09:32
  • I am still confused this post originally posted by tsnkff.did you upvote my answer?.how do you want me to help you. – Joe Oct 31 '16 at 09:35
  • @Joe I told you I ended up using the solution linked. You provided me a thoroughly written answer, which I couldn't test so I upvoted it. If I had tested, I would have accepted the answer instead. – tsnkff Oct 31 '16 at 09:44
  • Sorry man I thought. I am talking to a another guy... – Joe Oct 31 '16 at 09:47

2 Answers2

14

Try this code:

Note: Code Tested in Swift 3.

In ViewController A:

    override var isViewLoaded: Bool {
    title = "View 1"
    navigationController?.navigationBar.barTintColor = .blue
    navigationController?.navigationBar.isTranslucent = true
    navigationController?.navigationBar.tintColor = UIColor.white
    navigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName:UIColor.white]
    UIApplication.shared.statusBarStyle = .lightContent
    self.navigationController?.navigationBar.barStyle = .black // In case statusbar Light content wont work.
    return true
    }

In ViewController B:

    override var isViewLoaded: Bool {
    title = "View 2"
    navigationController?.navigationBar.barTintColor = .green
    navigationController?.navigationBar.tintColor = .white
    navigationController?.navigationBar.isTranslucent = true
    navigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName:UIColor.white]
    return true
    }

Output: No delay what so ever...

enter image description here

Pattern Image as a TintColor.

In ViewController A:

 navigationController?.navigationBar.barTintColor = UIColor(patternImage: UIImage(named: "viewOneImage.jpg")!)

In ViewController B:

 navigationController?.navigationBar.barTintColor = UIColor(patternImage: UIImage(named: "viewTwoImage.jpg")!)

Output:

enter image description here

Joe
  • 8,868
  • 8
  • 37
  • 59
0

I wasn't happy with the solution overriding isViewLoaded because if there's ever a time view_controller_a is shown and you use the value of view_controller_b.isViewLoaded for whatever reason, then you'll end up changing the nav bar to the wrong display.

Instead, I would store the nav bar configuration you want in each view controller and use UINavigationControllerDelegate:

class NavBarConfig
{
    var background_color: UIColor

    init( background_color: UIColor )
    {
        self.background_color = background_color
    }
}

class ViewController: UIViewController
{
    var nav_bar_config: NavBarConfig?
}

class NavControllerDelegate: UINavigationControllerDelegate
{
    //Called just before the navigation controller displays a view controller’s view and navigation item properties.
    func navigationController( _ nav_controller: UINavigationController, willShow view_controller: UIViewController, animated: Bool )
    {
        if let view_controller = view_controller as? ViewController, let nar_bar_config = view_controller.nav_bar_config
        {
            nav_controller.navigationBar.barTintColor = view_controller.nar_bar_config.background_color
        }
    }
}

After setting the delegate on your nav controller, your_nav_controller.delegate = NavControllerDelegate(), the delegate method will be called just before each view controller is displayed allowing you to configure the nav bar how you wish.

Kacy
  • 3,330
  • 4
  • 29
  • 57