This question already has answers here:
set initial viewcontroller in appdelegate - swift
(24 answers)
Closed 5 years ago.
I am trying to add a disclaimerViewController to my application as the initial viewController, which, if accepted will lead the user tot he entryViewController, and if not, will not allow the user to use the application. However, I want to to be that if the user accepts the disclaimer, any time after he opens the application he won't be presented with the disclaimerViewControleler but with the entryViewController.
I know that it has something do to with editing the AppDelegate.swift file, but am unsure of were to start.
You need to save user choice in UserDefaults
The code below is using Swift 3
If you don't want to load entryViewController then In the AppDelegate:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
//retrieve values from UserDefaults
//for the first time it will be false, because it was not set earlier
let isAccepted = UserDefaults.standard.bool(forKey: "isAccepted")
if isAccepted == false {
//present your disclaimer here
}else{
//show entryViewController
}
return true
}
Or you can load entryViewController and present disclaimer instantly, then in your entryViewController:
override func viewDidLoad() {
super.viewDidLoad()
//retrieve values from UserDefaults
//for the first time it will be false, because it was not set earlier
let isAccepted = UserDefaults.standard.bool(forKey: "isAccepted")
if isAccepted == false {
//present your disclaimer here
}
}
In the DisclaimerVC:
#IBAction func accept(_ sender: UIButton){
//it can be another action in your controller
//but anyway, you should save user choice after that
UserDefaults.standard.set(true, forKey: "isAccepted")
//add code here to dismiss disclaimer
}
The way I would go about doing this is by setting your disclaimerViewController to the initial view controller in your storyboard file.
In the swift file check to see if the user has previously accepted the disclaimer in the viewDidLoad method, if they haven't, allow the code to function normally and display the screen, if they have push the user on to your entryViewController
If you want to set your initial view controller in the AppDelgate and not in the storyboard, here is a useful link: set initial viewcontroller in appdelegate - swift
To transition to a new viewcontroller in swift, here is a useful link: Switching view controllers in swift
The smoothest way to implement this is to switch the views programmatically.
Make sure to set the restoration ids for your ViewControllers in the StoryBoard.
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var mainViewController: ViewController?
var acceptViewController: AcceptViewController?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
mainViewController = mainStoryboard.instantiateViewController(withIdentifier: "MainView") as? ViewController
acceptViewController = mainStoryboard.instantiateViewController(withIdentifier: "AcceptView") as? AcceptViewController
if mainViewController == nil {
print("mainViewController is nil")
}
if acceptViewController == nil {
print("acceptViewController is nil")
}
let isAccepted = UserDefaults.standard.bool(forKey: "isAccepted")
if isAccepted == false {
switchToAcceptViewController()
} else {
switchToMainViewController()
}
return true
}
func switchToMainViewController() {
//mainViewController?.selectedIndex = 0 // only needed if the main view controller is a tab view
self.window?.rootViewController = mainViewController
self.window?.makeKeyAndVisible()
}
func switchToAcceptViewController() {
//mainViewController?.selectedIndex = 0 // only needed if the main view controller is a tab view
self.window?.rootViewController = acceptViewController
self.window?.makeKeyAndVisible()
}
}
AcceptViewController
class AcceptViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func acceptAction(_ sender: Any) {
UserDefaults.standard.set(true, forKey: "isAccepted")
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.switchToMainViewController()
}
}
You can get the full project here: https://github.com/ryantxr/legendary-fiesta
Related
I am developing an app using swift 3 . I launch a tutorial like view controller on the first launch of my app and close it using a button .But the inital view controller does not get dismissed even when i click the close button. I know the question sounds familiar but i am unable to get a correct solution for it .
Here's the code of the the AppDelegate
import UserNotifications
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application:
UIApplication,didFinishLaunchingWithOptions launchOptions: [
UIApplicationLaunchOptionsKey: Any]?) -> Bool
{
let launchedBefore = UserDefaults.standard.bool(forKey: "launchedBefore")
if launchedBefore {
print("This is not the first Launch")
} else
{
print("First Launch, setting UserDefaults")
UserDefaults.standard.set(true, forKey: "launchedBefore")
self.window = UIWindow(frame: UIScreen.main.bounds)
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
let initiaView = storyBoard.instantiateViewController(withIdentifier: "FirstLaunchViewController")
self.window?.rootViewController = initiaView
self.window?.makeKeyAndVisible()
}
return true
}
Here's my code from the initialViewController
class FirstLaunchViewController:UIViewController
{
override func viewDidLoad() {
}
#IBAction func doneButtonTapped(_ sender: Any)
{
_ = navigationController?.popViewController(animated:true)
}
}
Try this
let rootViewController = self.window?.rootViewController // this is your initial viewcontroller set initial viewcontroller in storyboard
let initiaView = storyBoard.instantiateViewController(withIdentifier: "FirstLaunchViewController")
rootViewController.pushViewController(initiaView, animated: true)
Try setting the view controller as root view controller for window which you want to show after dismissing the initial view controller. Present the initial view controller from window root view controller
Hello I am trying to implement a push notification feature in my IOS app. Currently I have code that will open a specific viewcontroller if the app was opened through the push notification. The way i'm doing this is by pushing the viewcontroller ontop.
What I need to do now is to pass some data to the viewcontroller from the appdelegate. I understand to pass data from viewcontroller to viewcontroller is to use prepareforsegue. I tried doing this and it did not work
I tried researching how to accompolish this task but a lot of the answers on stackoverflow were outdated swift code that I don't have the ability to convert to swift 3. can someone explain to me how would i send data from appdelegate to a viewcontroller?
heres the code to show the VC which is in didFinishLaunchingWithOptions
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let destinationViewController = storyboard.instantiateViewController(withIdentifier: "detailPin2") as! detailPinViewController
let navigationController = self.window?.rootViewController as! UINavigationController
navigationController.pushViewController(destinationViewController, animated: false)
//Declare you variable somewhere within the app delegate scope
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var myVariable: String = "Hello Swift"
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
// some other app delegate’s methods....
}
//In your view controller
class ViewController: UIViewController {
#IBOutlet weak var myLabel: UILabel!
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let myOtherVariable = appDelegate.myVariable
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
myLabel.text = myOtherVariable
var anotherVariable: String = appDelegate.myVariable // etc...
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
OK - you've got most of the work already done...
When using segues, iOS creates your view controller and gives you access to it in prepare, where you probably have done something like this:
if let vc = segue.destination as? detailPinViewController {
vc.myVariable = "this is the data to pass"
}
In didFinishLaunchingWithOptions, you are doing the creation part, and the "presentation" part... so you can just add in the "pass the data" part:
let destinationViewController = storyboard.instantiateViewController(withIdentifier: "detailPin2") as! detailPinViewController
destinationViewController.myVariable = "this is the data to pass"
let navigationController = self.window?.rootViewController as! UINavigationController
navigationController.pushViewController(destinationViewController, animated: false)
and that should do it.
I use firebase for authentication. Once user logs in, i want move to home screen with Tab controller.
App Delegate Function
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
{
window = UIWindow(frame: UIScreen.mainScreen().bounds)
window?.makeKeyAndVisible()
navToSignin()
return true
}
func navToHome()
{
print("navToHome")
let customTab = CustomTabBar()
window?.rootViewController = customTab
}
func navToSignin()
{
let firstPage = SigninNewVC()
window?.rootViewController = firstPage
}
SigninNewVC
override func viewWillAppear(animated: Bool)
{
super.viewWillAppear(animated)
if FIRAuth.auth()?.currentUser?.uid != nil
{
if let appDel = UIApplication.sharedApplication().delegate as? AppDelegate
{
print("viewWillAppear")
appDel.navToHome()
//This func gets called but still does not navigate to Home VC
}
}
}
private func fbSignin(token: String)
{
FIRAuth.auth()?.signInWithCustomToken(token) { (user, error) in
if error == nil
{
if user != nil
{
if let appDel = UIApplication.sharedApplication().delegate as? AppDelegate
{
JulehHUD.hideProgressHUD(self.view!)
appDel.jumpToHome()
//This part works fine when i login it navigates to Home Screen
}
}
}
else
{
print("Error:\n\(error)\n")
}
}
}
When i login for the first time it works fine I am able to Navigate to Home screen. But if i relaunch the App i not able to navigate to the home screen. It stays in the SigninNewVC though the print Statements print("viewWillAppear")& print("navToHome") are executed. Not sure what i am doing wrong. Below is my Tab bar class
class CustomTabBar: UITabBarController
{
override func viewDidLoad()
{
super.viewDidLoad()
let homeController = HomeScreenVC()
let newHomeTab = UINavigationController(rootViewController: homeController)
newHomeTab.title = "Home"
viewControllers = [newHomeTab]
}
}
Kindly let me know what i am doing wrong.
I think you insantiated the VC wrongly, you should be using something like CustomTabBar(nibName: nil, bundle:nil) and add your subview in it's loadView:
Adding below code to navToHome() & navToSignin() in App Delegate fixed the issue.
window = UIWindow(frame: UIScreen.mainScreen().bounds)
window?.makeKeyAndVisible()
I would like to know which is the best way to do this and how to implement it:
When the app runs, it checks if there's a value in user defaults
NSUserDefaults.standardUserDefaults().valueForKey("uid") == nil If the value is nil, I would like the app to redirect to a view called LoginViewController, and if not nil, go to MainViewController. Both clases are UIViewController.
I was thinking in create a new RedirectViewController as initial View Controller, and check it there, but it doesn't work, it doesn't redirects:
class RedirectOnStart: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if NSUserDefaults.standardUserDefaults().valueForKey("uid") == nil {
self.performSegueWithIdentifier("loginRegisterSegue", sender: nil)
} else {
self.performSegueWithIdentifier("mainViewSegue", sender: nil)
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "loginRegisterSegue" {
let nav = segue.destinationViewController as! LoginRegisterViewController
}
if segue.identifier == "mainViewSegue" {
let nav = segue.destinationViewController as! ViewController
}
}
}
If your scenario starts with "when the app runs" you should consider working in the AppDelegate's didFinishLaunchingWithOptions directly.
For example:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
self.window = UIWindow()
if NSUserDefaults.standardUserDefaults().valueForKey("uid") == nil {
self.window?.rootViewController = LoginController()
} else {
self.window?.rootViewController = MainController()
}
self.window?.makeKeyAndVisible()
return true
}
Found that the above answer is written for Objective-C not Swift; currently written it won't load up the actual view controller you want. Below code will work within 'didFinishLaunchingWithOptions".
make sure you have a userdefault (in my case "Username") for the statement to check for. If it find it's empty; that'll direct you to "SignUpVC", if it's not empty, it'll direct you to "LoginVC".
you'll need to ensure you have set your identifiers for your View Controllers on your storyboard (in my case, 'Main').
after this, make sure you have some sort of method setup to go from Login/Signup to the next set of screens, such as a segue.
Hope this helps someone out there, Have a great day.
Swift 3:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
self.window = UIWindow()
if UserDefaults.standard.value(forKey: "Username") == nil {
let signupViewCon = storyboard.instantiateViewController(withIdentifier: "SignUpVC")
self.window?.rootViewController = signupViewCon
} else {
let loginViewCon = storyboard.instantiateViewController(withIdentifier: "LoginVC")
self.window?.rootViewController = loginViewCon
}
self.window?.makeKeyAndVisible()
return true
}
I am developing an iOS App that has several Tabs. So I decided my root view Controller will be a Tab Bar Controller with 2 views.
I need the user to login as soon as the application starts, so I'm trying to find out the best approach to do this.
Following other similar questions posted here I've tried this 2 approaches:
Presenting the login screen as a Modal View on top of the Tab Bar Controller
Instantiating the Login view controller
I've done this, by using a UserisLoggedIn flag in NSUserDefaults that is set every time the user logs in or logs out.
The problem I face with both approaches is that before the login screen appears there's a quick flickr of the first view in the Tab Bar.
I've tried to resolve this setting the alpha value of the Tab Bar to 0 if the user is not logged in for the modal approach, but for the instance approach it doesnt work, it still flickers before showing the login screen. Anyway, I find this solution tedious and not really elegant.
There must be a standard approach for doing this in iOS, since you have loads of apps that present first a login screen, but since I am a beginner with iOS Programming I still don't know how to do it.
Here's some code from my Tab bar controller:
1st Approach
Tab Bar Controller
override func viewDidLoad() {
super.viewDidLoad()
view.alpha = 0
let defaults = NSUserDefaults.standardUserDefaults()
if defaults.objectForKey("userLoggedIn") as NSString == "loggedIn"{
view.alpha = 1.0
}
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(false)
self.showLoginView()
}
func showLoginView(){
let defaults = NSUserDefaults.standardUserDefaults()
if defaults.objectForKey("userLoggedIn") == nil{
self.performSegueWithIdentifier("loginView", sender: self)
}
}
Login View Controller
func updateUserLoggedInFlag() {
// Update the NSUserDefaults flag
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject("loggedIn", forKey: "userLoggedIn")
defaults.synchronize()
}
#IBAction func login(sender: AnyObject) {
//Do my login here...
//If login successful:
self.performSegueWithIdentifier("dismissLogin", sender: self)
}
2nd Approach, Creating an Instance of the Login View Controller
1st View in the Tab Bar
override func viewDidAppear(animated: Bool){
super.viewDidAppear(true)
let defaults = NSUserDefaults.standardUserDefaults()
if defaults.objectForKey("userLoggedIn") == nil{
let loginController: LoginViewController = self.storyboard?.instantiateViewControllerWithIdentifier("LoginViewController") as LoginViewController
self.tabBarController?.presentViewController(loginController, animated: false, completion: nil)
}
Login View Controller
#IBAction func login(sender: AnyObject) {
//Do my login here...
//If login successful:
self.dismissViewControllerAnimated(true, completion: nil)
}
OK here is what did the trick for me. I don't know if it's the right thing to do, but it seems to work. Like Joseph Duffy mentioned all I did was to set the Login view as the rootviewcontroller in the delegate, and once I was succesfully logged in, switch back to the tab bar controller as rootviewcontroller.
Code in AppDelegate:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let defaults = NSUserDefaults.standardUserDefaults()
if defaults.objectForKey("userLoggedIn") == nil{
showLogin()
}
}
func showLogin() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let loginViewController = storyboard.instantiateViewControllerWithIdentifier("LoginViewController") as LoginViewController
self.window?.makeKeyAndVisible()
self.window?.rootViewController = loginViewController
}
Code in LoginViewController
#IBAction func login(sender: AnyObject) {
//Do my login here...
//If login successful:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let appDelegate: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
self.dismissViewControllerAnimated(true, completion: nil)
appDelegate.window?.rootViewController = storyboard.instantiateViewControllerWithIdentifier("tabBarID") as TabBarController
}
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
let defaults = NSUserDefaults.standardUserDefaults()
if defaults.objectForKey("userLoggedIn") == nil{
showLoginView()
}
func showLoginView(){
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let loginViewController: LoginViewController = storyboard.instantiateViewControllerWithIdentifier("LoginViewController") as LoginViewController
self.window?.makeKeyAndVisible()
self.window?.rootViewController?.presentViewController(loginViewController, animated: true, completion: nil)
}
Take a look at a similar question's answer. The solution is to do this check in your app delegate's application:didFinishLaunchingWithOptions: method by checking if the user is logged in and loading the login view from a storyboard when necessary.
I have created a blank project, added a view controller with a storyboard identifier of loginScreen and have the following code in the app delegate:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
self.showLogin()
return true
}
func showLogin() {
//loginScreen
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let loginViewController = storyboard.instantiateViewControllerWithIdentifier("loginScreen") as UIViewController
if let window = self.window {
window.makeKeyAndVisible()
window.rootViewController?.presentViewController(loginViewController, animated: false, completion: nil)
}
}
This shows the login screen and the main screen is not seen.