first of all sorry if the title is a bit confusing, I'll try to explain it better.
I don't use StoryBoard in my application so everything is done programmatically.
So I have a UITabBarController
which is the rootViewController
of my application as defined in the AppDelegate
:
window?.rootViewController = CustomTabBarController()
As you can see, the customized TabBar is defined as follow in the class CustomTabBar :
class CustomTabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
let mainController = MainViewController()
let mainNavigationController = UINavigationController(rootViewController: mainController)
mainNavigationController.title = "Home"
mainNavigationController.tabBarItem.image = UIImage(named: "icon_home")
let searchController = SearchViewController()
let searchNavigationController = UINavigationController(rootViewController: searchController)
searchNavigationController.title = "Search"
searchNavigationController.tabBarItem.image = UIImage(named: "icon_search")
// I have four items in the TabBar, all defined the same way as above.
//I removed them to keep the code a bit shorter.
viewControllers = [mainNavigationController, cameraNavigationController, searchNavigationController, profileNavigationController]
}
}
Basically the TabBar looks as follow :
So when I click a TabItem, the NavigationController
for the specific TabItem appears with its rootViewController
; everything works fine here.
However, the root View Controller for the Camera is full screen, meaning the TabBar is not visible for the user. I added a close button on the view so the user can "quit" the view. The problem is : as every item has its own navigation controller, when the camera item is selected, the CameraViewController is the first in the NavigationController
's stack, therefore, I can't pop it if the user clicks the close button.
I managed to find a way around it with this answer.
//close button clicked
self.tabBarController.selectedIndex = 0;
It works but this way, the user is "redirected" every time to the same tab.
What I want to do is "redirect" the user to the previous tab he was in. For example, let's say he was in "Search" and then he clicked on the camera TabItem, if he decides to close, he will be back in the "Search" section.
A solution would be to have a variable that stores the actual ViewController's associated position so when the button close is clicked, I just set the selected index to this variable. However, I don't think this is very elegant.
Another one would be to only have three NavigationController
(deleting the one for the Camera) and just pushing/poping the CameraViewController on the actual NavigationController
(which changes depending on the section the user is). This solution seems better but I can't find a way to make it work.
I've tried to implement the function didSelectViewController
and check if it equals to the cameraViewController,(if true) just push it to the actual NavigationController. It doesn't work as the actual NavigationController
is "nil" when I try to perform this action.
My question is : What is the best way to perform the task ?
Thanks for your time and sorry for the long post.
EDIT 2 :
So I tried the solution proposed by Joe Benton, which is similar to comments I received.
The idea is to present the camera screen modally with shouldSelectViewController
, his code was in Objective-C so here is the Swift 2 version :
func tabBarController(tabBarController: UITabBarController, shouldSelectViewController viewController: UIViewController) -> Bool {
if viewController == cameraViewController {
self.presentViewController(cameraViewController, animated: true, completion: nil)
return false
}
return true
}
}
As you can see, I present directly the ViewController
and not a NavigationController
but this is a detail.
Don't forget to inherit from UITabBarControllerDelegate
like so :
class CustomTabBarController: UITabBarController, UITabBarControllerDelegate
And set the delegate to self like so :
self.delegate = self
However, this solution does not work, if you try this code and select the CameraTab, you will get the following error :
Application tried to present modally an active controller UITabBarController
After searching a bit online, I appears that you can't present modally a ViewController
that is used by the TabBarController
, meaning you can't use presentViewController
for a viewController
defined previously this way :
viewControllers = [mainNavigationController, cameraViewController, searchNavigationController, profileNavigationController]
You can learn more about it here
So what I did was to instantiate two CameraViewController at the top of the class (so I can access them wherever I want) this way :
class CustomTabBarController: UITabBarController, UITabBarControllerDelegate {
let cameraControllerTest = CameraViewController()
let cameraViewController = CameraViewController()
override func viewDidLoad() {...}
...
}
Then, I added one of them in the ViewControllers
array so it appears on the UITabBarController
I doesn't matter which one.
After that, in the shouldSelectViewController
function, you just check which tab was clicked, if it is the instance of the CameraViewController
you have in your TabBar
array, you just present the other instance that is not in the array, else, you just return true. It looks like this :
func tabBarController(tabBarController: UITabBarController, shouldSelectViewController viewController: UIViewController) -> Bool {
if viewController == cameraViewControllerInArray {
self.presentViewController(cameraViewControllerNotInArray, animated: true, completion: nil)
return false
}
return true
}
I don't know if it is the best way to get around this error so let me know. Thank you all for your answers !