Normally when a UINavigationController is placed in a UITabBarController the navigation controller pops to root with the respective tab it is in is double tapped. How do I achieve the same effect with a UISplitViewController is between the tab bar controller and the navigation controller? Ideally it would recurse through the view controller's child view controllers and calling popToRootViewController on all navigation controllers that it finds. Do I have to add my own gesture recognizer to the tab bar since it doesn't look like there is a hook for knowing when a user has double tapped a tab?
In Swift 4, it can go something like this:
class TabBarViewController: UITabBarController, UITabBarControllerDelegate {
private var shouldSelectIndex = -1
override func viewDidLoad() {
super.viewDidLoad()
delegate = self
}
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
shouldSelectIndex = tabBarController.selectedIndex
return true
}
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
if shouldSelectIndex == tabBarController.selectedIndex {
if let splitViewController = viewController as? UISplitViewController {
if let navController = splitViewController.viewControllers[0] as? UINavigationController {
navController.popToRootViewController(animated: true)
}
}
}
}
}
Instead of setting up a UIGestureRecognizer I simply keep track of the selected index in shouldSelectViewController and popped to root on my master navigation controller in didSelectViewController if the old selected index was the same as the new one.
Related
I have 5 tabBarItem in my UITabBarController
One scenario, I have to open First index of UITabBarItem by clicking third UITabBarItem
My approach as in below:
extension FiveTabbarController: UITabBarControllerDelegate {
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
if item == (self.tabBar.items!)[2] {
tabBar.selectedItem = (self.tabBar.items!)[0] // ERROR
self.selectedIndex = 0 // NOT WORKING
}
}
}
Error: *** Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason:
'Directly modifying a tab bar managed by a tab bar controller is not allowed.'
Kindly guide me how to achieve this.
You don't want your view controller's base class to be a UITabBarDelegate. If you were to do that, all of your view controller subclasses would be tab bar delegates. What I think you want to do is to extend UITabBarController
class FiveTabbarController: UITabBarController, UITabBarControllerDelegate {
then, in that class, override viewDidLoad and in there set the delegate property to self:
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
}
now this class is both a UITabBarDelegate (because UITabBarController implements that protocol), and UITabBarControllerDelegate, and you can override/implement those delegate's methods as desired, such as:
extension FiveTabbarController: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
if let getSelectedIndex = tabBarController.viewControllers?.firstIndex(of: viewController), getSelectedIndex == 2 {
self.selectedIndex = 0
}
}
}
You can achieve the behavior by doing:
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
if selectedIndex == 2 {
self.selectedIndex = 0
}
}
The reason for the error is because you shouldn't modify the selectedItem, only the index. By modifying the index the tab bar controller will set the selectedItem.
Edited
Didn't notice you are using tabBar's method instead of tabbarcontroller's.
I want to implement a scrollToTop method on all of my viewControllers in my UITabBarController. The following is a method in the UITabBarControllerDelegate and triggers, when I select a tab.
The problem is, that I only want to scroll to the top of the viewController, when the viewController is active. So that the user can switch tabs without losing the scroll position, but when he touches the tab in the tabBar of the currently active tab, it should scroll to the top.
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
if viewControllerThatIsCurrentlyActiveInTabBar == viewController {
scrollToTop()
}
}
Basically, I need that condition of the if statement above.
I tried: viewController.isViewLoaded, tabBarController.selectedViewController == viewController, viewController.isBeingPresented. None of those conditions worked. It would either not trigger scrollToTop() or it would trigger always so that you lose the scroll position when you change tabs because it would immediately scroll to the top.
You need to make a code in should select instead of didselect. As it is unable to find the previous controller after selection. below is the example code for it.
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
if tabBarController.selectedViewController == viewController {
print("Same viewcontroller")
}
return true
}
Can you use below extension for getting top viewcontroller of tabbarcontroller.
extension UIViewController {
var top: UIViewController? {
if let controller = self as? UINavigationController {
return controller.topViewController?.top
}
if let controller = self as? UISplitViewController {
return controller.viewControllers.last?.top
}
if let controller = self as? UITabBarController {
return controller.selectedViewController?.top
}
if let controller = presentedViewController {
return controller.top
}
return self
}
}
You can use above extension below
if let rootViewController = UIApplication.top() {
//do with Active view controller
}
I need to display the UITabBarController, but I don't need it to switch me to the Controller from the viewControllers array. Can I reassign the event or would it be better to create my own such TabBarController?
You can create a subclass for UITabBarController and confirm to UITabBarControllerDelegate
Then you can perform custom tab section actions in shouldSelect method.
func tabBarController(_ tabBarController: UITabBarController,
shouldSelect viewController: UIViewController) -> Bool
{
if viewController == secondViewController {
//Do your actions
return false
}
return true
}
I have a tab bar controller with 3 tab bar buttons. At the moment it looks like this:
class CustomTabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
let firstTabBarController = FirstController()
let firstTabBarNavigationController = UINavigationController(rootViewController: firstTabBarController)
firstTabBarNavigationController.tabBarItem.title = "First Tab"
let secondTabBarController = SecondController()
let secondTabBarNavigationController = UINavigationController(rootViewController: secondTabBarController)
secondTabBarController.tabBarItem.title = "Second Tab"
let thirdTabBarController = ThirdController()
let thirdTabBarNavigationController = UINavigationController(rootViewController: thirdTabBarController)
thirdTabBarNavigationController.tabBarItem.title = "Third Tab"
viewControllers = [firstTabBarNavigationController, secondTabBarNavigationController, thirdTabBarNavigationController]
}
}
Right now, with the code above, all the view controllers are sitting inside the CustomTabBarController
I want the the middle tab bar button, the secondTabBarNavigationController to present a view controller, specifically the UIImagePickerController for a user to select an image, similar to Instagram.
How can this be achieved? I am not using storyboards
Just implement the func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool delegate method of the tabBar controller.
When the desired tab is pressed, present a controller and return false, otherwise return true.
I would also associate an empty UIViewController instance to the tab.
Ex.
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
if (tab for this controller is equal to the expected tab) {
// present you controller
return false
} else {
return true
}
}
I am implementing an iOS App with UITabBar with UINavigationViewController in Swift. Now facing an issue,
If I select first tab, I can see 'A' ViewController, and on click of any contents of 'A', I redirect to 'B' UINavigationViewController, Now If I click on Second tab, and then again Clicks the first tab, It is showing 'B' NavigationViewController. Expected is, It should display 'A' ViewController. How to achieve that?
Try implementing didSelectViewController delegate and then on selection of 'A ViewController' index redirect to root viewcontroller.
func tabBarController(tabBarController: UITabBarController, didSelectViewController viewController: UIViewController) {
let index : Int = (tabBarController.viewControllers?.indexOf(viewController))!
if index == 0
{
let navigationController = viewController as? UINavigationController
navigationController?.popToRootViewControllerAnimated(true)
}
}
Download Sample
#IBAction func itemB(sender: UIButton) {
// do something
self.tabBarController?.selectedIndex = 0
}
In Swift 3.1
Add UITabBarControllerDelegate to your TabBar Class:
class YourClass: UITabBarController, UITabBarControllerDelegate {
After:
override func tabBar(tabBar: UITabBar, didSelectItem item: UITabBarItem) {
let yourView = self.viewControllers![self.selectedIndex] as! UINavigationController
yourView .popToRootViewControllerAnimated(false)
}