In my app I am using a NavigationController to display a SlideView by using following code
func addChildSidePanelController(sidePanelController: SlideViewController) {
sidePanelController.delegate = centerViewController
let navigationController: UINavigationController = UINavigationController(rootViewController: sidePanelController)
view.insertSubview(navigationController.view, atIndex: 0)
addChildViewController(navigationController)
navigationController.didMoveToParentViewController(self)
}
So the centerViewController is my mainViewController. After the usage of this side panel, I need to remove it from my MainView. Currently I am using code
self.leftViewController?.view.removeFromSuperview()
self.leftViewController = nil;
The view inside the NavigationController is removed by this code, but the navigation bar and other container layers are still there.
How to remove whole navigation controller?
Related
I have 5 VC's, I'm successfully removing ViewController from navigation stack. But the problem is when click back button on navigation, it's moving into previous VC and it's showing removed VC on navigation bar.
Ex: I have 5 VC's: VC1, VC2, VC3, VC4, VC5.
Now I'm navigating from VC1 -> VC2, ..... VC4 -> VC5. And I have custom navigation bar back button title. Here I'm removing VC4 from stack.
When click back button in VC5 it's directly moving into VC3. But navigation bar is VC4. When click navigation bar once again now it's displaying VC3 navigation bar in same VC.
HOW TO resolve this issue. I want to display directly VC3 and vc3 navigation bar in single click.
Code to remove VC from Navigation stack:
guard let navigationController = self.navigationController else { return }
var navigationArray = navigationController.viewControllers // To get all UIViewController stack as Array
navigationArray.remove(at: navigationArray.count - 2) // To remove previous UIViewController
self.navigationController?.viewControllers = navigationArray
Use the following:
navigationController?.setViewControllers(navigationArray!, animated: true)
E.g.
guard let navigationController = self.navigationController else { return }
var navigationArray = navigationController.viewControllers
navigationArray.remove(at: navigationArray.count - 2)
navigationController.setViewControllers(navigationArray!, animated: true)
From the docs:
Use this method to update or replace the current view controller stack
without pushing or popping each controller explicitly. In addition,
this method lets you update the set of controllers without animating
the changes, which might be appropriate at launch time when you want
to return the navigation controller to a previous state.
If animations are enabled, this method decides which type of
transition to perform based on whether the last item in the items
array is already in the navigation stack. If the view controller is
currently in the stack, but is not the topmost item, this method uses
a pop transition; if it is the topmost item, no transition is
performed. If the view controller is not on the stack, this method
uses a push transition. Only one transition is performed, but when
that transition finishes, the entire contents of the stack are
replaced with the new view controllers. For example, if controllers A,
B, and C are on the stack and you set controllers D, A, and B, this
method uses a pop transition and the resulting stack contains the
controllers D, A, and B.
Edit 1
When you are pushing VC5, use the following code
let vc = YourVC5()
var array = navigationController?.viewControllers
array?.removeLast()
array?.append(vc)
navigationController?.setViewControllers(array!, animated: true)
The idea is when you push VC5 into stack, before pushing we are excluding VC4 from the list thus it will have VC3 beneath VC5 by default and you just need to call the navigationController?.popViewController(animated: true) and it should pop directly to VC3
Hide default back button and add custom back button with action:
override func viewDidLoad {
super.viewDidLoad()
self.navigationItem.hidesBackButton = true
let customBackButton = UIBarButtonItem(title: "Back", style: UIBarButtonItem.Style.plain, target: self, action: #selector(back))
self.navigationItem.leftBarButtonItem = customBackButton
}
Use popToViewController to move back to specific viewcontroller:
#objc func back(sender: UIBarButtonItem) {
guard let navigationController = self.navigationController else { return }
var navigationArray = navigationController.viewControllers // To get all
self.navigationController!.popToViewController(navigationArray[navigationArray.count - 2], animated: true)
}
if you are using custom NavigationBar than you need to use custom back button click Action in VC5 :-
#IBAction func btnBackAction(_ sender: UIButton) {
let vc = VC3()
self.navigationController.popToViewController(vc, animated: true)
}
And if you can use Default NavigationBar than need to remove VC4 in navigation stack in VC5 like this:-
guard let navigationController = self.navigationController else { return }
var navigationArray = navigationController.viewControllers // To get all UIViewController stack as Array
navigationArray.remove(at: navigationArray.count - 2) // To remove previous UIViewController
self.navigationController?.viewControllers = navigationArray
You can use popToViewController(_:animated:) (as Prakash Shaiva answered above):
guard let navigationController = self.navigationController else { return }
var navigationArray = navigationController.viewControllers // To get all
self.navigationController.popToViewController(navigationArray[navigationArray.count - 2], animated: true)
And try to update your NavigationBar in the method viewWillAppear(_:) for VC3.
I'm trying to create UITabBarController programmatically, adding multiple NavigationControllers to it. When UITabBarController contains one NavigationController - everything works as expected (see image)
But when i add multiple NavigationControllers to UITabBarController each screen becomes black (see another image )
The same black screen is shown when switching between tabs 1, 2, 3, 4 and 5.
Here's the code how UITabBarController is created
class TabBarViewController : UITabBarController{
override func viewDidLoad() {
super.viewDidLoad()
let controllers = [HistoryViewController.self, StatsViewController.self, DashboardViewController.self, ExpenseManagerViewController.self, ProfileViewController.self]
var navControllers: [UINavigationController] = []
controllers.forEach{ ctrl in
navControllers.append(getController(from: ctrl))
}
tabBar.tintColor = Color.green
viewControllers = navControllers
}
private func getController<TType: UIViewController>(from type: TType.Type) -> UINavigationController{
let ctrl = TType()
let navCtrl = UINavigationController(rootViewController: ctrl)
let ctrlName = String.init(describing: type.self).replacingOccurrences(of: "ViewController", with: String.empty)
navCtrl.tabBarItem.title = ctrlName
navCtrl.tabBarItem.image = UIImage(named: ctrlName)
navCtrl.navigationBar.topItem?.title = ctrlName
return navCtrl
}
}
Those UIViewControllers are created using "add Cocoa Touch Class" option and have assigned *.xib files with some minimum design (see one more image)
Any help regarding why all screens become black when multiple (2 and more) NavigationControllers added to TabBarController would be highly appreciated.
Thanks
Clearly you forget how to init the UIViewControllers with xib file:
private func getController<TType: UIViewController>(from type: TType.Type) -> UINavigationController{
let ctrl = TType(nibName: String.init(describing: type.self), bundle: nil)
let navCtrl = UINavigationController(rootViewController: ctrl)
First if you come from any screen then dont insert navigationbar in between that viewcontroller and tabbarcontroller and when you jump to tabbarcontroller set as rootview controller and any tab you want to open than place navigation controller in between. means dont open tabbar controller with navigationbar heirarchy but when u want to open controller with tabs then place navigation controller in between.
I came across a library call Motion on GitHub which provide sets of transition on view and controllers.
Based on the website, to use the animation on navigation controller we need to create a new navigation controller class like below, and I apply the new navigation controller. But when it failed and not pushing to the new view controller.
the GitHub link is https://github.com/CosmicMind/Motion
class AppNavigationController: UINavigationController {
open override func viewDidLoad() {
super.viewDidLoad()
isMotionEnabled = true
motionNavigationTransitionType = .zoom
}
}
then on the main view:
let navigation = AppNavigationController()
navigation.pushViewController(newViewController, animated: false)
If you're using Motion's animated NavigationController throughout your app, you can set it this way in your AppDelegate's didFinishLaunchingWithOptions method.
let newViewController = NewViewController(nibName: "NewViewController",bundle: nil)
let navigation = AppNavigationController(rootViewController: newViewController!)
self.window?.rootViewController = navigation
self.window?.makeKeyAndVisible()
UPDATE:
If you want to create a separate NavigationController and push specific UIViewController to it, then you need to present the navigation controller instead of pushing it. See below.
let motionNavController = AppNavigationController(rootViewController: galleryViewController)
motionNavController.motionNavigationTransitionType = .none
motionNavController.isNavigationBarHidden = true
self.present(motionNavController, animated: true, completion: nil)
I created a custom search bar and embedding it in the navigation bar, it appears but after I push another view controller, the search bar does not get replaced with the title of the pushed view controller. The search bar stays persistent throughout all views, instead of getting replaced with a title. Perfect example is Instagram search tab, you search for a person and click on the cell, their profile is pushed and the search bar is replaced with the custom title, back button, etc.
First VC
self.customSearchBar.tag = 4
self.navigationController?.view.addSubview(customSearchBar)
Second VC
if let nav: UINavigationController = self.navigationController {
if let searchBar = nav.view.viewWithTag(4) {
searchBar.removeFromSuperview()
}
}
You shouldn't place the searchbar inside the navigationcontroller view as this view is the same instance on all pushed viewcontrollers.
Add the searchbar to the the depending view controllers ui.
To add a searchbar on navigationBar, this is the way.
self.navigationController?.navigationBar.addSubview(customSearchBar)
To remove it when you push it to other viewController. Write the following code in the secondVC that is pushed inside it's viewDidLoad() function. Also, set the tag of customSearchBar to any number (TAG)
if let nav: UINavigationController = self.navigationController {
let bar: UINavigationBar = nav.navigationBar
if let searchBar = bar.viewWithTag(TAG) {
searchBar.removeFromSuperview()
}
}
In the question, the customSearchBar is added to self.navigationController.view. To remove it, you can do the following:
if let nav: UINavigationController = self.navigationController {
if let searchBar = nav.view.viewWithTag(TAG) {
searchBar.removeFromSuperview()
}
}
Edit:
Adding and removing a UIViewController's view as a subview of other UIViewController
// for adding
let viewController: ViewController = ViewController()
self.addChildViewController(viewController)
self.view.addSubview(viewController.view)
viewController.view.bounds = self.view.bounds // better to use autolayout here
viewController.didMove(toParentViewController: self)
// for removing
if let vc = self.childViewControllers.last {
vc.willMove(toParentViewController: nil)
vc.view.removeFromSuperview()
vc.removeFromParentViewController()
}
So i have an app were you're supposed to chose a location from a map but the way i'm doing it is by a popup off a view controller that has a MapKit and a search bar and a button for choosing users location, the problem lies on when i'm done with the View I normally called the
self.view.removeFromSuperview()
but the thing that is being remove is the View controller itself but il leaves behind the navigation bar and it doesn't let me do nothing else (in fact when I go to another tab and return to the same one the VC returns).
the way i'm instantiating the VC is like this:
func location ()
{
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc = mainStoryboard.instantiateViewController(withIdentifier: "mapV") as! UBICACIONViewController
let nav1 = UINavigationController()
nav1.viewControllers = [vc]
self.addChildViewController(nav1)
self.view.addSubview(nav1.view)
vc.delegate = self //this for the info that i'm getting afte(works fine)
nav1.didMove(toParentViewController: self)
}
So until here works fin but when the user pick te button that says "Use My Location" and tries to return there's the problem.
in my VC view controller this is what happens when finished:
func Remove()
{
if let del = delegate {
del.dataChanged(str: Event.sharedInstance.Location!)
}
self.view.removeFromSuperview() //Problem Here :/
}
Second VC
Return to First VC
Actually you have added a child view controller nav1. And nav1 has a subview nav1.view.
while removing you are removing this view from its superview, not the child view controller added.
Any ways, From you screen short..I understand that the pop up view is off full screen then why dont you present this VC modally, or else push it to navigation stack.