can't update navigationbar when popping to root view controller - ios

I have a UITabBar. In one tab is a UINavigationController. Let's say the 2nd or 3rd UIViewController in the stack has this:
class ChildVC: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.setNavigationBarHidden(true, animated: false)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
navigationController?.setNavigationBarHidden(false, animated: false)
}
}
If you click the current tab it will popToRootViewController() on the navigation controller. The problem is, in viewWillDisappear(:) of my current tab the navigationController is nil. So the navigationBar remains hidden.
What's the proper way to handle this? Should I just set the navigation bar to visible in the root view controller's viewDidAppear? That seems hacky.

If anybody else sees this, I don't know why the reference to self.navigationController gets set to nil before viewWillDisappear when you popToRootViewController() but a workaround I found was just to store your own reference to it.
class ChildVC: UIViewController {
private weak var navCtrl: UINavigationController?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navCtrl = navigationController
navCtrl?.setNavigationBarHidden(true, animated: false)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
navCtrl?.setNavigationBarHidden(false, animated: false)
}
}

You should override the viewWillAppear in the rootViewController and setNavigationBarHidden from there. navigationController is nil at viewDidDisappear because it has already been popped off the navigation stack.

Related

iOS: Hide the navigation bar for only one viewcontroller which is root of the UINavigationController?

I want to hide the navigationbar for only one viewcontroller which is the root viewcontroller of the UINavigationController.
Currently I am using below code to hide the navigation bar for a particular viewcontroller.
To hide the navigationbar,
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.isNavigationBarHidden = true
super.viewWillAppear(animated)
}
To show the navigationbar for other viewcontrollers,
override func viewWillDisappear(_ animated: Bool) {
self.navigationController?.isNavigationBarHidden = false
super.viewWillDisappear(animated)
}
When I am trying to use this code, the app is being crashed in iOS 13 devices because of threading violation: expected the main thread.
Please checkout the issue which I am getting when I use the above code to hide the navigationbar,
iOS 13: threading violation: expected the main thread
Please let me know if there is any other way to hide the navigationbar for only one viewcontroller.
I got the another way to hide/show navigationbar from one of my friend.
Set a delegate for the NavigationController:
navigationController.delegate = self
Hide/Show navigationbar for each ViewController all in one place
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
let hide = (viewController is YourVC)
navigationController.setNavigationBarHidden(hide, animated: animated)
}
import UIKit
class ViewController: UIViewController {
override func viewWillAppear(_ animated: Bool){
super.viewWillAppear(animated)
self.navigationController?.isNavigationBarHidden = true
}
override func viewWillDisappear(_ animated: Bool){
super.viewWillDisappear(animated)
self.navigationController?.isNavigationBarHidden = false
}
}
You can make it transparent (Completely invisible) when viewWillApper get called and back to normal when view willDisappear get called. Here are helper functions.
func makeNaBarTransparent() {
navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
navigationController?.navigationBar.shadowImage = UIImage()
navigationController?.navigationBar.isTranslucent = true
}
func restoreNavigationBarToDefault() {
navigationController?.navigationBar.setBackgroundImage(nil, for: .default)
navigationController?.navigationBar.shadowImage = nil
}
USAGE
import UIKit
class ViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
makeNaBarTransparent()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
restoreNavigationBarToDefault()
}
}

Navigation Bar on only one view in my whole app

I have several views in my app, and I only want a navigationbar on one of them.... I used a navigationcontroller and at first I was using this code (while my app was in its infancy and only had 2 views)
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.setNavigationBarHidden(true, animated: animated)
super.viewWillAppear(animated)
}
override func viewWillDisappear(_ animated: Bool) {
self.navigationController?.setNavigationBarHidden(false, animated: animated)
super.viewWillDisappear(animated)
}
It worked fine - however, the app has become more complex - I have these views
lazy var orderedViewControllers: [UIViewController] = {
return [self.newVc(viewController: "pageOne"),
self.newVc(viewController: "pageTwo"),
self.newVc(viewController: "pageThree"),
self.newVc(viewController: "pageFour"),
self.newVc(viewController: "activate")
]
}()
Where this code isn't applied to, even if I create a custom view controller for each view.
I thought the way to do this would be to put the top chunk of code in every view, but it's not working for the bottom chunk. In essence my question is how do I use NavigationController to create a bar ONLY on one view.
One option: use a "base view controller" class which handles hiding / showing the Navigation Bar, and make your "pages" sub-classes of the "base" class.
import UIKit
class BaseViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(true, animated: animated)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: animated)
}
}
class ViewController: UIViewController {
// has buttons with
// Show (e.g. push)
// segues to Settings, First, Second, Third view controllers
}
class SettingsViewController: UIViewController {
// Settings VC is a normal UIViewController, because
// we *want* the NavBar to remain visible
}
class FirstViewController: BaseViewController {
#IBAction func backTapped(_ sender: Any) {
self.navigationController?.popViewController(animated: true)
}
}
class SecondViewController: BaseViewController {
#IBAction func backTapped(_ sender: Any) {
self.navigationController?.popViewController(animated: true)
}
}
class ThirdViewController: BaseViewController {
#IBAction func backTapped(_ sender: Any) {
self.navigationController?.popViewController(animated: true)
}
}
You can use this method of UINavigationControllerDelegate
optional func navigationController(_ navigationController: UINavigationController,
willShow viewController: UIViewController,
animated: Bool){
if viewController == self."desired view controller" {
self.isNavigationBarHidden = true
}else{
self.isNavigationBarHidden = false
}
}
Thank you all for the support. I have resolved my issue by doing the following:
I put the only view controller I wanted to have a navigation bar in a navigation controller view the Embed menu.
I added a custom back button.

Present a modal view controller, but don't hide the navigation bar

I'm modally presenting a viewController with a semi-transparent view. It's a custom activity indicator. I would like it to cover the view, but leave the navigation bar and tab bar visible and accessible.
The docs, and several SO answers (e.g. Presenting a Modal View Controller hides the Navigation Bar) seem to suggest that presenting the modal onto the navigation controller should achieve this. But when I do it, it shows the tab bar correctly, but covers the navigation bar.
Any ideas? Here is the relevant code:
let spinnerVC = SpinnerViewController()
spinnerVC.modalPresentationStyle = .overCurrentContext
spinnerVC.modalTransitionStyle = .crossDissolve
self.navigationController?.present(spinnerVC, animated: true, completion: nil)
//self.navigationController is definitely not nil
You can present a your viewcontroller by adding as rootViewController of a navigationController and then present it over the current viewController like this:
let spinnerVC = SpinnerViewController()
let navVC = UINavigationController(rootViewController:spinnerVC)
navVC.modalPresentationStyle = .overCurrentContext
navVC.modalTransitionStyle = .crossDissolve
self.present(navVC, animated: true, completion: nil)
you could do it two ways:
first one:
Put this code in your parent view
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(true, animated: animated)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: animated)
}
second one:
Add a reference to your invoker in your alert controller in order to hide the bar like this:
weak var invokerView : UIViewController?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.invokerView?.navigationController?.setNavigationBarHidden(true, animated: animated)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.invokerView?.navigationController?.setNavigationBarHidden(false, animated: animated)
}
Don't present it. Add it as a child view controller to the top view controller of your navigation controller and add its view as a subView to the view of the same adjusting the frame.
let spinnerVC = SpinnerViewController()
spinnerVC.view.frame = self.navigationController?.topViewController?.view.bounds
self.navigationController?.topViewController?.addChildViewController(spinnerVC)
self.navigationController?.topViewController?.view.addSubview(spinnerVC.view)

Navigation bar is moving up to statusbar

I have a viewController. Which does not have navigationBar. I am pushing another viewController that has navigationBar. Which is going up
I am using following code to show the navigationBar
self.navigationController?.setNavigationBarHidden(false, animated: false)
I believe you trying to hide navigationBar in firstVC and show it in secondVC.
Try following method into your firstVC and make sure you embedded your firstVC with navigationController.
Your storyBoard flow layout should be look like below...
Implement below method in firstVC.
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.setNavigationBarHidden(true, animated: true)
}
override func viewWillDisappear(_ animated: Bool) {
self.navigationController?.setNavigationBarHidden(false, animated: true)
}
Output:Updated

How can I hide navigation bar of a specific View Controller?

What I am trying is to set a ViewController(root) with a NavigationController that will connect with three ViewController.
Two of the linked ViewController have to have a NavigationBar on the top of each screen. The other one do not have to have the Navigation bar. Further, the root View Controller do not have to have a Navigation bar.
I hide the NavigationBar on the root View Controller as follows:
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.setNavigationBarHidden(true, animated: animated)
super.viewWillAppear(animated)
}
override func viewWillDisappear(_ animated: Bool) {
self.navigationController?.setNavigationBarHidden(false, animated: animated)
super.viewWillDisappear(animated)
}
but I am not able to hide the Navigation bar on the linked View Controller that does not have to have the Navigation bar.
I have also tried on the viewDidLoad function of the View Controller in which I want to hide the Navigation bar using:
self.navigationController?.setNavigationBarHidden(false, animated: true)
but the Navigation bar is still being shown.
How can I hide the Navigation bar on a specific View Controller?
Thanks in advance!
You can Try like this:-
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.navigationController!.navigationBarHidden = true
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
self.navigationController!.navigationBarHidden = false
}
You are making mistake, in question you have set falsein viewDidLoad to hide navigationBar, you need to set true instead of false, also try on viewDidAppear.
self.navigationController?.setNavigationBarHidden(true, animated: true)
Use below code in viewDidAppear method
self.navigationController?.setNavigationBarHidden(true, animated: true)
Try this code in viewDidAppear :-
self.navigationController?.navigationBarHidden = true

Resources