How to handle a view controller independantly in UITabBarController? - ios

My app's root is a UITabBarController with 5 sections, each of them contains a UINavigationController.
I also want to add a chat feature in the app, that could be accessed with a rightBarButton present in every navigation bar of the app. I would like it to show a chat UIViewController on the screen, unselecting the currently selected tab bar item and without losing the navigation state of the five navigation controllers, even the one that was previously selected before tapping the chat button. What would be my best bet to do it?
Thanks for your help/ideas.

Step 1: In your storyboard add a ChatViewController
- Embed your ChatViewController in Navigation View Controller if you wanna have a navigation bar. Add a close BarButtonItem in your ChatViewController.
Step 2: Create a close Action in your ChatViewController and binding with BarButtonItem in the StoryBoard.
#IBAction func CloseAction(_ sender: UIBarButtonItem) {
dismiss(animated: true, completion: nil)
}
Step 3:
In storyboard, select the Navigation Controllers with the rightBarButton and choose Present Modally and connect to Navigation Controller of the ChatViewController.
You can go to the ChatViewController without losing the navigation state of any navigation controller.

Related

TabBar is hidden after going back to the initial ViewController of a TabBarController Using Segue

I have a tab bar controller with three table view controllers and the second VC is embedded in a navigation Controller. in the second VC, I made the tabBar hidden using this line self.tabBarController?.tabBar.isHidden = true and I created a bar button to go back to the first view controller which is the "home" VC using segue with modal presentation.
Screenshot of my StoryBoard
My problem is after hitting the back button and going back to home VC from the second VC, the tabBar is still hidden even though I put self.tabBarController?.tabBar.isHidden = false in the home VC's viewWillAppear method and the second VC's viewWillDisappear method.
Here is the result that I expected vs what I got
expected home VC
result home VC
How can I make the Tab Bar show?
When you are using the modal presentation segue, you are creating a completely new instance of HomeViewController. The new HomeViewController is not linked to the TabBarController in your hierarchy.
Here's you initial view hierarchy:
TabBarController
-> HomeVC
-> CreateVC (Navigation Controller)
-> CreateQuizVC
-> SavedVC
Now after tapping the back button you'll get the following:
TabBarController
-> HomeVC
-> CreateVC (Navigation Controller)
-> CreateQuizVC
-> HomeVC(2)
-> SavedVC
What you could do is, instead of using the segue to go back, add an IBAction in your code to set the selectedIndex of the TabBar programatically, and link the Back UIBarButtonItem to this IBAction.
#IBAction func backButtonAction(_ backButton: UIBarButtonItem) {
// Keep in mind that the CreateQuizVC is embeded in a NavigationController.
// The NavigationController is the child of the TabBarController
navigationController?.tabBarController?.selectedIndex = 0
navigationController?.tabBarController?.tabBar.isHidden = false
}
However, my suggestion is you use the TabBar as it's intended by Apple. Don't hide it while you're presenting your CreateQuizVC, and use the TabBar to navigate between the tabs. This will help with user experience, since everybody on iOS is expecting this behaviour from a TabBar.

Opening a navigation controller from Tabbar with back button on first ViewController

I know this question has been asked so many times on the SO, but I am asking this just because it is relevantly different and also some answers are out dated and some are in Objective-c which I can not understand properly.
USE CASE:
I have a UITabBar controller, and it is working fine. Let say I have 4 tabs in it and user click on the button given in the Tab 4. now on it I have to open some series of View Controllers. let say User has following patteren to follow.
4.A-->4.B--> 4.C and can go back to first like so: 4.C-->4.B-->4.A
And finally User must also be allowed to go back to Tab4 after closing 4.A view controller
WHAT I DID:
I am able to open the View controller using this code.
let VC1 = storyboard.instantiateViewController(withIdentifier :"myVcId") as! UIViewController
let navController = UINavigationController(rootViewController: VC1) // Creating a navigation controller with VC1 at the root of the navigation stack.
self.present(navController, animated:true, completion: nil)
This is opening a navigation controller and having me navigate the view controller as per my requirements but I want following thing too
WHAT I WANT: As I am presenting the Navigation Controller modally, I want to show the back button at very first view controller, and I want that if user select back button it kills all the Navigation controller and go the previous view controller where he came from
You need to select the 4th tab and embed it inside a navigation then do
let vc = storyboard.instantiateViewController(withIdentifier :"myVcId") as! UIViewController
self.navigationController?.pushViewController(vc, animated: true)
then you'll see a back on that first vc , to return to that tab do
self.navigationController?.popToRootViewController(animated: true)
As far as I understood, your structure looks like this:
A TabBarController, with ViewControllers as tabs, and from them you present a navigation controller with other ViewControllers, like this:
(TabBar) -vc-> (View) -present-> (NavController) -root-vc-> (View)
I have two suggestions for making it easier to handle.
First option
Use navigation controllers as tabs for your TabBarController, and make your ViewControllers that are tabs their root view controllers.
(TabBar) -vc-> (NavController) -root-vc-> (View) -push-> (View)
Second option
Another option is to use a navigation controller as your initial view controller, and make your tab bar this navigation controller's root view controller.
(NavController) -root-vc-> (TabBar) -view-> (View) -push-> (View)
Both options should work, and the first one should be a little easier to handle.
You need to instantiate the navigationController with 4A as the rootViewController.
Then, in the viewDidLoad for 4A, you need to instantiate 4B, and push it to your navigationController.
Then, finally, in your 4B viewController's viewDidLoad you need to instantiate 4C and push it to your navigationController's stack.
P.S.: Push all the viewControllers without animations.
This should be achieving your strange scenario.
EDITED:
You need your 4th tab from your tabBar to be a navigationController,
and its root view Controller to be the initial VC which will then push
your new navController, otherwise you won't have the back button.
So, your stack should be something like this:
4CVC
|-(push)
4BVC
|-(push)
4AVC
|-(push)
newNavController
|-(push)
someVC
|
navController
|
tab1 tab2 tab3 tab4
|
tabBar

Changing view controller to tab bar controller

I have a tab based application with 2 tabs (HomeViewController and SettingsViewController).
On the SettingsViewController I have a button that will take the user to a third view (ChangeSomeSettingController).
On that third view, I have a TableView with this function
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
...
let settingsViewController = self.storyboard!.instantiateViewControllerWithIdentifier("SettingsViewController") as! SettingsViewController
self.presentViewController(settingsViewController, animated:true, completion:nil)
}
This will move the user back to the SettingsViewController. However, I am not able to get the tab bar to display at the bottom of the screen like it is when I first load the application.
How can I go back to the SettingsViewController and keep the tabs at the bottom of the screen?
First of all, presentViewController:animated:completion: will present SettingsViewController modally. What you need is a UINavigationController flow.
1) Add create UITabBarController
2) Select the item(UIViewController) that will has a UINavigationControler flow.
3) Delete it
4) Add UITableViewController
5) Add the new UITableViewController to the UITabBarController
6) Select the UITableViewController and embed in(Editor > Embed In > Navigation Controller) to a Navigation Controller.
7) Add a UIViewController and add a segue(Show) between UITableViewController and UIViewController
8) The result final result should be something like this:
In ChangeSomeSettingController, if you pushViewController from SettingViewController, you should call
navigationController?.popViewControllerAnimated(true)
or if you want to back to SettingViewController
//back to root view controller from navigation
navigationController?.popToRootViewControllerAnimated(true)
// you should call to tabbar view controller
UITabbarViewController *tabbarVC = (UITabbarViewController *)[UIApplication sharedApplication] delegate].window.rootViewController;
[tabBarController setSelectedIndex:1]; // 1 is index of SettingViewController
This's example for me, with your problem you can modify to adapt your requirement. hope this help for you.
This line:
self.presentViewController(settingsViewController, animated:true, completion:nil)
will not move back the user to settings VC, it will present a new instance of the settings VC, which ofcourse covers the tab bar.
You need to push the VC when you select the tabs, so you don't have to present them again if you want to go back.
If you don't want a navigation controller, then you should present the tab bar controller instead of settings vc.

Present View Controller Over current tabBarController with NavigationController

When presenting or dismissing VC, I do not want to keep hiding and showing tabBar because it creates a poor user experience. Instead, I want present the next VC straight over the tab bar such that when I dismiss the nextVC by dragging slowly from left to right, I can see the tabBar hidden behind the view (As shown in image below)
Note, my app has two tabs with two VCs(VCA,VCB) associated to it. Both VC also have navigation bar embedded. VCA segues to VCA1 and VCB segues to VCB1. At the moment, inside VCA and VCB I am calling the following function to segue with some hiding and unhiding done when viewWillappear (Code below).
self.navigationController?.showViewController(vc, sender: self)
// Inside ViewWillAppear Only reappear the tab bar if we successfully enter Discover VC (To prevent drag back half way causing tab bar to cause comment entry to be floating). This code check if we have successfully enters DiscoverVC
if let tc = transitionCoordinator() {
if tc.initiallyInteractive() == true {
tc.notifyWhenInteractionEndsUsingBlock({(context: UIViewControllerTransitionCoordinatorContext) -> Void in
if context.isCancelled() {
// do nothing!
}
else {
// not cancelled, do it
self.tabbarController.tabBar.hidden = false
}
})
} else {
// not interactive, do it
self.tabbarController.tabBar.hidden = false
}
} else {
// not interactive, do it
self.tabbarController.tabBar.hidden = false
}
----------Working solution from GOKUL-----------
Gokul's answer is close to spot on. I have played with his solution and came up with the following improvement to eliminate the need to have a redundant VC and also eliminate the initial VC being shown for a brief second before tabVC appears. But without Gokul, I would never ever come up with this!!
Additionally, Gokul's method would create a bug for me because even though I do have a initial "normal" VC as LoginVC before tabVC is shown. This loginVC is ONLY the rootVC if the user needs to login. So by setting the rootVC to tabVC in most cases, the navVC will never be registered.
The solution is to embed navigation controller and tabBar controller to one VC. But it ONLY works if the navVC is before the TabBarVC. I am not sure why but the only way that allowed me to have navVC-> tabVC-> VC1/VC2 is to embed VC1 with a navVC first than click on VC1 again to embed tabVC (It wouldn't allow me to insert one before tabVC and I also had to click the VC1 again after embedding the NavVC).
For your requirement we need to make some small changes in your given view hierarchy
Let me explain step by step,
To meet your requirement we have to add a UIViewController(let's say InitialVC) embedded with a UINavigationController and make it as initial viewcontroller.
Then add a UITabbarController with 2 VC (VCA,VCB) // IMPORTANT: Without any navigationcontroller embedded.
Add a segue between InitalVC and TabbarController with an unique identifier(ex: Initial)
In viewWillAppear of InitalVC perform segue as below (InitialVC is unnecessary to our design we are using this just to bridge navigationController and tabbarController).
self.performSegueWithIdentifier("Initial", sender: nil)
In TabbarControllerclass hide your back button, this ensures that InitialVC is unreachable.
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.hidesBackButton = true
}
Now add a segue from a button between VCA and VCA1, thats it build and run you will see VCA1 presenting over VCA's tabbar.
What we have changed?
Instead of adding UINavigationController inside UITabbarController we have done vice versa. We can't directly add Tabbar inside navigation to do that we are using InitialVC between them.
Result:
1st way is create a image of the tabbar using UIGraphicsGetImageFromCurrentImageContext and set it on the bottom of the other view...
2nd way is show the next view in another new window that is above the tabbar, that way you wont need to hide the tabbar anymore, but seems like its in the navigation controller so this way doesnt seems available
Hiding and unhiding the tab bar is unnecessary. You only need to embed the UITabBarController inside the UINavigationController. That is, UINavigationController as the initial vc, UITabBarController as the root vc of UINavigationController.

Show tab bar on modal View

On click button "Go!" show white View Controller.
I want show tabbar on white ViewController, but it doesn't show. How i make this correctly? Swift please, thanks.
Do this in firstView,so the firstViewController embed in NavigationController,then make a push segue,the tabBar will not dismiss.
You have to use navigation controller to see tab bar. by presenting modally you can't get tab bar in white controller.
Swift code:
let secondViewController = self.storyboard.instantiateViewControllerWithIdentifier("SecondViewController") as SecondViewController
self.navigationController.pushViewController(secondViewController, animated: true)
I think you are using wrong segue just delete old segue and add new segue this way by pressing control key:

Resources