Get the title of the tab selected in UITabBarController at application launch - ios

I am trying to get the tab title at application launch.
I can do this to read the tabBarItem.title when the user changes tabs:
func tabBarController(
tabBarController: UITabBarController,
didSelectViewController viewController: UIViewController) {
UserActivity.trackScreen(name: viewController.tabBarItem.title)
}
This method does not fire for the initial selection. I tried this approach in UITabVarController's viewDidLoad method.
override func viewDidLoad() {
super.viewDidLoad()
UserActivity.trackScreen(name: self.selectedItem.title) // I think this is not set yet, it is nil.
}
This does not work.
How do I get the selected tab bar item, or the tab bar item that will be selected, on app launch?

Doing an action on the "selected" tab is a special case for the first launch, because the delegate method didSelectViewController does not fire.
If (and this might be a big "if") you can assume the first tab is the one that is selected on app launch, this code will work for handling the first-launch case:
if let vcs = self.viewControllers {
var firstVC = vcs[0] as UIViewController
UserActivity.trackScreen(name: firstVC.tabBarItem.title)
}
This works for me. Open to better answers.

Related

iOS - double UITabBarControllerDelegate cancels behavior

I have a Swift application with 4 bottom tabs. In the home tab I have a video running. In the 4th tab I have a favorites list.
When the user changes tabs, if he's on the home screen, the video should stop. Also, when the user taps on the 4th tab, the favorites list should update so that the user can see the recent additions.
I have the following on the home tab view controller:
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
print("TAB CHANGED")
if let jwp = jwPlayer {
jwp.stop()
}
}
and this on the favorites tab view controller:
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
let tabBarIndex = tabBarController.selectedIndex
if tabBarIndex == 3 {
let userId = UserDefaults.standard.integer(forKey: "userId")
favoritesVC.updateData(with: userId)
}
}
Both conform to UITabBarControllerDelegate. Both include:
self.tabBarController?.delegate = self
When I launch the app, the video starts playing, I tap on any tab and the video stops. No matter which tab I tap on I see the message "TAB CHANGED". But as soon as I tap on the favorites tab and I move to another, I stop seeing the "TAB CHANGED" message. If I then move to the home screen and play the video and then move to a different tab, the video no longer stops.
The didSelect on the 4th tab is cancelling the didSelect on the 1st tab.
How can I get both of them to work? I have placed them on both view controllers because on the first one I need to reference the video and on the 2nd one I need to reference the list view controller (the 4th view controller actually has two top tabs which switch between a favorites vc and a downloads vc).
UPDATE:
I moved the delegate to the TabBarController subclass. I added the following:
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
let tabBarIndex = tabBarController.selectedIndex
let hvc = HomeViewController()
if tabBarIndex != 0 {
hvc.stopPlayer()
}
}
I added
delegate = self
and
class TabBarViewController: UITabBarController, UITabBarControllerDelegate {
In the HomeViewController I added this:
func stopPlayer() {
print("TAB CHANGED")
if let jwp = jwPlayer {
jwp.stop()
}
}
however when I change tabs, jwPlayer is always nil.
It seems like you are handling the UITabBarControllerDelegate calbacks in multiple places and for that to happen, you are also changing following multiple times -
self.tabBarController?.delegate = self
Here's what you should do -
Handle UITabBarControllerDelegate in one place.
Dispatch the necessary work calls from there to relevant screens.
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
if tabBarIndex == 0 {
homeVC.startPlayer()
}
else {
homeVC.stopPlayer()
if tabBarIndex == 3 {
let userId = UserDefaults.standard.integer(forKey: "userId")
favoritesVC.updateData(with: userId)
}
}
}
This can be handled anywhere, inside your TabBarController subclass if you have one, or any other object that is guaranteed to exist for the application lifetime.
TabBarController’s delegate does not have to be either of the view controllers. It could be some other object (not even a view controller) that could hold weak references to both view controllers, for example, or post a notification etc

iOS Black screen when close modally presented view controller

I have a tabbar controller and suppose if the user goes to 3rd tab and in 3rd view controller, User presses a button and there I present the view controller as modal .
Now if user switched the tab let say tab 1 or 2 without closing the modally presented view controller, and mean while he comes back to tab 3, the user will still able to see the modally presented view controller, now if user closes the modally presented view controller here, then there is a black screen instead of view controller that opened the modally presented view controller.
The possible work around is that I hide the tabbar controller so that user must close modally presented the view controller first. but it is not the requirement right now. Due to some reasons I can not hide bottom tab bar controller.
Please suggest me how to do following
First of all when user switches tab after opening modally presented view controller, so when he closes the modally presented vc he must see the screen at the back
If point no 1 is not possible so when user goes back again to tab 3 (in which he started modally presented VC) there should not be modally presented view controller. In other words when user switched the tab and the modally presented view controller is opened, it must close, before moving to other tab.
You can do it with UITabBarControllerDelegate.
Option 1: Creating subclass of UITabBarController
class MyTabBar: UITabBarController, UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
}
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
if let currentlySelectedViewController = self.viewControllers?[self.selectedIndex] as? YourThirdTabViewController,
let presentedViewController = currentlySelectedViewController.presentedViewController {
presentedViewController.dismiss(animated: false, completion: nil)
}
return true
}
}
Here I am assuming that I have my own sub class of UITabBarController which is set as UITabBarControllerDelegate using self.delegate = self in view didLoad.
Option 2: Make TabBarController's ViewController UITabBarControllerDelegate in their ViewDidLoad
If you dont wanna have your own subclass of UITabBarController all you have to do is simply set your self as UITabBarControllerDelegate in each ViewController's ViewDidLoad
You can have a common protocol or extension to UIViewController and confirm UITabBarControllerDelegate to avoid code duplication if you decide to go down the path of not subclassing UITabBarController
protocol TabBarControllerProtocol: UITabBarControllerDelegate where Self: UIViewController {}
extension TabBarControllerProtocol {
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
if let currentlySelectedViewController = self.tabBarController?.viewControllers?[self.tabBarController?.selectedIndex ?? 0] as? SomeViewController,
let presentedViewController = currentlySelectedViewController.presentedViewController {
presentedViewController.dismiss(animated: false, completion: nil)
}
return true
}
}
In whichever ViewController, you want this logic to kick in you can say
class SomeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.tabBarController?.delegate = self
}
}
extension SomeViewController: TabBarControllerProtocol {}
Thats it.
shouldSelect viewController gets called every time user taps on tab to switch.
The if condition checks if ViewController at selectedIndex (already selected not the new one) is viewController of specific interest or not and also checks if this view controller has presented anything or not, if it has presented it will call dismiss on it
This way when user switches tab from 3rd tab to any other tab, if third tab VC has presented another view controller modally it will dismiss it before it switches tab.

Action not working if UITabBar has more than 5 items

I want to perform an action when the user clicked on some tabs in UITabBar without opening another view. For example, setting tab or share.
Here is what I did :
class ViewTabBarController: UITabBarController,UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
// Do any additional setup after loading the view.
}
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
print("do something")
}
}
This code works fine if the UITabBar has just 5 items.
But the problem here is that if UITabBar has more than 5 items those that are under the "More Tab" did not call the tabBar() function when clicked.
From the documentation description of the didSelect method of UITabBarControllerDelegate:
Tells the delegate that the user selected an item in the tab bar.
What that means is that the method is called when the user taps on one of the buttons in the bar. When you have a 'more' button then that is the button in the tab bar so tapping 'more' fires that method.
The view controllers in the 'more' section are actually processed in a different way involving the use of a UINavigationController. With these views they don't have a button in the tab bar and therefore this even is not fired.

How to force a tab bar item to go to the root level of that item whenever its tapped

I'm new to iOS and Swift. In my storyboard I've set up a tab bar controller where the right most item is a "More" item that leads to a table view controller embedded in a navigation controller. Each item in the table view (static) goes to a view controller (say "View 1", "View 2", and "View 3").
The default behavior is as follows. Let's say I tap "More". Then I'm looking at the table with cells for "View 1", "View 2", and "View 3". Then let's say I tap "View 1". If I then click a different item in the tab bar controller, and then click "More" again, rather than take me to the table view, it will take me to "View 1" since that's the last thing I tapped when I was last in "More". I'd like the behavior to be that if I tap outside of "More" with another tab bar element, any time I tap "More" again, I want it to always take me to the table view, regardless of what I was viewing previously within "More".
Essentially, I want "More" to forget its state or reset its state.
How do I force this to happen?
I will walk you through an example to make sure that it is clear.
Consider that this is the storyboard:
It has a tabbar view controller connected to:
A view controller (the one on the top).
A table view controller which is embeded in navigation controller (the one on the bottom). It has tableview with a static cell, when tap on it, it pushes to another view controller.
So far so good! now, the first view controller class should conforms to UITabBarControllerDelegate and implements tabBarController(_:didSelect:) method, as follows:
class ViewController: UIViewController, UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
tabBarController?.delegate = self
}
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
// im my example the desired view controller is the second one
// it might be different in your case...
let secondVC = tabBarController.viewControllers?[1] as! UINavigationController
secondVC.popToRootViewController(animated: false)
}
}
Remark: if you have more that tow view controllers connected to the tabbar view controller, you should apply this code to the first view controller that should appear in the tabbar view controller.
And that's it!
Output:
Cheers up! hope this helped.
The code supplied works very well with one exception. If you have multiple tabbar items linked to one view controller then the indexed code tabBarController.viewControllers?[1] will only work on "1". It is possible to have "2" and "3" etc. By adding tabBarController.selectedIndex as the index it will always return back to root for that tabbar item. Below is how I fixed the code. I hope this helps someone:
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
// im my example the desired view controller is the second one
// it might be different in your case...
let secondVC = tabBarController.viewControllers?[tabBarController.selectedIndex] as! UINavigationController
secondVC.popToRootViewController(animated: false)
}
after making the class confirm to the tabBarController delegate just add this function
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
self.navigationController?.popToRootViewController(animated: false)
}

IOS - When tapped on a tab item, I need to check condition to show other view controller

In my app, I have UITabBarController, when user tapped on a tabbar item (e.g: tab index is 3), I want to check one condition (if...) to show different ViewController.
So my question is where to implement this condition function?
you need to set the delegate of the UITabBarController as below:
func tabBarController(tabBarController: UITabBarController, didSelectViewController viewController: UIViewController) {
if viewController is TabBarDelegate {
let v = viewController as! TabBarDelegate
v.didSelectTab(self)
}
}
try with create tabbar by button and view is a scrollview

Resources