ViewWillAppear Not called in tabbar items view controller - ios

I have structure:
*- TabBarViewController (Root)
*-- NavigationViewController
*---- ChatViewController
*-- NavigationViewController
*---- MenuViewController
and while I'm switching tabbar items, viewWillAppear in (Chat, Menu) called only once, but in NavigationVC called every times i switch.
Is it possible to call automatically viewWillAppeare in Chat and Menu ViewControllers while switching items?
super.viewWillAppear is inside method.
my code looks like:
class TabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
let v1 = storyboard!.instantiateViewController(withIdentifier: "ChatViewController")
let v2 = storyboard!.instantiateViewController(withIdentifier: "MenuViewController")
viewControllers = [v1,v2]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
class ChatViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print(self,#function)
}
}
class MenuViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print(self,#function)
}
}
It works on clear new project but on old ( where im working, and i have loot of funcionality, doesn't work)
StoryboardId is linked to NavigationViewController in Storyboard

I found issue:
In extension UINavigationController i have method
open override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if !UserDefaults.standard.isUserPresented {
navigationBar.barTintColor = .rgbColor(red: 43, green: 43, blue: 43, alpha: 1)
} else {
navigationBar.barTintColor = .rgbColor(red: 100, green: 100, blue: 100, alpha: 1)
}
}
and this block viewWillAppear in child view controllers in NavigationBar

Your question is not clear about adding viewcontroller to tabbarcontroller and navigation controller . I have created everything in storyboard . View will appear in view controller are :
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print("Menu View will appear")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print("Chat View will appear")
}
Am able to get below output when i switch :
Menu View will appear
Chat View will appear
Menu View will appear
Chat View will appear

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.

How to hide the navigation bar shadow only in detail view?

There are many answers in SO that provide solutions for hiding the navigation bar shadow. Those work for me except for this particular case, which I'm describing here. Therefore, this question is not a duplicate.
To test this particular case, I created a new project using the master-detail app template. In the DetailViewController -> viewDidAppear, I coded the following:
self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
self.navigationController?.navigationBar.shadowImage = UIImage()
The above code works for a Single View App and on iPad Air 2 simulator. However, it doesn't work on the detailViewController of a master-detail app in iPhoneX simulator.
Alternatively, I also tried retrieving the subviews of the navigationBar in viewDidAppear and tried hiding the shadow (see code below). However, the subview count is zero. How could that be?
for parent in self.navigationController!.navigationBar.subviews {
for childView in parent.subviews {
if(childView is UIImageView) {
childView.removeFromSuperview()
}
}
}
Any help on this is much appreciated.
For Generic flow
You could use this setup. Say ViewController is the all other view controller class (where you want the shadow), and DetailViewController is the detail view controller class.
The thing i'm doing preserving the shadow image.
ViewController.swift
class ViewController: UIViewController {
var shadowImage: UIImage!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
shadowImage = self.navigationController?.navigationBar.shadowImage
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.navigationBar.shadowImage = shadowImage
}
}
And DetailViewController.swift
class DetailViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.navigationController?.navigationBar.shadowImage = UIImage()
}
}
Storyboard setup
Output
Note: Another neat approach would be storing the shadow within the DetailsViewController and setting it while the view is about to disappear
class DetailsViewController: UIViewController {
var shadowImage: UIImage!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
shadowImage = self.navigationController?.navigationBar.shadowImage
self.navigationController?.navigationBar.shadowImage = UIImage()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.navigationController?.navigationBar.shadowImage = shadowImage
}
}
This solution is more elegant and resulting in a clean management.
For MasterDetailFlow, Using SplitViewController
In your MasterViewControlelr.swift
override func viewWillAppear(_ animated: Bool) {
clearsSelectionOnViewWillAppear = splitViewController!.isCollapsed // placeholder code when you created the project
super.viewWillAppear(animated)
self.navigationController?.navigationBar.shadowImage = nil
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.navigationController?.navigationBar.shadowImage = UIImage()
}
In your DetailViewController.swift
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.navigationBar.shadowImage = UIImage()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.navigationController?.navigationBar.shadowImage = nil
}
Output(Master/Detail flow)

Swift Navigation Bar Color

Is it possible to set the navigation bar color for just a single View Controller in the navigation hierarchy? Let the default navigation bar color be red and just the last view controller in the line should have a blue one. I've used these two lines to color the navigation bar of said view controller:
navigationController?.navigationBar.barTintColor = .blue
navigationController?.navigationBar.tintColor = .white
But when going back (e.g. by pressing the back button) the navigation bar stays blue. Setting the color back to red using above code doesn't do anything.
The navigationBar is shared across all the view controllers that are in the same UINavigationController stack.
If you want to change it's look for a specific view controller, you'll have to set the new style when the view controller is shown and remove it when the view controller is dismissed. This could be done in the viewWillAppear/viewWillDisappear of your view controller for example.
I could get the navigation bar to change colors coming from a ViewControllerB to ViewControllerA perfectly fine with your code. I am not sure what your initial problem was. Here is my code which worked:
ViewController A:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.navigationBar.barTintColor = .red
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func buttonAction(_ sender: Any) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "Second")
//self.present(controller, animated: true, completion: nil)
self.navigationController?.pushViewController(controller, animated: true)
}
}
ViewController B:
import UIKit
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.navigationBar.barTintColor = .blue
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
It worked without an issue.

Presenting UINavigationController Swift

I am presenting a loading screen where I do not want the navigation bar to be shown. So I use
self.navigationController.navigationBar.hidden = true
Which does the trick and hides the navigation bar. But when I want to show the navigation bar, I want to animate it in.
I have tried using this code but the bar does not appear.
self.navigationController.setNavigationBarHidden(false, animated: true)
After running the above code the bar is still hidden, how can I show/animate the bar?
I think your method is correct already. You just need to know where to put the code. Try the following code.
Code for ViewController 1
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBarHidden = true;
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.navigationController?.navigationBarHidden = true;
}
#IBAction func buttonTapped(sender: UIButton) {
self.performSegueWithIdentifier("goToScreen2", sender: self)
}
}
Code for ViewController 2
import UIKit
class ViewController2: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: true)
}
}
Update Answer:
I am able to unhide the navigation bar using the following code.
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBarHidden = true;
}
#IBAction func buttonTapped(sender: UIButton) {
self.navigationController?.setNavigationBarHidden(false, animated: true)
}
Screen shot of the implementation:-

Resources