Present a View Controller modally from a tab bar controller - ios

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

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)

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.

Programatically load a navigationController on tabitem click

I have made a UITabBarController where when selecting one of the item I load an imagepicker.Now after user chose an image from picker i want to load a navigation controller, which I'm not able to. My code is
extension BaseTabBarController : UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
if self.viewControllers?.index(of:viewController) == 2 {
// Presenting image picker here
present(self.imagePicker, animated: true, completion: nil)
return false
} else {
return true
}
}
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
}
}
here's the code after user has picked image
extension BaseTabBarController : ImagePickerDelegate {
func doneButtonDidPress(_ imagePicker: ImagePickerController, images: [UIImage]) {
//This is the StoryboardID of Navigationcontroller i want to goto
let detailVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "UploadNavigationController")
//This is not working
present(detailVC, animated: true,completion: nil)
}
}
Here controller is not shown and I get this warning
Warning: Attempt to present <UINavigationController: 0x7fb4969c4800> on <BaseTabBarController: 0x7fb49780ea00> whose view is not in the window hierarchy!
I want to show the navigation controller with the tab bar still visible in the bottom
I guess, as you want to show the navigation controller with the tab bar still visible in the bottom, then you should first dismiss image picker then push new controller not present.
Because if you present any controller will cover whole screen as it is presenting from root controller. If you push then it will push from current controller and tabor remains as it was.
extension BaseTabBarController : ImagePickerDelegate {
func doneButtonDidPress(_ imagePicker: ImagePickerController, images: [UIImage]) {
self.imagePicker.dismiss(animated: true) {
//This is the StoryboardID of Navigationcontroller i want to goto
let detailVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "UploadNavigationController")
//This is not working
let nav = UINavigationController(rootViewController: detailVC)
navigationController?.pushViewController(nav, animated: true)
//present(detailVC, animated: true,completion: nil)
}
}

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)
}
}
}

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)
}
}
}
}
}

Resources