Present modally when tab on tabbar - ios

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.

Related

Push View Controller by overriding shouldSelect method in TabbarViewController

I want to push a ViewController when an item in TabBar is selected I have written following code but it is not working
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
if let ind = tabBarController.viewControllers!.index(of:viewController) , bottomBarTabsArray[ind].viewType == BottomBarInfo.VIEWTYPE_ASSISTANT { //
let tabVC = AppStrings.appStoryBoard.instantiateViewController(withIdentifier: "assitantViewControllerID") as! AssitantViewController
self.selectedViewController?.navigationController?.pushViewController(tabVC, animated: true)
return false
}
return true
}
how to push ViewController when tabbar item is selected instead of switching to ViewController
Replace
self.selectedViewController?.navigationController?.pushViewController(tabVC, animated: true)
with
(self.selectedViewController as! UINavigationController ).pushViewController(tabVC, animated: true)

Trigger image picker on UITabBarController button click

I have made a tabbar in storyboard with 4 bar items, I connected all of them to other view controllers by rightclick dragging and setting viewcontroller segue. Now for the second button i want to show an imagepicker instead of a viewcontroller. When i delete the second segue from storyboard in UITabBarController, my 4th bar item disappears.
This is my tabview controller
class BaseTabBarController: UITabBarController, UITabBarControllerDelegate {
let arrayOfImageNameForUnselectedState = ["home", "explore", "addIcon", "notification", "accountIcon"]
let arrayOfImageNameForSelectedState = ["homeFilled", "exploreFilled", "addIcon", "notificaitonFilled", "accountIcon"]
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
if let count = self.tabBar.items?.count {
for i in 0..<count {
let imageNameForSelectedState = arrayOfImageNameForSelectedState[i]
let imageNameForUnselectedState = arrayOfImageNameForUnselectedState[i]
self.tabBar.items?[i].selectedImage = UIImage(named: imageNameForSelectedState)?.withRenderingMode(.alwaysOriginal)
self.tabBar.items?[i].image = UIImage(named: imageNameForUnselectedState)?.withRenderingMode(.alwaysOriginal)
}
}
}
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
if tabBar.items?.index(of: item) ?? 0 == 2 {
//Clicked add tab, cancel segue and show imagepicker
} else {
selectedTabindex = tabBar.items?.index(of: item) ?? 0
}
}
}
How do I show image picker on 2nd bar button click
Do not Delete Second segue from storyboard put dummy view controller to show tab button inside tabbar. Implement UITabBarController controller's delegate method in subclass of UITabBarController and return false in shouldSelect method for second viewcontroller and present ImagePicker View for there.
Code:
class BaseTabBarController : UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
}
override var prefersStatusBarHidden: Bool {
return false
}
}
extension BaseTabBarController : UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
if self.viewControllers?.index(of:viewController) == 1 {
// TO Do code for Image Picker and Present it
return false
} else {
return true
}
}
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
}
}

swift4: how to check if user logged befor tab bar item clicked

This question has been asked before and has been answered, my question is not unique but there must be something missing. I'm simply trying to check if user logged in to app before as his data stored in UserDefaults but it doesn't work for me, this is the class of my TabBarViewController
class TabViewController: UITabBarController, UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
}
// UITabBarDelegate
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
print("Selected item")
}
// UITabBarControllerDelegate
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
if(viewController is MessagesViewController) {
print("trueee")
if(UserStorage.id == "") {
Toast.toast(messsage: "not loggoed user", view: self.view)
}
} else {
print("faaaaaaaalse")
}
print("Selected view controller")
}
}
i want to check if user open MessageViewController then if user is logged in to print something but it always print faaaaaaaalse
note: Toast.toast() is a function i created to show toast
and UserStorage.id returns user id stored in USerDefaults
this is image which shows my structure:
what should I do ?
Just your tab bar’s root controllers are 2 navigaton controllers, not MessageViewController. Firstly with tabBarController you have to find navigationController which contains your MessageViewController than in this navigation find the needed ViewController.
So I have the solution for you:
import UIKit
class TabbarController: UITabBarController, UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
// for tab bar initialization
if let viewControllers = self.viewControllers,
viewControllers.count >= 1,
// the index of viewController is 0 here, but if your tab bar's started controller is not 0 you can set yours
let navigationController = viewControllers[0] as? UINavigationController {
for controller in navigationController.viewControllers {
if let messagesViewController = controller as? MessagesViewController {
doWithMessagesViewControllerWhatYouWant(_viewController: messagesViewController)
}
}
}
}
// UITabBarControllerDelegate
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
if let navigationController = viewController as? UINavigationController{
for controller in navigationController.viewControllers {
if let messagesViewController = controller as? MessagesViewController {
doWithMessagesViewControllerWhatYouWant(_viewController: messagesViewController)
}
}
}
}
private func doWithMessagesViewControllerWhatYouWant(_viewController: MessagesViewController) {
print("do some operations with messagesViewController")
if(UserStorage.id == "") {
Toast.toast(messsage: "not loggoed user", view: self.view)
}
}
}

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!!

UITabBarControllerDelegate on two separate UIViewControllers - Swift

I have the following code that i'm trying to achieve a "scroll to top" on a UIViewcontroller and "scroll to beginning" on another UIViewController.
The first VC has a table view and the second VC has a collection view.
-NearMeViewController-
override func viewDidLoad() {
super.viewDidLoad()
self.tabBarController?.delegate = self
//....
}
extension NearMeViewController: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
let tabBarIndex = tabBarController.selectedIndex
if tabBarIndex == 0 {
self.nearMeTable.setContentOffset(CGPoint.zero, animated: true)
}
}
}
-MapSearchViewController-
override func viewDidLoad() {
super.viewDidLoad()
self.tabBarController?.delegate = self
//....
}
extension MapSearchViewController: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
let tabBarIndex = tabBarController.selectedIndex
if tabBarIndex == 2 && requests.mapEventData.count > 0 {
self.resultsCollectionView?.scrollToItem(at: IndexPath(row: 0, section: 0),
at: .left,
animated: true)
}
}
}
They are both connected to the same UITabBar of course.
The issue is that when i open the app, and i press the tabBarIndex 0 it scrolls to top of the table view.
Then i change VC and i try to press the tabBarIndex 2, so it takes me to the first item of the collection view.
Afterwards I go back to the first VC and when i try to press the tabBarIndex 0, the tableview is not scrolling to top (as it was doing when i first opened the app).
But the second VC with the collection view is working fine.
Any idea why this might happen?
viewDidLoad is not being called each time the VC appears but only once (or when it was deallocated before)
the delegate of vc 0 is set only once... and then vc1 is loaded and set as delegate... but then not vc0 again.
to make it work, set the delegate in viewWillAppear.
note
changing the delegate over and over for this is weird... and checking the index is fragile at best go with DonMag's solution.
It looks like you are checking the tabBarController's .selectedIndex ... but that is the index of the currently showing tab, not the tab you are "on your way to."
More likely, you want to use code similar to this:
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
if let vc = viewController as? MapSearchViewController {
// user tapped Map Search tab item
}
if let vc = viewController as? NearMeViewController {
// user tapped Near Me tab item
}
}
Edit: Here is a simple example, using a subClassed UITabBarController.
Set up your storyboard as normal, creating View Controllers and connecting them as "Tabs", then set the Custom Class of the TabBarController to MyTabBarController. This sample code will (hopefully) be easy to follow.
A runnable example is at: https://github.com/DonMag/SWTabBarSubclass
//
// TabBarSample.swift
//
import UIKit
class FirstViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// all your normal setup stuff....
}
func firstViewSpecific(_ aValue: Int) -> Void {
print("Passed value is:", aValue)
// do something with the passed value
}
}
class NearMeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// all your normal setup stuff....
}
func resetMe() -> Void {
print("resetMe() called in NearMeVC")
// do whatever you want, such as
self.nearMeTable.setContentOffset(CGPoint.zero, animated: true)
}
}
class MapSearchViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// all your normal setup stuff....
}
func resetMe() -> Void {
print("resetMe() called in MapSearchVC")
// do whatever you want, such as
if requests.mapEventData.count > 0 {
self.resultsCollectionView?.scrollToItem(at: IndexPath(row: 0, section: 0),
at: .left,
animated: true)
}
}
}
class MyTabBarController: UITabBarController, UITabBarControllerDelegate {
var myValue = 0
override func viewDidLoad() {
super.viewDidLoad()
// make self the UITabBarControllerDelegate
self.delegate = self
}
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
if let vc = viewController as? FirstViewController {
myValue += 1
vc.firstViewSpecific(myValue)
return
}
if let vc = viewController as? NearMeViewController {
vc.resetMe()
return
}
if let vc = viewController as? MapSearchViewController {
vc.resetMe()
return
}
}
}
might not apply to all scenarios, check if your VC has been set to
self.tabBarController?.delegate = self
within your ViewDidLoad()

Resources