push / present view controller over tab bar Swift - ios

I try to reproduce the same way to push the screen as in the fb messenger for example.
My schema: TabController -> NavController-> embedVC-> pushedVC.
The pushedVC must be over tabbar. I don't want to hide it with tabbar.isHidden = true.

Try to use UIViewController.hidesBottomBarWhenPushed = true on that screen where you need to hide the tab bar.
You can also try to arrange the UITabBarController as child in UINavigationController.
As far as I know (I can be wrong), UITabBarController will not work in the UINavigationController, so sometimes someone uses their custom TabBarController.

SWIFT 5 (works also on previous versions)
Before pushing the ViewController, set hidesBottomBarWhenPushed = true
Example:
let viewController = UIViewController()
viewController.hidesBottomBarWhenPushed = true
navigationController?.pushViewController(viewController, animated: true)

Related

Navigation Controller doesn't show the title while working with TabBar Controller(Programmatically)

I'm using a navigationController as the window.rootViewController. And as the rootViewController of navigationController, I'm using a tabBarController. But when I run the app, the title of the navigationBar is not showing up (My Tabbar Controller shows up and I'm being able to see my viewController. No problem about that). I've tried to name the navigationBar in every viewController of the tabBar with using both of the code below in the viewDidload, but still nothing changed.
navigationController?.title = "title"
navigationController?.navigationBar.topItem?.title = "title"
I've set the navigationController with tabbar in the SceneDelegete as below:
let countriesTabbarController = TabBarControllerBuilder.build()
let navigationController = UINavigationController(rootViewController: countriesTabbarController)
window?.rootViewController = navigationController
window?.makeKeyAndVisible()
How can I show my navigationController title for every viewController of the tabBar?
Each view controller has a property called navigationItem of type UINavigationItem. If you set its title, it will show up in the navigation bar if one exists.
For example in viewDidLoad, add this:
self.navigationItem.title = "title"
In other words: The title is set on the view controller contained in a navigation controller stack, not the navigation controller itself. Also not on the navigation bar, which probably also has a good reason I can't recall at the moment.
https://developer.apple.com/documentation/uikit/uinavigationitem

How to update bottom TabBar without doing the navigation in Swift?

I have an app with a HomeView and a bottom TabBar which load other views on tap. On this TabBar bar I also have a More button which on click opens a listView, each cell of this view also opens other views. (When I'm talking about views, I mean new tunnels/navigation controllers are displayed for example: MyPicturesViewController, SettingsViewController etc...).
There is my problem, I have to implement a feature that from my HomeView on a button click it navigates to a specific view that is not on the TabBar but in the More list and also update the TabBar to highlight the More button.
I tried at first to navigate manually with this code:
let viewController = UIStoryboard.instantiate(NewViewController.self)
self.viewController?.navigationController?.pushViewController(viewController, animated: true)
it works pretty well
and then update the tabbar manually
self.viewController?.tabBarController?.selectedIndex = 4
But updating the TabBar redirect automatically to the selected index which is the More list view and don't even take in count the manual navigation I did in the previous code.
So my question is, is it possible to update TabBar bar without loading its own navigationcontroller? If not how can I achieve what I want to do, I tried many things such as create a navigationcontroller and its viewcontrollers and replace the tabbar viewcontrollers stacks but I didn't succeed.
Thank you in advance for those who will take time to help me!
Instead doing it before
self.viewController?.tabBarController?.selectedIndex = 4
Push your controller in tabBar delegate method
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
if viewController == desiredNavigationController {
show(desiredNavigationController, animated: true) // push
return false
}
return true
}

Tab bar missing when a segue is added

So I have 3 VCs embedded in a navigation controller and a tab bar controller. However, whenever I add a segue from the 3rd VC to the 1st VC, the navigation bar and tab bar disappear from the storyboard. So I tried adding a segue programmatically on the 3rd VC swift file as such:
let collectionVC = self.storyboard?.instantiateViewController(withIdentifier: "collectionVC") as! CollectionViewController
let navigationVC = UINavigationController(rootViewController: collectionVC)
self.present(navigationVC, animated: true, completion: nil)
Unfortunately, with the added code above, the tab bar is still missing but the navigation bar is there. I hope someone could help me.
If I understood correctly your problem, you are using a UINavigationController and you are trying to go from VC3 to VC1, which means that you have a navigation stack like this:
VC1 -> VC2 -> VC3
If you want to go back to VC1, you can use:
navigationController?.popToRootViewControllerAnimated(true)‌​
You will still have your tab bar, your navigation bar and - most important - you won't create another instance of VC1. This is the correct - and easiest - way of dealing with a UINavigationController.

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.

How to Push a New View Without Storyboards Swift

First of all, I don't want to use storyboards at all. I am able to "present" the targeted view controller, but I can't get it to show with the standard iOS back button at the top. My understanding is I can get this to work by pushing the controller instead of presenting it. I don't get any errors, but nothing happens.
On a button click this code is run. The commented code is what successfully presented the ForgotPasswordPage :
// Changes to Forgot Password Page
func forgotPasswordSwitch(sender: UIButton!) {
//let ForgotPassword:ForgotPasswordPage = ForgotPasswordPage()
//self.presentViewController(ForgotPassword, animated: true, completion: nil)
let ForgotPassword = ForgotPasswordPage()
self.navigationController?.pushViewController(ForgotPassword, animated: true)
}
You have to manually create a UINavigationcontrollerto get the back bar. To do this you can use the code from this answer, which achieves this by using this code:
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
var nav1 = UINavigationController()
var mainView = ViewController() //ViewController = Name of your controller
nav1.viewControllers = [mainView]
self.window!.rootViewController = nav1
self.window?.makeKeyAndVisible()
Here just add all the ViewControllers you want to be under the Navigation controller into the array and then push between them.
Hope that helps, Julian
I know that sometimes it's much better develop apps programmatically, but there are many time that Storyboard can save you a lot of time. You just simply use it for this situations...
1) In you Storyboard, localize the view controller you want to enable for pushing
2) Select it and find the "Editor" in your top bar
3) Select Editor->Embed In->Navigation Controller
And now your view controller it's ready for pushing

Resources