Tab Bar Controller index changing? - swift

I have 2 tabs : Calc VC and Browse VC; they are in that order and the app starts on Calc VC.
Using a print statement tabbarcontroller.selectedindex in viewwillappear of each VCs I learned that when Calc VC first appear, it shows an index of 0. When I tap on Browse VC, it shows an index of 1. So far so good.
When I tap on Calc VC, its index becomes 1 and Browse VC becomes 0. It remains like that until you quit the app.
Why it's an issue? I'm trying to disable Browse VC when Calc VC is active by using .isEnabled = false but I can't do it that way because of the changing index

At the time of viewWillAppear triggered, selectedIndex might not be changed. You should use viewDidAppear.
override func viewDidAppear(_ animated: Bool) {
print(tabBarController?.selectedIndex)
}
Or you can use tabBar(_:didSelect:) of UITabBarDelegate
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
print(tabBar.items?.index(of: item))
}

Related

swift xcode iOS: can I re-use a loaded modal fullscreen view controller?

I have a storyboard with two view controllers. First one, VC_1, has one button that opens 2nd one - VC_2.
VC_2 also has a button that opens VC_1.
Both controllers have almost identical code:
class VC_1: UIViewController
{
override func viewDidLoad()
{
super.viewDidLoad()
print(“VC_1 loaded")
}
override func viewDidAppear(_ animated: Bool){ print(“VC_1 appeared") }
override func viewDidDisappear(_ animated: Bool){ print(“VC_1 disappeared") }
#IBAction func btnShowVC_2(_ sender: UIButton)
{
let storyboard = UIStoryboard(name: "Main", bundle: nil)
secondVC = storyboard.instantiateViewController(identifier: “VC_2”)
secondVC.modalPresentationStyle = .fullScreen
show(secondVC, sender: self)
}
}
The difference is only in "VC_2" instead of "VC_1" in the 2nd controller code.
I have seen this View Controller creation code in Apple documentation and many other examples around the Internet.
When I press the button on the VC_1, I see in the debug window, that VC_2 is loaded and appeared, and VC_1 is disappeared. And same, of course, happens when I press the button on VC_2 - it disappears, and VC_1 is loaded again.
My questions are:
what happens with View Controller object after "viewDidDisappear" has been called? Does it really disappear from memory, or "disappear" only means "you cannot see it on the screen?". I do not see "viewDidUnload" in the documentation...
I suppose that "viewDidLoad" means that new View Controller object was created in memory. Is there any way to load the View Controller object only once, and then hide and show it without causing "viewDidLoad" to be called? I tried to do it with global variable "secondVC" but got "Application tried to present modally an active controller" error.
viewDidDisappear: called after the view is removed from the windows’
view hierarchy. No, View controller object just left the view property. By the way the amount of memory used by view controllers is negligible. So dont think about too much. If you want to catch when Your View controller object release from the memory put
deinit { print("vc deallocated") }
viewDidUnload, it has been deprecated since the iOS
6, which used to do some final cleaning.
Partly true. Keep in mind ViewDidload called one time for the life cycle of view controller. There is a method called before viewdidload but this is not related with your question.
In addition to "There is a method before viewdidload" -> loadView( ) is a method managed by the viewController. The viewController calls it when its current view is nil. loadView( ) basically takes a view (that you create) and sets it to the viewController’s view (superview).

How to detect tab bar index changes in swift?

I have 3 tabs in a Tab Bar Controller: Main, TabOne, TabTwo(tab index 0,1,2 respectively)
Main view controller has a table view will show all the elements in a an array. In view controllers TabOne and TabTwo, they all have buttons:
#IBAction func mypost(_ sender: Any) {
tabBarController?.selectedIndex = 0}
The array in Main view controller should be changed differently based on the tab index of the previous view controller. Ex.
If the previous view controller is TabTwo, then the table view will only show all the elements of even indices
BUT
If the previous view controller is TabOne, then the table view will only show all the elements of odd indices
How can I do it?
One possible solution is to have your main view controller set itself up as the delegate of the tab bar controller. Then implement the delegate method shouldSelect and look at the tab controller's currently selected index.
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool
let index = tabBarController.selectedIndex
if index == 1 {
// load data appropriate for coming from the 2nd tab
} else index == 2 {
// load data appropriate for coming from the 3rd tab
}
return true
}

Performing segue with UINavigationController (without IBAction)

It's easier to show you a drawing and then explain.
Dashboard Storyboard
I have 2 separate UIViewControllers (i've included just one in the drawing, the other is irrelevant) embedded in container view called ContainerViewController.
Post Storyboard
NewPostViewController shows a UIButton that presents TextPostViewController. As you can see, all of them are embedded in UINavigationControllers. Now, once the completion block of the new post is being called, I have to present the ContainerViewController and it needs to handle it's own logic. The problem is that it's embedded in UINavigationController and once I present it, the UITaBbar is hidden.
I tried to do this:
self.performSegue(withIdentifier: "TextPostToNavContainerVC", sender: nil)
The transition is successful but I'm losing the UITabBar, even though in the DashboardViewController and the ContainerViewController I called:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
tabBarController?.tabBar.isHidden = false
}
What am I doing wrong or is there are better way to do that?
You should instantiate the tab bar controller. not the view controller.
Imagine you're putting a initial view controller ahead of your tab bar controller. Making your tab bar not being pushed
If I undestand it correctly.
You are doing this
Segue connect to a view controller
But you should actually do this Segue connected to a tab bar controller
You can try to add it as a child to control it's frame like this
let textPost = self.storyboard?.instantiateViewController(withIdentifier: "containerID") as! TextPostToNavContainerVC
textPost.view.frame = CGRect(x:20,y:0,width:self.view.frame.width,height:self.view.frame.height-50)
self.view.addSubview(nvc.view)
self.addChildViewController(textPost)
textPost.didMove(toParentViewController: self)

Swift - Selected tab bar index not triggering scroll to top

I have a tab bar with five items, and I am trying to add a functionality to scroll to the top when the user taps the tab bar item again. Added the UITabBarControllerDelegate to the views where I want to trigger the event and also created a function to determine the selected tab bar index.
When I open the app, index 0 is auto-selected and works perfectly. The view auto scrolls to the top when I scroll down and tap the tab bar index. The problem occurs when I go to index 1 and trigger the scroll there. It somehow completely removes the auto-scroll from my first tab bar item.
Selecting other tab bar items without the auto scroll does not affect index 0 at all.
Home (index 0)
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
let tabBarIndex = tabBarController.selectedIndex
if tabBarIndex == 0 {
self.collectionView?.setContentOffset(CGPoint(x: 0, y: -10), animated: true)
}
}
Users (index 1)
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
let tabBarIndex = tabBarController.selectedIndex
if tabBarIndex == 1 {
self.tableView?.setContentOffset(CGPoint(x: 0, y: 0), animated: true)
}
}
Anything with a delegate property can only have one delegate assigned to it at any given time. What ever set the delegate most recently will receive the next delegate method call.
In your case you can probably reset the tab controller's delegate to self in each view controller's viewDidAppear method since you want the currently visible view controller to be the current tab controller delegate.
Add the following to each view controller that needs to be the tab controller's delegate:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.tabBarController?.delegate = self
}

How to call viewWillDisappear method in Tabbar Application having navigations also

I have created 5 Tabs in my application. In Tab1 i have UITableView. On didSelectRowAtIndexPath i am navigating to another UIView in which I am showing my all 5 Tabs. And I also play song in that navigated view.
Now when I click Back button in navigation and i again go to my original view, i am able to call viewWillDisappear (as expected and normal situation).
But when I click directly another tab then viewWillDisappear is not called in the navigated View. Why this Happens??
I have just thought in a way that when I directly clicks the another Tab then the view in Tab1 will call viewWillDisappear. But the navigated view will not call that method.
So what could be possible solutions?? kindly give some hints...
I got the viewWillDisappear to work by calling
self.definesPresentationContext = true
in viewDidLoad()
Otherwise, viewWillDisappear wasn't getting called. And this is what I have in it:
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
self.searchController.active = false
tableView.reloadData()
}
Hope this helps.
I think that you need to catch the event when you switch between tabs. When you switch from Tab1 to Tab2, as you expect, viewWillDisappear of Tab1 will not be called. Instead, the viewWillAppear of Tab2 will be called.
Else if you want to catch the event when you switch tabs, check this link.
That is because the you have created tabBarController and you are pushing it as a viewController from the mainView.
So whole TabBarController is treated as one viewController.
Hope this helps you.
if you want to call this method create the nsnotification center object viewWillDisappear
and when you want to call this method post this notification.
That's how I got it to work.
// UITabBarControllerDelegate
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
print("Selected view controller")
// do stuff...
self.navigationController?.setNavigationBarHidden(false, animated: false)
return true
}