TabBar shouldSelectViewController Issues - swift

I'm using the tab bar controller template that is packaged with swift. I have embedded the FirstViewController and SecondViewController into a navigation controller. I have added a third ViewController that is accessed by a segue from the FirstViewController. When I'm in the third ViewController and I hit the SecondViewController tab the simulator takes me to the SecondViewController but when I hit the FirstViewController tab, I'm taken back to the third ViewController - I want to go back to the FirstViewController when I select it's tab. What is the way to implement this? Searching through the documentation I think I should be using:
func tabBarController(_ tabBarController: UITabBarController,
shouldSelectViewController viewController: UIViewController) -> Bool

Pop to root view when tab is selected. This answer seems relevant.
Swift code:
func tabBarController(tabBarController: UITabBarController, didSelectViewController viewController: UIViewController) -> Bool {
if let viewController = viewController as? UINavigationController {
viewController.popToRootViewController(animated: false)
}
}

For newer Versions of Swift the delegate function should be as following:
func tabBarController(tabBarController: UITabBarController, didSelectViewController viewController: UIViewController) {
if let viewController = viewController as? UINavigationController {
viewController.popToRootViewControllerAnimated(false)
}
}
Your class has to be consistent with the UITabBarControllerDelegate protocol and the delegate property should be set during the loading process.

Related

swift disable pop vc animation when tap on tabbar item

I have a tabbarcontroller when i tap on item which is selected then navigationController do pop to root vc. How can i disable animation ? i want that it will do pop to root vc but without animation
i can just disable pop to root vc, but dont know how to disable animation
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
return viewController != selectedViewController
}
Try popToRootViewController programmatically when tap is detected:
extension ARTabBar: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
if viewController == selectedViewController {
if let viewController = viewController as? UINavigationController {
viewController.popToRootViewController(animated: false)
}
return false
} else {
return true
}
}
}

TabBar Controller with custom action

I am new to swift. I am implementing tabbar controller in my Project and facing some design difficulty. My goal is when user click a tabbar item it should not navigate to another view controller. It should stayed in the current view and add a pop up view to the current view controller.I have tried but always it navigate to next view controller.
Create a UITabBarController subclass and use that class for your tab bar controller. Confirm to UITabBarControllerDelegate in the tab bar controller and return false in tabBarController shouldSelect method when you don't want to navigate to a view controller. Here you can show the popup view.
class TabbarController: UITabBarController, UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
delegate = self
}
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
if let navigationController = viewController as? UINavigationController,
navigationController.viewControllers.contains(where: { $0 is MoreViewController }) {
//show pop up view
return false
} else {
return true
}
}
}
Or you can add UITabBarControllerDelegate in one of its embedded view controller like this
class ViewController: UIViewController, UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.tabBarController?.delegate = self
}
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
if let navigationController = viewController as? UINavigationController,
navigationController.viewControllers.contains(where: { $0 is MoreViewController }) {
//show pop up view
return false
} else {
return true
}
}
}

Swift - resetting vc in nav controller that is embedded in tab bar

I have a customerViewController that has a simple a form. When the user presses submit, a segue is triggered and another view appears. The problem occurs when the user goes back to the customerViewController and finds all of the old information still there. I could simply reset the form fields, but what I'd really like is to find a way to reset the entire VC. From what I've learned so far, the way to reset a vc that hasn't been pushed is to remove it and then add it back.
customerViewController is the initial view controller in a navigation controller which is embedded in a tab bar controller. I have a tabBarController class that is a UITabBarControllerDelegate. This is where I call:
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
if item.tag == 2 { //This is the tab with my navigation controller
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "CustomerVCID")
var viewcontrollers = self.navigationController?.viewControllers
viewcontrollers?.removeFirst()
viewControllers?.insert(vc, at: 0)
self.navigationController?.setViewControllers(viewcontrollers!, animated: true)
}
The problem with my code is that navigationController?.viewControllers is nil in the code above. I can reference viewControllers which gives me a list of tab bar viewControllers, but I'm not sure how to get from there to the navigation controller.
I guess my question is, assuming that I'm on the right track, how do I reference the view controllers in my navigation controller?
You can reset your form values in vc inside the viewWillAppear(_:),
class ViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
//clear the textfields, textviews values etc. here.
}
}
It turns out, I was over-complicating things by trying to access navigationController.viewControllers or tabBarController.viewControllers. All I needed was viewControllers which is a property of UITabBarController that contains an array of the controllers associated with each tab:
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
if item.tag == 2 { //tab with navigation controller
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vcon = storyboard.instantiateViewController(withIdentifier: "CustomerVCID")
for viewcontroller in viewControllers! {
if let vc = viewcontroller as? UINavigationController {
vc.viewControllers.removeFirst()
vc.viewControllers.insert(vcon, at: 0)
vc.setViewControllers(vc.viewControllers, animated: true)
}
}
}

ios swift method to navigate through viewcontroller programatically

i would like create my function to navigate through my views.
i have a function
func ShowView(name:String,caller:AppDelegate){
//story board
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let initViewController: UIViewController = storyBoard.instantiateViewControllerWithIdentifier(name) as UIViewController
caller.UIWindow?.rootViewController = initViewController
}
but i need to call this function from AppDelegate and UIViewController , how can i get uiwindow from UIViewController , from AppDelegate is ok but i can't keep AppDelegate like caller parameter cause i would like that it work with UIViewController.
I don't really know which i must use for my parameter.
many thanks
You don't need to replace rootView Controller every time if you are moving from master to details views. Connect root view to details view using segue. Set a identifier to segue in your storyboard. Overide prepareForSegue in your viewController class.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "showDetails")
{
var viewController=segue.destinationViewController as DetailsView
//set data
}
}

Pop to root view when tab is selected

I've been having some trouble with something I thought might be easy. I have a table in my root view controller, when a row is selected I push a new view and from there I go to another tab.
My question is how do I make sure that as soon as the user taps the first tab the navigation controller will pop to root?
Following delegate is called while each tab is selected on tabbar.
-(void) tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
Put following code inside this delegate method.
if ([viewController isKindOfClass:[UINavigationController class]])
{
[(UINavigationController *)viewController popToRootViewControllerAnimated:NO];
}
its working fine on my app.
For Swift lovers:
import UIKit
class YourTabBarControllerHere: UITabBarController,
UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self;
}
func tabBarController(tabBarController: UITabBarController,
didSelectViewController viewController: UIViewController) {
if let vc = viewController as? UINavigationController {
vc.popViewControllerAnimated(animated: false);
}
}
}
Edit: Swift 3 update, thanks to #Justin Oroz for pointing that out.
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)
}
What you are trying to do sounds a little bit odd. Have you read the Human Interface Guidelines on combining UINavigationControllers and UITabBarControllers?
However, what you need to do is detect the selection of the tab by setting a delegate for your UITabBarController and implementing the tabBarController:didSelectViewController: delegate method. In this method you need to pop back to the root view controller using UINavigationController's popToRootViewControllerAnimated: method.
Swift 5.1 Answer:
class YourTabBarName: UITabBarController, UITabBarControllerDelegate
{
override func viewDidLoad()
{
super.viewDidLoad()
self.delegate = self
}
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController)
{
if let vc = viewController as? UINavigationController
{ vc.popToRootViewController(animated: false) }
}
}
[self.navigationController popToRootViewControllerAnimated:NO];
Swift 4.2
The solution that works for me is to subclass the UITabBarController and add the two delegate functions as follows:
import UIKit
class MyCustomTabBarController: UITabBarController, UITabBarControllerDelegate {
var previousSelectedTabIndex:Int = 0
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
}
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
self.previousSelectedTabIndex = tabBarController.selectedIndex
}
override func tabBar(_ tabBar: UITabBar, didSelect item:
UITabBarItem) {
let vc = self.viewControllers![previousSelectedTabIndex] as! UINavigationController
vc.popToRootViewController(animated: false)
}
}
Make sure you set animated to false otherwise you will get
Unbalanced calls to begin/end appearance transitions for the targeted ViewController
Try this.
class TabBarClass: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
let vc = self.viewControllers![selectedIndex] as! UINavigationController
vc.popToRootViewController(animated: false)
}
}
First, you should create subclass of UITabbarController and add Observer:
- (void)viewDidLoad {
[super viewDidLoad];
[self.tabBar addObserver:self forKeyPath:#"selectedItem" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];
}
When tabbar is selected, We will process in method:
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
if ([keyPath isEqualToString:#"selectedItem"] && [object isKindOfClass:[UITabBar class]]){
UITabBar *bar = (UITabBar *)object; // The object will be the bar we're observing.
// The change dictionary will contain the previous tabBarItem for the "old" key.
UITabBarItem *wasItem = [change objectForKey:NSKeyValueChangeOldKey];
NSUInteger was = [bar.items indexOfObject:wasItem];
// The same is true for the new tabBarItem but it will be under the "new" key.
UITabBarItem *isItem = [change objectForKey:NSKeyValueChangeNewKey];
NSUInteger is = [bar.items indexOfObject:isItem];
if (is == was) {
UIViewController *vc = self.viewControllers[is];
if ([vc isKindOfClass:[UINavigationController class]]) {
[(UINavigationController *)vc popToRootViewControllerAnimated:YES];
}
}
}
}
The UTabController suggests a different UX for letting a user "pop to root". When switching back to a tab, it keeps the full UINav stack from before. If they tap the bar item a second time (tapping the selected tab), only then does it pop to root. That's all automatic. Some apps, like instagram, allow a third tap to scroll to top.
I'd suggest sticking with the defaults as that's what users will be expecting.
The below had worked for me .This code in swift 3:
1> subclass UITabbarController and implement two below method with one iVAr:
class MyTabBarController: UITabBarController ,UITabBarControllerDelegate {
var previousSelectedTabIndex : Int = -1
}
2> set the tabbar delegate in viewdidLoad
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self // you must do it}
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
self.previousSelectedTabIndex = tabBarController.selectedIndex
}
func tabBarController(_ tabBarController: UITabBarController,
shouldSelect viewController: UIViewController) -> Bool {
if self.previousSelectedTabIndex == tabBarController.selectedIndex {
let nav = viewController as! UINavigationController // mine in nav_VC
for vc in nav.childViewControllers {
if vc is YUOR_DESIRED_VIEW_CONTROLLER {
nav.popToViewController(vc, animated: true)
return false// IT WONT LET YOU GO TO delegate METHOD
}
}
}
return true
}
tabBarController.selectedIndex give you the selected tab
In tabBarController_shouldSelect_viewController method you can set your desired view controller with some easy calculation.
if you are not getting the above code play with both above method and you come to know how both working together
Use selected view controller to popToRootViewController. Basically you need to cast this instance.
Swift
((selectedViewController) as! UINavigationController).popToRootViewController(animated: false)
//
// I just added extra line so the scroll bar won't annoy you.
Xcode 11.5, Swift 5:
You don't need to use two delegate methods. Only one is enough:
extension CustomTabBarController: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
if tabBarController.viewControllers?.firstIndex(of: viewController) == tabBarController.selectedIndex,
let navigationController = viewController as? UINavigationController {
navigationController.popToRootViewController(animated: true)
}
return true
}
}
//create a tabbar controller class set it to your TabbarController in storyboard
import UIKit
class MyTabbarViewController: UITabBarController,UITabBarControllerDelegate{
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
}
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController)
{
if let vc = viewController as? UINavigationController
{ vc.popToRootViewController(animated: false) }
}
}