I am building a UIViewController (outer viewController) that contains another UIViewController (inner viewController). To do this I am using a container view. Now I want the inner viewController to push to another UIViewController, so I basically want the inner viewController to be a child of a UINavigationController. I know that you cannot change the content of a container view once it has been initialised (not directly anyways), so I've hit a wall.
Does anyone have any thoughts on how I can nest a UINavigationController inside a UIViewController or do I need to rethink my approach to the problem?
You can simply take a UINavigationController.
Initialise it with your InnerViewController.
Now, add the UINavigationController as (childViewController + addSubview + didMoveToParentViewController) in the OuterViewController.
Now as the InnerViewController is located inside a UINavigationController. You can push whatever you want on the UINavigationController.
Heavily inspired by #7vikram7 I ended up with a container view that embedded a UINavigationController which had my InnerViewController as its root viewController:
In my InnerViewController I am now able to push to any UIViewController, and control my navigation stack, for example:
override func viewDidLoad() {
super.viewDidLoad()
// Hide the navigation bar
self.navigationController?.setNavigationBarHidden(true, animated: false)
// Push to whatever viewController I want to
let viewController = self.storyboard?.instantiateViewControllerWithIdentifier("myAwesomeViewController") as! AwesomeViewController
self.navigationController?.pushViewController(viewController, animated: true)
}
Related
Here is the layout of my app
I am trying to access a variable of startViewController from within TableViewController.
When I print the viewControllers of splitViewController (print(self.splitViewController?.viewControllers)) from within tableViewController this is what I get
Optional([<UINavigationController: 0x12200f600>, <temp.CollectionViewController: 0x121e16860>])
That second viewController(temp.CollectionViewController) should be of type startViewController.
The startViewController in my program presents the collectionViewController using a segue with kind show. The collectionViewController has already been segued to when I call print(self.splitViewController?.viewControllers).
try to cast it as below :
splitViewController?.viewControllers.forEach({ (vc) in
if let startController = vc as? startViewController {
--- Your code ---
return
}
})
I ended up needing to put StartViewController inside a UINavigationController. I was then able to access the first viewController with
var detailStart = (splitViewController?.viewControllers[1] as? UINavigationController)?.viewControllers.first as? StartViewController
I then hid the unwanted navigation bar with
navigationController?.setNavigationBarHidden(true, animated: false)
Which was placed inside viewDidAppear
This ended up being an ok solution for me, because I needed to put CollectionViewController inside a UINavigationController anyway, but it seems kind of hacky and maybe not the best practice
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.
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)
}
Is there a way to navigate from a UIView, which is part of a UIViewController, to another UIViewController neither using storyboards nor navigation controllers?
I have a UITapGestureRecognizer which I want to navigate to another UIViewController (BuyController()) whenever it is tapped once. Accordingly, my code looks like this:
//handle singleTap
func singleTapOnLikesDetected(_ sender: UITapGestureRecognizer) {
print("check") //works
let toController = BuyController()
BarController().pushViewController(toController, animated: true) //error occurs since BarController is no navigationcontroller
}
BarController (UITabBarController, UITabBarControllerDelegate) is my rootViewController in the AppDelegate.swift. The UIView however belongs to another UIViewController called HomeController (UIViewController, UIScrollViewDelegate). I do not use storyboards at all and I would like to keep it that way, as I am going to work on a project in a team (thus, a programmatic solution would be very welcome)...
You can simply do this to navigate, without using navigationController:
self.present(toController, animated: true, completion: nil)
Or Use this,
UIApplication.shared.keyWindow?.rootViewController?.present(toController, animated: true, completion: nil)
Only UIViewControllers can present other UIViewControllers not UIViews so your won't find the present method on the UIView. You also can't replace self with HomeController() because that creates a new HomeController with it's own UIView which hasn't been added to any other view (as the error suggests).
You have a couple of options really:
1) Create a protocol/delegate on your UIView and make the HomeController conform to it. Then the UIView can call the method in the protocol and the HomeController do the actual switching.
2) You could get the root view controller to present the new UIViewController like this: UIApplication.shared.keyWindow?.rootViewController?.present(viewController, animated: true, completion: nil)
I have a UITableViewController that is embedded in a UINavigationController and in a UITabBarController.
When I select a row, I want to open my UIViewController in the UINavigationController but not in the UITabBarController.
When I create the segue from the cell to my UIViewController in the Interface Builder, I select Show (eg. Push).
The problem is that it keeps the UITabBarController as well.
Then I tried the other kinds of segue but none of them display the UINavigationController.
I thought about adding self.tabBarController?.tabBar.hidden = true in viewDidLoad() and override willMoveToParentViewController:
override func willMoveToParentViewController(parent: UIViewController?) {
super.willMoveToParentViewController(parent)
if parent == nil {
self.tabBarController?.tabBar.hidden = false
}
}
It works fine except when I make a driven transition (paning from the edge to go back to the parent view controller).
How to do it the proper way?
UIViewController has a property named hidesBottomBarWhenPushed which will do exactly what you want.
Just set tableViewController.hidesBottomBarWhenPushed = true and you should be good to go!
See Apple's documentation
Edit: if you're using Interface Builder to construct your views, there's actually a checkbox you can click, so you don't have to set it programmatically.