Dissmiss from UITabBarController - ios

I guess it can be duplicated, but I looking everywhere and didn't find a solution for me.
So about my question. I have something like this
open this image to see more
In my AppDelegate I have func
func logIn() {
let userExist = UserDefaults.standard.string(forKey: "auth_key_user")
if userExist != nil && userExist != "" {
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let whereToGo = storyboard.instantiateViewController(withIdentifier: "AllPostsOfUserTabBarController") as! AllPostsOfUserTabBarController
window?.rootViewController = whereToGo
}
}
If user exist it lead me to first view controller inside tab bar controller. There I have navigation button with action to logout.
I need log out(send data to the server) and then go to my first view controller with text field and button where I can again log in.
How do I need to implement it?

Try this code after logout-
DispatchQueue.main.sync() {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let loginVC = storyboard.instantiateInitialViewController()
appDelegate.window?.rootViewController = loginVC
}
Hope it helps!

Put this code in function of your logout button. It will throw you back to your root controller.
func logoutBtnClicked()
{
DispatchQueue.main.sync()
{
(send your data to server)
self.navigationController?.popToRootViewController(animated: true)
}
}

Try this in appdelegate
While Login
self.window?.rootViewController = whereToGo
While Logout
func setupLoginViewController() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let loginVC: UIViewController? = storyboard.instantiateViewController(withIdentifier: "loginVCID") as? UIViewController
let navController: UINavigationController? = UINavigationController(rootViewController: loginVC!) as UINavigationController
window?.rootViewController = navController
}

Related

Push view controller not working in Swift

let storyboard = UIStoryboard(name: "Main", bundle: nil)
let playGameViewController = (storyboard.instantiateViewController(withIdentifier: "PlayGameViewController") as! PlayGameViewController)
self.navigationController?.pushViewController(playGameViewController, animated: true)
push not worked but I tried present working, I want navigate with push.
Try running the below code to know where you have gone wrong.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
guard let playGameViewController = storyboard.instantiateViewController(withIdentifier: "PlayGameViewController") as? PlayGameViewController else {
print("This means you haven't set your view controller identifier properly.")
return
}
guard let navigationController = navigationController else {
print("This means you current view controller doesn't have a navigation controller")
return
}
navigationController.pushViewController(playGameViewController, animated: true)
Try using breakpoints to figure out if any variable is nil. In your case, there is more probability for having your navigationController being nil.

Going from Navigation controller to tab controller views

I am developing the iOS app. and it has some design restrictions by the client. So it looks like I have to use navigation controller and the tab controller simultaneously on different number of Viewcontrollers.
I can categories my views into 2 category.
SignUp/Login views (this is entry point at first start of app)
MainView -> This become entry point once the user is login
Now Category 1 is using NavigationView Controllers. where as Category2 is using Tab bar controllers.
What I want case 1: I want when the user install my app, he is taken to Login view which has Navigation View. Now if he already has no account he will go to "Create new account" this is the 2nd scene of the Navigation view. Now on successful creation of account, he needs to close all other navigation view controllers and need to jump to MainView which will be Tabbar view controller.
case 2: suppose user close my app after getting login, when he open it up again, now the entery point will be Mainview which has Tab bar view controller. Now I know I need to do it in App delegate method but how?
I am doing this way,and it looks like working. But I am not getting bottom tab. Why is it so?
class ViewSwitcher {
static func updateRootViewController() {
let status = UserDefaults.standard.bool(forKey: KeyConstants.IS_USER_LOGGEDIN)
var rootViewController : UIViewController?
#if DEBUG
print(status)
#endif
if (status == true) {
let mainStoryBoard = UIStoryboard(name: "Main", bundle: nil)
let mainTabBarController = mainStoryBoard.instantiateViewController(withIdentifier: "idTab1VC") as! Tab1VC
rootViewController = mainTabBarController
} else {
let mainStoryBoard = UIStoryboard(name: "Main", bundle: nil)
let signInViewController = mainStoryBoard.instantiateViewController(withIdentifier: "idLoginVC") as! LoginVC
rootViewController = signInViewController
}
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController = rootViewController
}
}
Any Idea how to do these cases? How to navigate programmatically by closing all the view controllers trees?
The same process I have been using for my app.
Use this code to change the rootViewController in Case2
var window: UIWindow?
var tabBarController : UITabBarController?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Tabbar controller
let storyBoard = UIStoryboard.init(name: "Main", bundle: Bundle.main)
let tab1 = UINavigationController.init(rootViewController: storyBoard.instantiateViewController(withIdentifier: "tab1"))
tab1 = UITabBarItem.init(title: "Title 1", image: UIImage(named : "Image.png") , tag: 0)
let tab2 = UINavigationController.init(rootViewController: storyBoard.instantiateViewController(withIdentifier: "tab2"))
tab2 = UITabBarItem.init(title: "Title 2", image: UIImage(named : "Image.png") , tag: 1)
// Navigation controller or Login view controller
let nav1 = UINavigationController.init(rootViewController: storyBoard.instantiateViewController(withIdentifier: "nav1"))
tabBarController = UITabBarController.init()
tabBarController?.delegate = self
tabBarController?.selectedIndex = 0
tabBarController?.viewControllers = [tab1,tab2]
// The Bool value which you have to set as True after a user logged in
if UserDefaults.standard.bool(forKey: "LoggedIn"){
print("Tabbar")
self.window?.rootViewController = self.tabBarController
}else{
print("Navigation")
self.window?.rootViewController = nav1
}
return true
}
Before or After that, you have to write the same code where a user clicked on login in your loginViewController, in your case it is Case1
func loginClicked(){
UserDefaults.standard.set(true, forKey: "LoggedIn")
DispatchQueue.main.async {
let appdelegate = UIApplication.shared.delegate as! AppDelegate
// Same as above code and replace self with appDelegate without if condition and at last
appdelegate.window?.rootViewController = appdelegate.tabBarController
}
}
And after user logged out
DispatchQueue.main.async {
UserDefaults.standard.set(false, forKey: "LoggedIn")
let appdelegate = UIApplication.shared.delegate as! AppDelegate
let story = UIStoryboard.init(name: "Main", bundle: Bundle.main)
let nav1 = UINavigationController.init(rootViewController: storyBoard.instantiateViewController(withIdentifier: "nav1"))
appdelegate.window?.rootViewController = companyNavigation
}
Use below code in app delegate didFinishWithLaunchingOptions , for main view directly.. here uid means stored value whether to check user is Active or Not.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
if let user_id = UserDefaults.standard.value(forKey: "uid")
{
let homeViewController = storyboard.instantiateViewController(withIdentifier: "HomeTabBarCtrlr") as! UITabBarController
let nav = UINavigationController(rootViewController: homeViewController)
self.window!.rootViewController = nav
}else{
SignUp/Login views
}
Step by step approach to this would be:
Check if user is logged in or not. For this use UserDefaults.
If user not logged in, set LoginVC as rootVC.
On Login save a key in UserDefault to signify user logged in.
Next time when user opens the app check for key and if user logged in, set TabBarVC as rootViewController.
At Logout, clear the key.
Sample Code for the above will be:
AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
{
...........
//Check for user login and set rootViewController.
if UserDefaults.standard.bool(forKey: "login"){
self.window?.rootViewController = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController()
}
else{
self.window?.rootViewController = UIStoryboard(name: "Login", bundle: nil).instantiateInitialViewController()
}
return true
}
At Login Success
func loginUser(){
...
if userLoginSuccess{
UserDefaults.standard.set(true, forKey: "login")
}
}
At Logout
UserDefaults.standard.set(false, forKey: "login")
As like you know that you want to do it in Appdelgate.
When user login successfully you have to store a flag in userdefault.
In app delegate you have to check that if this flag is set or not.If set then create a navigation view controller with your initial view controller call moveToLoginWindow().
or if not then create tab bar view controller call movetoTabBarController().
func moveToLoginWindow() -> Void {
if let rootController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "LoginVC") as? LoginVC{
let navigationController = UINavigationController.init(rootViewController: rootController)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
}
}
func movetoTabBarController() -> Void {
let nav2 = UINavigationController()
let second = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "secondViewController") as? SecureNotesVC
nav2.viewControllers = [second!]
nav2.tabBarItem = UITabBarItem.init(title: "Titel 2", image: UIImage.init(named: "2.png"), selectedImage: UIImage.init(named: "2_sel.png"))
let nav3 = UINavigationController()
let third = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "thirdviewController") as? FormFillsVC
nav3.viewControllers = [third!]
nav3.tabBarItem = UITabBarItem.init(title: "Titel 2", image: UIImage.init(named: "3.png"), selectedImage: UIImage.init(named: "3_sel.png"))
let nav4 = UINavigationController()
let fourth = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "fourthviewContoller") as? SettingsVC
nav4.viewControllers = [fourth!]
nav4.tabBarItem = UITabBarItem.init(title: "4", image: UIImage.init(named: "4.png"), selectedImage: UIImage.init(named: "4_sel.png"))
if #available(iOS 11.0, *) {
nav2.navigationBar.prefersLargeTitles = true
nav3.navigationBar.prefersLargeTitles = true
nav4.navigationBar.prefersLargeTitles = true
}
let tabBarController = UITabBarController()
tabBarController.viewControllers = [ nav2,nav3, nav4,nav5]
self.window!.rootViewController = tabBarController;
}

Properly redirect to a view controller inside tab bar's navigation controller when push notification is clicked

I have a tabBar. Each of its tabs' ViewControllers have been embedded inside their respective NavigationControllers.
Hierarchy:
Tabbarcontroller --> NavigationController --> VC1 --> VC2 -->...VCn (for tab1)
I have to redirect to one of the viewcontrollers say VC2 when i click on push notifications. The code i am using is as follows.
let navigationController = UINavigationController()
let storyboard = UIStoryboard.init(name: "Main", bundle: Bundle.main)
if let chatViewController = storyboard.instantiateViewController(withIdentifier: "chatViewController") as? ChatViewController {
navigationController.viewControllers = [chatViewController]
window?.rootViewController = navigationController
}
By using this, I am redirected to the respective ViewController. However, i am not able to get back to the tabBar. So is it possible to redirect in such a way that it allows me the get back to the tab bar, thus providing the same hierarchy as in the UI?
EDIT:
The image below shows my layout.
I would like to accomplish the following:
When the user taps the push notification, the app should be directed to VC-B. *It should not create new object for VC-B and add to the navigation stack if VC-B is already on top of navigation stack.
If the app had been terminated and the user taps on the notification, it should open VC-B.
For determining if the app had been terminated, I set a flag as:
func applicationWillTerminate(_ application: UIApplication) {
UserDefaults.standard.set(true, forKey: UserDefaultsKey.isTerminated)
}
This flag is set false at the end of didFinishLaunchingWithOptions function.
For redirection, I check this flag to determine if the app had been terminated:
func performRedirectionToSuitableViewController(userInfo: [AnyHashable: Any]) {
let isTerminated = UserDefaults.standard.object(forKey: UserDefaultsKey.isTerminated) as! Bool
if isTerminated {
let storyboard = UIStoryboard.init(name: "Main", bundle: Bundle.main)
let tab = storyboard.instantiateViewController(withIdentifier: "tabBar") as! UITabBarController
tab.selectedIndex = 0
let nav = tab.viewControllers![0] as! UINavigationController
let chatViewController = storyboard.instantiateViewController(withIdentifier: "chatViewController") as! ChatViewController
chatViewController.chatThreadId = userInfo["thread_id"] as? String
nav.pushViewController(chatViewController, animated: true)
} else {
if let tab = window?.rootViewController?.presentedViewController as? UITabBarController {
let storyboard = UIStoryboard.init(name: "Main", bundle: Bundle.main)
let chatViewController = storyboard.instantiateViewController(withIdentifier: "chatViewController") as! ChatViewController
chatViewController.chatThreadId = userInfo["thread_id"] as? String
if let nav = tab.selectedViewController as? UINavigationController {
nav.pushViewController(chatViewController, animated: true)
}
}
}
}
With this code, my first requirement is partially fulfilled. Need to determine if the viewcontroller is at the top of the navigation stack.
And, in the case of terminated app, clicking the push notification opens the tab bar, with the default selection index being selected.
I have spent several days trying to fix this. But cannot get it work.
I suppose you have to do something like this:
let tabBar: UITabBarController // get your tab bar
tabBar.selectedIndex = 0 // eg. zero. To be sure that you are on correct tab
if let navigation = tabBar.viewControllers?[tabBar.selectedIndex] as? UINavigationController {
let storyboard = UIStoryboard.init(name: "Main", bundle: Bundle.main)
if let chatViewController = storyboard.instantiateViewController(withIdentifier: "chatViewController") as? ChatViewController {
navigation.pushViewController(chatViewController, animated: true)
}
}
Well, you are setting current window.rootViewController, which should be that TabBarViewController I'd say. That's why you cannot get back to the TabBar
What you should do is
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
if let chatViewController = storyboard.instantiateViewController(withIdentifier: "chatViewController") as? ChatViewController, let tabBar = storyboard.instantiateViewController(withIdentifier: "tabBar") as? UITabBarController {
let navigationController = UINavigationController(rootViewController: chatViewController)
navigationController.viewControllers = [chatViewController]
tabBar.viewControllers = [navigationController]
window?.rootViewController = tabBar
}
To get the current navigation controller from the tabbar controller. You can use this function
func getVisibleViewController(_ rootViewController: UIViewController?) -> UIViewController? {
var rootVC = rootViewController
if rootVC == nil {
rootVC = UIApplication.shared.keyWindow?.rootViewController
}
if rootVC?.presentedViewController == nil {
return rootVC
}
if let presented = rootVC?.presentedViewController {
if presented.isKind(of: UINavigationController.self) {
let navigationController = presented as! UINavigationController
return navigationController.viewControllers.last!
}
if presented.isKind(of: UITabBarController.self) {
let tabBarController = presented as! UITabBarController
return tabBarController.selectedViewController!
}
//UIAlertController
if presented.isKind(of: UIAlertController.self) {
let alertController = presented as! UIAlertController
return alertController.presentingViewController
}
return getVisibleViewController(presented)
}
return nil
}
Using the function you can navigate your viewcontroller like below.
let mainStoryboardIpad : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let yourView = mainStoryboardIpad.instantiateViewController(withIdentifier: "yourView") as! NewnoteViewController
let navi = self.getVisibleViewController(self.window!.rootViewController) as! UINavigationController
navi.pushViewController(yourView, animated: true)
You can create a method in your RootViewController that will redirect user to a specific view after receiving a push notification. Here's what I did in my previous project.
class RootViewController: UIViewController {
private var currentView: UIViewController
init() {
self.currentView = ViewController()
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
addChildViewController(currentView) // 1
currentView.view.frame = view.bounds // 2
view.addSubview(currentView.view) // 3
currentView.didMove(toParentViewController: self) // 4
}
func showDetailsScreen() {
if let updateDetailView = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "UpdateDetailsNotif") as? UpdateDetailsViewController{
let navController = UINavigationController(rootViewController: updateDetailView)
addChildViewController(navController)
navController.view.frame = view.bounds
view.addSubview(navController.view)
navController.didMove(toParentViewController: self)
currentView.willMove(toParentViewController: nil)
currentView.view.removeFromSuperview()
currentView.removeFromParentViewController()
currentView = navController
}
}
}
Then you can call that method on your AppDelegate like this:
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
let application = UIApplication.shared
AppDelegate.shared.rootViewController.showDetailsScreen()
completionHandler()
}
}

At startup, open the specified ViewController

I have two VC (ViewController, SettingsVC, VC2).
How can I make it so that when you turn on the Switch(located in SettingsVC) when the application starts it would be shown VC2??
The default is ViewController.
Write this code in didFinishLaunchingWithOptions -
let isSwitchOn = UserDefaults.standard.bool(forKey: "isSwitchOn")
if isSwitchOn {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc2 = storyboard.instantiateViewController(withIdentifier: "Your identifier")
self.window!.rootViewController = vc2;
}
Set isSwitchOn in SettingsVC like this -
UserDefaults.standard.set(true, forKey: "isSwitchOn")

How can I change the rootViewController to a VC that has not been loaded yet?

Imagine an application like Twitter in which once you log in, you never see the sign in screen again until you log out. I have done this by changing the initial viewController in the appDelegate on launch. However, when going to sign out, there is now nothing below the new VC in the stack to dismiss to. How can I properly make the sign in screen the rootViewController after the initial view has loaded so that I can dismiss on logout?
In appDelegate I have changed the initialVC like so:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewController = storyboard.instantiateViewController(withIdentifier: "RootVC")
self.window?.rootViewController = initialViewController
And then I would like to basically insert the signInVC beneath my initial VC so that I can "dismiss" to it.
Take a bool value called signup_status and store it in UserDefaults.standard and set it as false
And change this value to true when user is logged in and and make is false when he is log out
Now in AppDelegate check for user is login or not
if UserDefaults.standard.bool(forKey: "signup_status"){
//if true the dont show login page and make home screen root view controller
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let viewController = storyBoard.instantiateViewController(withIdentifier: "homepage") as! homepageViewController
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController = viewController
}else {
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let viewController = storyBoard.instantiateViewController(withIdentifier: "login") as! login
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController = viewController
}
now in logout
set the value of UserDefaults.standard for key signup_status false
UserDefaults.standard.set(false, forKey: "signup_status")
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let viewController = storyBoard.instantiateViewController(withIdentifier: "login") as! login
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController = viewController

Resources