Keep the UINavigationController while moving to next UIViewController but hide UITabBarController - ios

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.

Related

Use UINavigationController inside UITabbarController

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.

How I could clean UINavigationBar transitions history?

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)
}

UIViewController containing a flow of UIViewControllers

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)
}

UIPageViewControllerDataSource - segueForUnwindingToViewController not being called

I have a UIViewController that is set up as a UIPageViewControllerDataSource named MainPVC.swift. It is my entry point on my Storyboard. In it, I am instantiating another UIViewController named TrackerVC which is set up in my Storyboard - but not connected to MainPVC by segue.
Instantiating TrackerVC:
func viewControllerAtIndex(index: Int) -> TrackerVC {
let childViewController = self.storyboard?.instantiateViewControllerWithIdentifier("trackerVC") as TrackerVC
childViewController.screenIndex = index
return childViewController
}
I have another UIViewController named NotesVC. When I push a button on TrackerVC, I use a custom UIStoryBoardSegue to display NotesVC. On NotesVC, I have another button that I want to return to TrackerVC using another custom UIStoryboardSegue via Unwind.
Cancel button tapped in NotesVC:
#IBAction func cancelButtonTapped(sender: AnyObject) {
self.performSegueWithIdentifier("unwindFromNotesSegue", sender: self)
}
Now to my issue -
When I push the button on TrackerVC, the custom UIStoryboardSegue works great and displays NotesVC as expected. However, when I tap the button on NotesVC, the custom UIStoryboardSegue is ignored and the generic segue that slides the view down is being used.
I have traced the issue down to segueForUnwindingToViewController not being called in TrackerVC. I have all of my connections set up properly in my Storyboard and all of the required methods (the method unwind uses).
I have also tried creating a 3rd UIViewController and displaying it using the custom UIStoryboardSegues along with Unwind and calling it from NotesVC and everything works fine. segueForUnwindingToViewController is being called on NotesVC as expected.
Thanks for any help!
Do you have NavigationController attached? You may need to subclass the NavigationController and add the segueForUnwindingToViewController there. It's what solved the similar problem I was having.

How do I create a UITabBarController with a custom UITabBar class without using IB?

I can create a UINavigationController with custom bar classes by using initWithNavigationBarClass:toolbarClass:. There doesn't seem to be an equivalent for UITabBarController, so how do I get it to use a custom UITabBar class?
Every solution I've seen so far is unsuitable because either
It uses IB
It adds a second tab bar to the UITabBarController instead of changing its existing one, or
It throws UITabBarController away and makes a new controller class.
I want a real UITabBarController created in code using a custom class for its tab bar. How do I achieve this?
This is surprisingly hard! The best I've come up with is subclassing UITabBarController and then doing this in the init:
super.init(nibName: nil, bundle: nil)
object_setClass(self.tabBar, CustomTabBar.self)
(self.tabBar as? CustomTabBar)?.setup()
Unfortunately you can't set the class before the call to super.init (not in Swift anyway), and so by the time you change the class the init method has already been run and so won't be called on your custom subclass. To get around this, I've just added a setup() method to do all my customisation in.
Another option in Swift is to extend UITabBar and do something like this:
extension UITabBar {
open override func willMove(toSuperview newSuperview: UIView?) {
super.willMove(toSuperview: newSuperview)
/// Customise in here.
}
// Modify the height.
open override func sizeThatFits(_ size: CGSize) -> CGSize {
return CGSize(width: size.width, height: 64.0)
}
}
However this will affect all instances of UITabBar, so I prefer the first option.
This solution from ESTabBarControlller, for replace native UITabBar without IB.
open override func viewDidLoad() {
super.viewDidLoad()
let tabBar = { () -> CustomTabBar in
let tabBar = CustomTabBar()
tabBar.delegate = self
return tabBar
}()
self.setValue(tabBar, forKey: "tabBar")
}
As far as I know you can't do this. Your best option to do this without IB is to have your own UIViewController (not subclassing UITabBarController) and then add your own subclass of UITabBar to that Controller.
You may also want to review the Controller Hierarchy if you decide to follow this approach.
I do not think it is possible.
Here is the Apple Documentation that talks about the tabBar property of UITabBarController.
You should never attempt to manipulate the UITabBar object itself
stored in this property. If you attempt to do so, the tab bar view
throws an exception. To configure the items for your tab bar
interface, you should instead assign one or more custom view
controllers to the viewControllers property. The tab bar collects the
needed tab bar items from the view controllers you specify.
The tab bar view provided by this property is only for situations
where you want to display an action sheet using the showFromTabBar:
method of the UIActionSheet class.

Resources