How to set viewcontroller of UITabbarItem to default viewcontroller when switching tabs? - ios

I have a UITabBarController. It has 4 tabBarItem.
Suppose I'm at 1st tabBarItem defaultViewController(1) and I
went to another ViewController(2) which is shown after some
actions in first defaultViewController(1).
Then I switched to 2nd tabBarItem defaultViewcontroller(2).
Again I switched back to 1st tabBarItem, it shows ViewController(2).
I want to show defaultViewController(1). How can I achieve this using swift 4.
defaultViewController(1) and defaultViewController(2) are the default ViewController for 1st and 2nd TabBarItem respectively.***

Let's say you have UINavigationController inside each tab of UITabBarController, and defaultViewController(1) is the rootViewController of your first tab, inside that there is a button that navigate to ViewController(2).
For this first of all, let's create generic solution. Create UIApplication Extension like this,
extension UIApplication {
class func topViewController(controller: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
if let navigationController = controller as? UINavigationController {
return topViewController(controller: navigationController.visibleViewController)
}
return controller
}
}
Implement UITabBarControllerDelegate in AppDelegate and do below code,
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
if tabBarController.selectedIndex == 0 {
UIApplication.topViewController()?.navigationController?.popToRootViewController(animated: false)
}
}
In above code, I have taken tabBarController.selectedIndex to 0, you can make it different depending on your requirement.
Let me know in case of any queries.

Extend a subclass of UITabbarController and use it as your tabbar's class. In that implement UITabBarControllerDelegate, didSelect and use popToRootViewController to pop to your defaultViewController.
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController){
if viewController is UINavigationController {
//when you have `UINavigationController`
let rootNavigationController = viewController as! UINavigationController
rootNavigationController.popToRootViewController(animated: false)
} else {
//when you don't have `UINavigationController` then dismiss all viewcontroller that was presented.
let rootViewController = viewController
if rootViewController.presentingViewController != nil {
rootViewController.dismiss(animated: false, completion: nil)
}
}
}
Note: add self.delegate = self to conform the protocol inside viewDidLoad method.

Related

Present modally when tab on tabbar

I am trying to add a new tabbar item in my tabbar, and when tapping the item, the viewcontroller connected to it, should be displayed fullscreen and as a modal.
I have connected the desired VC to the tabbar as a root (in order for it to be displayed in the tabbar).
I have tried the following code, but the VC is displayed as normal behaviour for a tabbar viewcontroller.
How can I make it show modally fullscreen instead?
class tabBarViewController: UITabBarController, UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.tabBarController?.delegate = self
}
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
let isModalView = viewController is dreamteamViewController
if isModalView {
// you can refactor this part of the code
let cameraController = UINavigationController(rootViewController: dreamteamViewController())
self.present(cameraController, animated: true, completion: nil)
return false
} else {
return true
}
}
Change your viewDidLoad like this to set the delegate callback to correct instance:
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
}
Define and storyboard identifier for your desired viewcontroller in the story board
Change your shouldSelect method like below:
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool
{
let isModalView = viewController is DesiredViewController
if isModalView {
// you can refactor this part of the code
let mainStory = UIStoryboard(
name: "Main", bundle: nil
)
let desiredVC = mainStory.instantiateViewController(
withIdentifier:"YourIdentifier"
) as UIViewController
let navBar = UINavigationController.init(rootViewController: desiredVC)
self.present(navBar, animated: true, completion: nil)
return false
} else {
return true
}
}
I posted a project in my github that present one of the tabbar item modally. Please check it from https://github.com/Hbehboodi/Navigation-TabbarWithModalTabItem if any thing is not clear.

Present a View Controller modally from a tab bar controller

I want to build a view with a camera. Something just like Instagram where there is a button in the middle that the user can click and the camera view shows up.
I implemented a code for the TabViewController in the AppDelegate but nothing happens, no animation or Presentation of the new ViewController.
Here is my AppDelegate:
import UIKit
class AppDelegate: UIResponder, UIApplicationDelegate, UITabBarControllerDelegate {
var window: UIWindow?
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: ViewController) -> Bool {
if viewController is ViewController {
let storyboard = UIStoryboard(name: "Main.storyboard", bundle: nil)
if let controller = storyboard.instantiateViewController(withIdentifier: "cameraVC") as? ViewController {
controller.modalPresentationStyle = .fullScreen
tabBarController.present(controller, animated: true, completion: nil)
}
return false
}
return true
}
Here is my Storyboard:
Any ideas?
I suggest to create a custom class for your TabBarController, then assign the delegate to that.
You can either assign and check the restorationIdentifier of the view controller, or do a type check. I usually use storyboard identifier as the restoration identifier of the view controller(s).
class TabBarController: UITabBarController, UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
}
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
if let identifier = viewController.restorationIdentifier, identifier == "cameraVC" {
let vc = self.storyboard?.instantiateViewController(withIdentifier: "cameraVC") as! CameraViewController
present(vc, animated: true, completion: nil)
return false
}
return true
}
}
Here's a sample you can play with:
https://gist.github.com/emrekyv/3343aa40c24d7e54244dc09ba0cd95df
I just tried and It worked perfectly for me:
Create a Custom class for your TabBarController and assign it to your Controller in Storyboard.
After that override didSelect of tabBarController and write your presentation code there :
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
if let controller = self.viewControllers?[self.selectedIndex] as? ViewController {
controller.modalPresentationStyle = .fullScreen
self.present(controller, animated: true, completion: nil
}
}
Hope it helps!!

Present a View modally from a tab bar controller

How can I present a View modally from a tab bar controller so that the view goes over the actual one?
I want to build a view with a camera. Something just like "WhatsApp" or "Instagram" where there is a button in the middle that the user can click and the camera view shows up.
Additionally, the user should move the tab he was before when the close button was clicked.
This is how my ViewController is connected to the TabBarController:
I've had to implement something similar in an app I'm currently building, it's relatively straightforward to do, you need to implement a delegate method of UITabBarController in order to achieve this.
The delegate method you need to implement is:
tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool
Returning false from this method will stop the tab controller from selecting your tab, you then just need to implement your own logic to present the UIViewController programatically.
Here's an example:
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
// If your view controller is emedded in a UINavigationController you will need to check if it's a UINavigationController and check that the root view controller is your desired controller (or subclass the navigation controller)
if viewController is YourViewControllerClass {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
if let controller = storyboard.instantiateViewController(withIdentifier: "storyboardID") as? YourViewControllerClass {
controller.modalPresentationStyle = .fullScreen
self.present(controller, animated: true, completion: nil)
}
return false
}
// Tells the tab bar to select other view controller as normal
return true
}
I've not tested the above code as my implementation is slightly different and has more variables. The general principle is the same.
Let me know how you get on and I'll update the answer if necessary.
Assuming that you are conforming to UITabBarControllerDelegate, you could implement:
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
// here, you should edit "0" to be matched with your selected item
// for instance, if there is 5 items and the desired item is in the middle, the compared value should be "2"
if tabBarController.selectedIndex == 0 {
// simply, you will need to get the desired view controller and persent it:
let desiredStoryboard = UIStoryboard(name: "Main", bundle: nil)
let desiredViewController = desiredStoryboard.instantiateViewController(withIdentifier: "storyboard id")
present(desiredViewController, animated: true, completion: nil)
}
}
let modalVC = self.storyboard?.instantiateViewController(withIdentifier: "ViewControllerIdentifier")
modalVC.modalTransitionStyle = .crossDissolve
modalVC.modalPresentationStyle = .full or .overfullscreen // please check which of the options work
self.present(modalVC, animated: true, completion: {
})

How to reset tab (or tab controller) of UITabBarController when user select tab?

Swift: I have UITabBarController, with 8 tabs. When user select any tab including more tab, I want reset content of selected tab by Popping to rootView controller?
How could this be done?
I tried to reset navigation controllers in below methods, It works for tabs which are visible at bottom but it doesn't work for More tab.
tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController)
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem)
A clean way to do this:
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
let index = tabBarController.selectedIndex
if index == NSNotFound || index > 4 {
tabBarController.moreNavigationController.popToRootViewController(animated: false)
return
}
let navController = tabBarController.viewControllers?[tabBarController.selectedIndex] as? UINavigationController
navController?.popToRootViewController(animated: false)
}
Create a custom class for your UITabViewContoller, set a delegate and put there that piece of code.
You can create a UITabBarController class for your tabBar and in that class ovveride the tabBar didSelect method from their on select of any tab you can popViewController
class TabBarViewController: UITabBarController {
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
let navigationController1 = self.viewControllers![0] as? UINavigationController
navigationController1!.popViewController(animated: false)
let navigationController2 = self.viewControllers![1] as? UINavigationController
navigationController2!.popToRootViewController(animated: false)
let navigationController3 = self.viewControllers![2] as? UINavigationController
navigationController3!.popToRootViewController(animated: false)
let navigationController4 = self.viewControllers![3] as? UINavigationController
navigationController4!.popToRootViewController(animated: false)
}
}
Use viewControllers property of UITabbarController. viewControlers is an array of UIViewController for UITabbarController. Exchange/Move position of view controllers upon tabbar(controller) selection.
Use tabbar(controller)'s delegate method to hanlde these operations, like:
Swift 3
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
if var viewcontrollers = tabBarController.viewControllers {
viewcontrollers.remove(at: tabBarController.selectedIndex)
viewcontrollers.insert(viewController, at: 0)
tabBarController.viewControllers = viewControllers
}
}
You can also use this delegate method, if you have an access to tabbarController where you have implemented.
func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
// Write a similar code to exchange/move view controllers using tabbarController instance.
}
This worked for me.
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
guard let navigationController = tabBarController.viewControllers?[tabBarController.selectedIndex] as? UINavigationController else {
return
}
navigationController.popToRootViewController(animated: false)
}
To fully support More, create a custom class like below:
class Main_TabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
self.customizableViewControllers = [] //remove Edit from More (OPTIONAL)
}
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
if let index = tabBar.items?.firstIndex(of: item) {
if let count = viewControllers?.count, count > 5, index == 4 {
DispatchQueue.main.async {
self.moreNavigationController.popToRootViewController(animated: false)
}
} else if let vcs = viewControllers, let vc = vcs[index] as? UINavigationController {
DispatchQueue.main.async {
vc.popToRootViewController(animated: false)
}
}
}
}
}

How to get the ViewController from UITabBarItem

When a user taps on an item, I want to get the ViewController associated with that tab.
The TabBar delegate no longer provides the ViewController delegate. Instead, it provides a didSelectItem delegate.
override func tabBar(tabBar: UITabBar, didSelectItem item: UITabBarItem) {
}
How do I get the ViewController from UITabBarItem?
If you're using a UITabBarController and not just a UITabBar look into using UITabBarControllerDelegate rather than UITabBarDelegate. UITabBarControllerDelegate provides the method:
func tabBarController(_ tabBarController: UITabBarController,
didSelectViewController viewController: UIViewController)
This is a swift 4 Extension version that works for me:
import UIKit
extension UIApplication {
var visibleViewController: UIViewController? {
guard let rootViewController = keyWindow?.rootViewController else {
return nil
}
return getVisibleViewController(rootViewController)
}
private func getVisibleViewController(_ rootViewController: UIViewController) -> UIViewController? {
if let presentedViewController = rootViewController.presentedViewController {
return getVisibleViewController(presentedViewController)
}
if let navigationController = rootViewController as? UINavigationController {
return navigationController.visibleViewController
}
if let tabBarController = rootViewController as? UITabBarController {
// Uncomment the line bellow the TabBarController
//return tabBarController.selectedViewController
// uncomment the line bellow to get the visible ViewController of the TabBarController
return getVisibleViewController(tabBarController.selectedViewController!)
}
return rootViewController
}
}
This can be called as easy as this:
let visibleVC = UIApplication.shared.visibleViewController
I hope this work for you as well ;)

Resources