I'm trying to Use UINavigationController inside a UITabbarController.
this is my Controllers Structure
-UITabbarController(InitialView)
-tabItemOne-DashboardController
-SomeButtons with StoryboardSegue-To-DifferentViewController
-tabItemTwo-OtherController
-tabItemThree-OtherController
Now I want to show the back button when some StoryboardSegue is performed in DashboardViewController.
Let's say I open the app and can see 4 tabbarItems on UITabbarController, in the first tabbar item I have DashboardViewController, in this DashVC I've 3 4 different buttons to show other viewcontrollers. So far so good, everything is working. but once the child viewcontroller from DashVC is on screen, I want to show a back button on the top as UINavigationController do.
I've tried to put the UINavigationController before UITabbarController but its not showing. i've tried to do embed it in DashboardVC but again its not showing.
Do i need to embed seperate UINavigationController with each of childViewControllers of DashboardVC?
Any help is appreciated.
So what you need is this
-UITabbarController(InitialView)
-NavigationController -tabItemOne-DashboardController
-SomeButtons with StoryboardSegue-To-DifferentViewController
-tabItemTwo-OtherController
-tabItemThree-OtherController
and in DashBoardController you need to add this code in
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.navigationBar.isHidden = true
}
override func viewWillDisappear(_ animated: Bool) {
self.navigationController?.navigationBar.isHidden = false
}
this code is to hide the navigation bar in Dashboard and restoring when pushing another view controller so you back button is not hide.
also if you need the same functionality in the others view controller you should embebed in navigation controllers.
is not exactly your case but all navigation Controller are Childs of the UITabBarController that is the initial viewController.
Related
I'd like to present a navigation controller from a tab bar controller's tab's root view controller and have the tabs from the tab bar controller visible.
Here's a photo of an example storyboard:
The tab bar controller has a relationship to the ItemOneViewController, which is implemented like this:
import UIKit
class ItemOneViewController: UIViewController {
private func presentNavigationController() {
let id = "NavController"
guard let vc = storyboard?.instantiateViewController(withIdentifier: id) else { return }
present(vc, animated: true)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
presentNavigationController()
}
}
I hoped that by presenting the navigation controller from the root view controller of a tab on a tab bar controller that the navigation controller would be presented behind the tabs, but when I run the described app, this is what the simulator looks like:
This is what the view hierarchy looks like:
I think I remember a previous colleague mentioning that a UINavigationController will replace the view stack, which makes sense but is there anyway to keep it or bring the UITabBarController with it?
I've tried adjusting the presentation style and context but haven't had any luck.
Try embedding your ItemOneViewController in Navigation Controller instead of embedding you ViewController VC in Navigation Controller.
Select ItemOneViewController from storyboard.
Go to the Editor and Embed with Navigation Controller
Your view hierarchy at Storyboard looks Like this :
I currently have parental "menu" TableView with UINavigationBar and from each cell there is a segues by reference outlet to 3 similar Views with different information.
In each View there is a buttons to other 2 Views.
With every button's segue opens another View.
The problem:
From every View UINavigationBar's back button returns me to previous View but i tries to make back button to "menu".
Additional Bar Button Item and segue from it makes very close effect but segue animation is not like in UINavigationController.
How I could clean UINavigationBar transitions history in segue to initial View?
You can try pop to root view controller or You can edit navigation controller viewControllers property and remove/add some VC in between.
You can try Unwind Segue mechanism too.
Here are some methods(function) that navigation controller providing for pop operations. They are returning optional UIViewController (intance) from it’s navigation stack, that is popped.
open func popViewController(animated: Bool) -> UIViewController? // Returns the popped controller.
open func popToViewController(_ viewController: UIViewController, animated: Bool) -> [UIViewController]? // Pops view controllers until the one specified is on top. Returns the popped controllers.
open func popToRootViewController(animated: Bool) -> [UIViewController]?
Here is sample code as a solution to your query::
// if you want to back to root of your app
if let rootNavigationController = self.window?.rootViewController as? UINavigationController {
rootNavigationController.popToRootViewControllerAnimated(true)
}
// But if you want to back to root of your current navigation
if let viewcontroller = self.storyboard?.instantiateViewController(withIdentifier: "NewViewController") as? NewViewController { // or instantiate view controller using any other method
viewcontroller.navigationController?.popToRootViewControllerAnimated(true)
}
How do I have the navigation bar specific to each view controller? On the right side, the navigation bar stays with the view controller, and the left view controller has its own navigation controller. Should I just make a custom transition? Any ideas ?
Add this code in your first viewController
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.setNavigationBarHidden(false, animated: false)
}
Then add navigationBar in your viewcontrollers from storyboad. Or you can add one in viewDidLoad programmatically
I have created a simple tab bar with three views in storyboard. The tab bar works well, but when I try to show another view controller from a button within a tab, the new view is placed over the whole screen and also over the tab bar.
This is how I present the view so far when a button is pressed:
#IBAction func buttonPressed(_ sender: UIButton) {
let newVC = self.storyboard?.instantiateViewController(withIdentifier: "extraVC")
self.present(newVC!, animated: true, completion: nil)
}
The other idea I had was this:
self.tabBarController?.present(vc!, animated: true, completion: nil)
But this didn't work either.
So how can I present another view controller within the tab bar (so that the bottom bar is still shown)?
When you present a view controller modally, its presentation style will be "Full Screen" by default. What you want to do is have it do in this case is just cover part of the screen (the part where the presenting view controller is drawn.
One way to accomplish this is to:
Set the modalPresentationStyle for the presented view controller to be .currentContext or .overCurrentContext
In the view controller that will be presenting the modal, set its definesContext property to true.
These steps can be done either in Interface Builder by setting attributes on the segue and the view controller, or you can modify your code to include the following:
#IBAction func buttonPressed(_ sender: UIButton) {
let newVC = self.storyboard?.instantiateViewController(withIdentifier: "extraVC")
self.definesPresentationContext = true
newVC?.modalPresentationStyle = .overCurrentContext
self.present(newVC!, animated: true, completion: nil)
}
What this combination of properties does is:
Indicate for the presented view controller that you want it to be presented in a non-full screen context (some specific section of the screen)
Indicate that the presenting view controller is in the section of the screen / the context you want the modal to be drawn according to.
More details can be found in the Apple Documentation
When you are using present method, the ViewController is presented modally and covers your UITabBarConntroller. Instead of showing your view modally you can embed every first view controller in your TabBar into UINavigationController and then use method pushViewController to push it onto stack. You will have your TabBar visible and nice looking animation for free.
In Xcode, I created a new project using the Tabbed App template to illustrate the solution above. This will create a project with a tabbar controller and two view controllers. I added a button with the title "view page" to the first view controller and embedded a navigation controller from the storyboard.
The storyboard will look like this after making the above changes:
In the FirstViewController.swift file, I created an IBAction for the button with the following code that will create another view controller called DetailViewController, with the title Favorites and a background color of orange. I used the navigation controller to present it by pushing it onto the navigation controller stack.
#IBAction func viewPageButtonTapped(_ sender: UIButton) {
print("viewPageButtonTapped")
let pinkViewController = DetailViewController()
pinkViewController.title = "Favorites"
pinkViewController.view.backgroundColor = UIColor.orange
navigationController?.pushViewController(pinkViewController, animated: true)
}
When I run the project on the simulator, I got the desired result. Hope this helps give you some ideas.
In your viewController do:
self.tabBarController?.present(nextViewController, animated: true/false, completion: {})
Consider a storyboard where we have UITabBarController, in it any UIViewController(lets call it VC) embedded in a UINavigationController. We want VC to have a BarButtonItems on its navigation bar. This storyboard is presented by push segue from another storyboard (having another navigation controller).
Everything looks OK in XCode, but navigation bar does not change in VC at the runtime. However when I change presenting this storyboard from push to modal, everything seems to be fine. IMHO it is because of embedding the navigation controller but I do not see any reason why it is not working. Any idea how to fix it legally (presenting by push) and without any pain would be helpful.
Thanks in advance
So I think you will have to employ some code to fix your issue but not much. I built a test project to test this and will attach images along with code.
First if I understand you correctly you have a navigationController push the new storyboard in question. See attached image.
I named the storyboard being pushed because that is what is happening. Then in my storyboard named Push here is the setup.
In the first view controller of the tabbarcontroller I added the below code. Obviously this hides the navigation controller that pushed us here. If you then visit controller number 2 our new navigation controller and items show. If hiding the navigation controller in the tabbarcontroller view controller 1 is not what you want to do then. continue reading.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
//or to unhide from returning the opposite ->self.parent?.navigationController?.isNavigationBarHidden = true
self.parent?.navigationController?.isNavigationBarHidden = true
}
If you did not want to hide the navigation controller in the first view controller but when visiting controller 2 you want to see your items then add this to your viewWillAppear and in the first controller in viewWillAppear change the code from true to false.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Do any additional setup after loading the view, typically from a nib.
self.parent?.navigationController?.isNavigationBarHidden = true
}
This hides the parent navigation controller as basically that was covering up your navigation controller in your example. So above hides the parent navigation controller. This is also why presenting modally worked. Your navigation controller was hidden from the start. Hope this helps.
**Edit
If you want the navigation controller in tab 2 view controller but you want to keep the parent in tab one to be able to go back with the back button you can set this in viewWillAppear instead so it would look like this in view controller 1.
//tabcontroller vc 1
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.isNavigationBarHidden = false
}
And in tabcontroller view controller 2 with the item in the bar you could do this.
//tabbarcontroller vc 2 with own navigationcontroller
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.parent?.navigationController?.isNavigationBarHidden = true
}
Finally if you want the back button visible in both controllers but want different right buttons do it programmatically in viewWillAppear
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tabBarController?.navigationItem.setRightBarButton(UIBarButtonItem(barButtonSystemItem: .edit, target: self, action: #selector(FirstViewController.editSomthing)), animated: true)
}
And if you want to remove it in the other controller
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tabBarController?.navigationItem.rightBarButtonItem = nil;
}
In Both of the above examples directly above this, we are keeping the parent navigation controller so you would not need to embed your view controllers of the tab controller inside uinavigation controller.
You could also use a combo of the above code if you want the hide/show parent navigation controller in viewWillAppear as well. Some of this is dependent on the view hierarchy you choose now and in the future.