Checking if ViewController is already in background or not - ios

I have an app which starts with different VCs depending whether the user is already logged in or not.
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
window = UIWindow.init(frame: UIScreen.mainScreen().bounds)
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let initialViewController: UIViewController
if DataManager.getInstance().getUserInfo() == nil {
initialViewController = storyboard.instantiateViewControllerWithIdentifier("authenticationViewController")
} else {
initialViewController = storyboard.instantiateViewControllerWithIdentifier("locationsNavigationViewController")
}
window!.rootViewController = initialViewController;
window!.makeKeyAndVisible();
return true
}
If the user is not logged in, the app starts with AuthenticationViewController, otherwise it starts with LocationsNavigationViewController, which is a NavigationViewController
In the latter VC, the is a button for logout. The problem is when the user taps on that button, I don't know if I have to dismiss the LocationsNavigationViewController (because AuthenticationViewController is in background) or if I have to dismiss LocationsNavigationViewController and perform a segue for opening the AuthenticationViewController.
So far, I have just covered the first use case. So in LocationsNavigationViewController I call this function
func showAuthentication() {
dismissViewControllerAnimated(true, completion: nil)
}
But when the app starts with LocationsNavigationViewController dismiss the VC is not enough of course, because the the AuthenticationViewController has never been instantiated.
How can I solve this please?

self.navigationController?.viewControllers
This is an array which will contain all your previous view controller. You can enumerate it and check whether your view controller is exist or not.

If you are using UINavigationController then you can check any UIViewController present or not!
let rootViewController = application.windows[0].rootViewController as! UINavigationController
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
if !rootViewController.viewControllers.contains(UIViewController_Class()){
let notificationVC = mainStoryboard.instantiateViewControllerWithIdentifier(constInstance.notificationsIdentifier) as! UIViewController_Class_Name
rootViewController.pushViewController(notificationVC, animated: false)
}
Hope this helps!

Thanks to Sohil's answer, I change the showAuthentication function in this way
func showAuthentication() {
//Since the app can start with different VC, I have to check which is the window root VC
if UIApplication.sharedApplication().windows[0].rootViewController is AuthenticationViewController {
dismissViewControllerAnimated(true, completion: nil)
} else {
performSegueWithIdentifier("authenticationSegue", sender: self)
}
}
And added a segue from the NavigationViewController to the AuthenticationViewController, called authenticationSegue
Pretty simple solution.

You can create one function in appdelegate for logout
and in logout function chanege rootviewcontroller of window like
func logoutUser()
{
var login: UIViewController?
login = LoginViewController(nibName : "LoginViewController", bundle : nil)
let nav = UINavigationController(rootViewController: login!)
self.window?.rootViewController = nav
}

Related

Why does pushViewController doesn't work after navigating through Dynamic Link?

I'm building an app that can receive Firebase's Dynamic Link and redirects it into a certain UIViewController after clicking the link. So based on this question that I asked, I have the code in AppDelegate.swift that navigates the app to the UIViewController like this:
func goToDynamicLinkVC() { // This function is in AppDelegate.swift
let destinationStoryboard: UIStoryboard = UIStoryboard(name: "DynamicLink", bundle: nil)
let destinationVC = destinationStoryboard.instantiateViewController(withIdentifier: "DynamicLinkView") as? DynamicLinkVC
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = destinationVC
self.window?.makeKeyAndVisible()
}
And in that UIViewController I also have a function that moves to another UIViewController if a user pressed a button like this:
func routeToDynamicForm() { // This function is in DynamicLinkVC class
let destinationVC = UIStoryboard(name: "DynamicLink", bundle: nil).instantiateViewController(withIdentifier: "DynamicFormView") as! DynamicFormVC
self.navigationController?.pushViewController(destinationVC, animated: true)
}
But weirdly enough now that I pressed the button it doesn't move me to the next View Controller (in this case, DynamicFormVC). I've tried to debug the button whether it's working or not by doing this:
print("button tapped")
It shows the message on the debug area, but it still doesn't redirects to the next view controller. So what can I do to resolve this? If you need more information feel free to ask and I will provide it to you. Thank you.
I changed the goToDynamicLinkVC function to something like this:
func goToDynamicLinkVC() { // This function is in AppDelegate.swift
let destinationStoryboard: UIStoryboard = UIStoryboard(name: "DynamicLink", bundle: nil)
let destinationVC = destinationStoryboard.instantiateViewController(withIdentifier: "DynamicLinkView")
let navController = UINavigationController()
navController.setViewControllers([destinationVC], animated: true)
self.window?.rootViewController = navController
}

AddingViewcontroller manually into the stack swift 3

I am coding the sign out button in my app.This is the flow of my app.
SplashvwController -> secretCodeViewController ->
LoginviewController -> DashboardViewController
in this DashboardViewController I have the signOut button.
My app has single sign in facility once user logged, next time when he opens the app,
SplashvwController -> DashboardViewController
I want to prompt the user to Loginviewcontroller whenever he clicks the sign out button.
Question
When user going through path 1 I can simply do popviewcontroller to go back to the previous viewcontroller. But when user go though the 2nd path,
how can I add the Loginviewcontroller manually into my
viewcontrollers stack to perform the same operation?
How can I check whether the LoginviewController exists in my current Viewcontrollers stack?
Please help me
I think, the below code helps you,
for (var i = 0; i < self.navigationController?.viewControllers.count; i++) {
if(self.navigationController?.viewControllers[i].isKindOfClass(Loginviewcontroller) == true) {
self.navigationController?.popToViewController(self.navigationController!.viewControllers[i], animated: true)
break;
}
}
To add a ViewController Manually , check the code below..
let navigation = mainStoryboard.instantiateViewController(withIdentifier: "LoginViewController") as! LoginViewController
let nav = UINavigationController(rootViewController: navigation)
appdelegate.window!.rootViewController = nav
Set the storyboard reference identifier for a LoginViewController on the Main.Storyboard file.
Whenever you want to show the LoginViewController just call the function
func launchLoginView() {
if let loginVC: LoginViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("LoginStoryboardID") as? LoginViewController {
// .instantiatViewControllerWithIdentifier() returns AnyObject! this must be downcast to utilize it
// LoginStoryboardID is the reference id for login view controller.
self.presentViewController(loginVC, animated: true, completion: nil).
// OR
//UIApplication.shared.keyWindow?.rootViewController = loginVC
}
}
Landing screen based on the User login status.
In AppDelegte.swift
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let isLoggedAlready = //Get the login status
if isLoggedAlready == true {
if let dashBoardVC: DashboardViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("DashboardStoryboardID") as? DashboardViewController {
//Already logged in then directly launch dashboard vc.
//change the code based on your needs
window?.rootViewController = dasbBoardVC
}
}
// Otherwise let it go as flow 1
return true
}
}

revealViewController() always returns nil

I'm having some troubles with revealViewController in Xcode 7.2 and iOS 9.2.
My app starts with a view controller embedded in a navigation controller to perform a login.
After login, or if the login token is present, I jump to another view controller embedded in a navigation controller with the following code:
let homePage = self.storyboard?.instantiateViewControllerWithIdentifier("HomeViewController") as! HomeViewController
let homePageNav = UINavigationController(rootViewController: homePage)
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.window?.rootViewController = homePageNav
In this home view controller I would like to have a left navigation menu with SWRealViewController.
I had the SWRealViewController view linked with sw_front to my home navigation controller, and the following code:
if (self.revealViewController() != nil) {
self.menuButton.target = self.revealViewController()
self.menuButton.action = "revealToggle:"
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
But self.revealViewController() always returns nil, so it does not work.
I think I lost the revealViewController somewhere (maybe when I jump from the first navigation controller to the second) but I do not know what to do.
The most convenient to be a reason for the revealViewController to be nil
is you didn't connect segues correctly in stroyboard.
See this tutorial it's quite easy to follow.
Update
If in your case you just need to open a login vc if the user is not logged in you may do like this:
in AppDelegate
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
var rootVCStoryboardId = userIsLoggedin ? "SWRevealViewController" : "LoginViewController"
self.window?.rootViewController = UIStoryboard(name: Storyboards.main, bundle: NSBundle.mainBundle()).instantiateViewControllerWithIdentifier(rootVCStoryboardId)
Where SWRevealViewController is the stroyboard id for SWRevealViewController and LoginViewController is the storyboard id for your login view controller(or its navigation controller if exists).
In case someone is wondering how to do a manual segue, this is what worked for me at the end.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let sw = storyboard.instantiateViewControllerWithIdentifier("SWRevealViewController") as! SWRevealViewController
self.view.window?.rootViewController = sw
let destinationController = self.storyboard?.instantiateViewControllerWithIdentifier("StoryboardID") as! NameOfViewController
let navigationController = UINavigationController(rootViewController: destinationController)
sw.pushFrontViewController(navigationController, animated: true)
Incase you are skipping login scene based on the current user information, then make sure to instantiate the storyboard with SWRevealViewController. See below code for reference:
if User.currentUser != nil {
//There is a current user
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("SWRevealViewController")
window?.rootViewController = vc
}
else{
//No current user
}

How can you create an instructional screen, that only appears the first time (in Swift)

I there a simple way to route a user to a view controller only one time? This ViewController could then be an instructional screen that the user only sees once at startup.
I have this now:
let skipScreen = NSUserDefaults.standardUserDefaults()
#IBAction func skipButton(sender: AnyObject) {
let skipNow = true
skipScreen.setObject(skipNow, forKey: "skip")
skipScreen.synchronize()
}
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject :AnyObject]?) -> Bool {
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
var storyboard = UIStoryboard(name: "Main", bundle: nil)
var initialViewController = storyboard.instantiateViewControllerWithIdentifier("ViewController") as UIViewController
self.window?.rootViewController = initialViewController
self.window?.makeKeyAndVisible()
}
override func viewDidLoad() {
super.viewDidLoad()
var skipNow: Bool? = skipScreen.objectForKey("skip") as Bool?
if skipScreen.boolForKey("skip") == true{
var nav = self.view?.rootViewController as UINavigationController
var storyBoard = UIStoryboard(name: "Main", bundle: nil)
nav.pushViewController(storyBoard.instantiateViewControllerWithIdentifier("BookingViewController") as BookingViewController, animated: false)
}
}
But it gives errors with the self.windowparts and says that the viewController does not have a member named 'window'
How could I make this work?
Thanks!
My suggestion for creating tutorial/insturctional screens would be to create images(full screen) with opacity background color and text full visible. Handle showing with NSUserDefaults(after you show one screen, save that to user defaults so you know next time that you shouldn't show that particular image).
Don't forget to create pictures for all screens (for iphone: 4, 5, 6, 6+)
You need to check in your appdelegate's didFinishLaunchingWithOptions method for, lets say a boolean value in your userdefaults, which indicates wether you should start with a "instructional" viewcontroller or the normal viewcontroller.
So you can set the instrucitonal viewcontroller as the default viewcontroller in your storyboard, and you can push the "non-instructional" viewcontroller if it has been shown.
if userDefaults.boolForKey("HasBeenShownInstructions") {
var nav = self.window?.rootViewController as UINavigationController
var storyBoard = UIStoryboard(name: "Main", bundle: nil)
nav.pushViewController(storyBoard.instantiateViewControllerWithIdentifier("BookingViewController") as BookingViewController, animated: false)
}
Dont forget to save the boolean value to userdefaults when the user has dismissed/ended the instructions.
This is just one of many ways to solve the issue. You could also do it the other way around. Depends on what you need.

Check for user in session in AppDelegate and trigger UIViewController if not

I have a Swift application, and what I'd like to do is that every time the app becomes active, I'd like to check for a user in session. If not found, I'd like to show a login view controller that I designed in my Storyboard. If found, I need everything to just resume as usual.
Where is the best way to trigger this check? Is AppDelegate's applicationDidBecomeActive the right place for this?
If yes, how do I actually instantiate the login view controller and show it? I have another home view controller, which is set to be my initial view controller. How do I manage this controller if and when I do end up successfully pushing the login view controller from the app delegate in case there is no user found in session? I don't want the home view controller to show up if a user is not found.
Any advise is greatly appreciated!
Hope this answer can help!
Put this in your AppDelegate.swift file. With the if you can check the value saved in local memory.
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
var userLoggedIn: Bool = NSUserDefaults.standardUserDefaults().boolForKey("isUserLoggedIn")
if (userLoggedIn){
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
var homeViewController = mainStoryboard.instantiateViewControllerWithIdentifier("HomeViewController") as!
HomeViewController
window!.rootViewController = homeViewController
}
return true
}
If let's say you store a token in your usersession, we go look if there is a token set or not. If it's already set (so not null) then we go to your other viewcontroller
let prefs = NSUserDefaults.standardUserDefaults()
let checkfortoken = prefs.stringForKey("token")
if(checkfortoken != ""){
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("identifierofyourview") as UIViewController
self.presentViewController(vc, animated: true, completion: nil)
}
Now you want to check this when you start your app so we gonna put the code into appdelegate in the first function (didFinishLaunchingWithOptions):
func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
UIApplication.sharedApplication().setStatusBarStyle(UIStatusBarStyle.LightContent, animated: true)
//so here ...
return true
}

Resources