I want to be able to close and reopen the same view controller on my app. The reason I want to do this is because I have a "tutorial" scene that I only want to be brought up the first time you open the app. Every other time I want the app to instantly open up the second view controller. Here is my code:
for GameViewController:
UserDefaults.standard.set(0, forKey: "View")
for GameViewController2:
UserDefaults.standard.set(1, forKey: "View")
for AppDelegate:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:
[UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let viewCount = UserDefaults.standard.integer(forKey: "View")
var VC = UIViewController()
let storyboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
print(viewCount)
if viewCount == 0 {
//FirstView should initiate
VC = storyboard.instantiateViewController(withIdentifier: "First") as! GameViewController
} else {
//SecondView should inititate
VC = storyboard.instantiateViewController(withIdentifier: "Second") as! GameViewController2
}
self.window?.makeKeyAndVisible()
self.window?.rootViewController = VC
return true
}
The problem is that every single time I open the app, it always starts on my first view controller instead of my second view controller. Any help would be appreciated.
Based on the code above, you are not calling UserDefaults.synchronize
var userDefaults = UserDefaults.standard
userDefaults.set(1, forKey:"View")
userDefaults.synchronize()
In both cases it must be synchronised as this saves changes to disk
Update: As noted by #cs4alhaider synchronize() is not needed in Swift 4, so this problem would not occur anymore.
Related
I'm creating an iPad app with a SplitViewController, thats supposed to work in 2 ways.
You just open the app, and are presented with a master tableview
showing some assignments. These assignments can be 1 of three
different types. You select an assignment and are presented with a
detail ViewController using a show detail segue
You click a link
from another app, opening the app with an id, and should now only be
presented with the assignment corresponding to the id.
I have the following functions in AppDelegate:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let splitViewController = self.window!.rootViewController as! UISplitViewController
UIApplication.shared.statusBarStyle = .lightContent
splitViewController.delegate = self
splitViewController.preferredDisplayMode = .primaryOverlay
return true
}
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
if let itemid = getQueryStringParameter(url: url.absoluteString, param: "itemid"){
print(itemid)
NetworkService().getSpecificExercise(id: itemid) { response, error in
let exercise = response! as VoiceExercise
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
switch exercise.type {
case "STRENGTH":
print("Strength")
let initialViewController : StrengthViewController = storyBoard.instantiateViewController(withIdentifier: "StrengthViewController") as! StrengthViewController
initialViewController.exercise = exercise
self.window?.rootViewController = initialViewController
self.window?.makeKeyAndVisible()
case "RANGE":
print("Range")
let initialViewController : RangeViewController = storyBoard.instantiateViewController(withIdentifier: "RangeViewController") as! RangeViewController
initialViewController.exercise = exercise
self.window?.rootViewController = initialViewController
self.window?.makeKeyAndVisible()
case "COMBINED":
print("combined")
let initialViewController : CombinedViewController = storyBoard.instantiateViewController(withIdentifier: "CombinedViewController") as! CombinedViewController
initialViewController.exercise = exercise
self.window?.rootViewController = initialViewController
self.window?.makeKeyAndVisible()
default:
print(exercise.type)
}
}
}
return true
}
My problem is, that if the app has not been started yet (ie not running in background), and I open the app from another app (or a test link on the same ipad through safari), nothing happens from the open url function - it just shows the splitviewcontroller, with the tableview, as if I'd opened the app on its' own. If the app has already been opened, it behaves as expected - showing the assignment I expect.
Right now that is what I want. When you end an assignment, it opens the other app again, if the app has been opened by a link.
If I wanted to (when I click a link to open) show the assignment, but contained in a navigationcontroller, that was contained in the splitviewcontroller, where the splitviewcontroller had its' desiplaymode set to .primaryHidden, how would I go about this. All tutorials I've found only deals with Navigationcontroller, not contained in a splitviewcontroller. (this is still only for open url)
I'm using NSUserDefault in AppDelegate to decide which VC to show to a user first after they load my app.
If user hasn't set a specific setting, they go to VC 1 where they set that.
If user has the setting, they go to VC 2 and on.
My problem is that sometimes I'm being sent to VC1 when I know I have the setting.
This leads me to believe that NSUserDefaults are not being fully loaded before my conditional checks for the value.
Could this be possible and if so is there a way to "block" the load process until the variable is fully loaded?
Here's the relevant code from AppDelegate that I'm using:
class AppDelegate: UIResponder, UIApplicationDelegate {
let prefs = NSUserDefaults.standardUserDefaults()
var window: UIWindow?
func updateNavBar(navBar: UINavigationBar) {
navBar.barTintColor = UIColor(netHex:0x1d8696)
navBar.tintColor = UIColor(netHex:0xAEFFDD)
navBar.titleTextAttributes = [NSForegroundColorAttributeName : UIColor(netHex:0xAEFFDD),NSFontAttributeName : UIFont(name: "Lato-Bold", size: 16)!]
}
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
FIRApp.configure()
FBSDKApplicationDelegate.sharedInstance().application(application, didFinishLaunchingWithOptions: launchOptions)
self.updateNavBar(UINavigationBar.appearance())
if let team = self.prefs.stringForKey("team")
{
print("Team prefs found. (" + team + ") User sent to IVList")
// show IVList VC
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.window = UIWindow(frame: UIScreen.mainScreen().bounds)
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil) // this assumes your storyboard is titled "Main.storyboard"
let yourVC = mainStoryboard.instantiateViewControllerWithIdentifier("TabBarController") as! UITabBarController // inside "YOUR_VC_IDENTIFIER" substitute the Storyboard ID you created in step 2 for the view controller you want to open here. And substitute YourViewController with the name of your view controller, like, for example, ViewController2.
appDelegate.window?.rootViewController = yourVC
appDelegate.window?.makeKeyAndVisible()
}else{
// show editProfile VC
print("No team prefs found. User sent to profile Edit")
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.window = UIWindow(frame: UIScreen.mainScreen().bounds)
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil) // this assumes your storyboard is titled "Main.storyboard"
let yourVC = mainStoryboard.instantiateViewControllerWithIdentifier("editProfile") as! EditProfileViewController // inside "YOUR_VC_IDENTIFIER" substitute the Storyboard ID you created in step 2 for the view controller you want to open here. And substitute YourViewController with the name of your view controller, like, for example, ViewController2.
appDelegate.window?.rootViewController = yourVC
appDelegate.window?.makeKeyAndVisible()
}
return true
}
I think it's a load timing thing because the issue is intermittent and inconsistent. It happens about 20% of the time.
I recently had exactly this issue.
The simple answer is that didFinishLaunchingWithOptions() in the appDelegate is only called AFTER the nib has been loaded for your initial view controller.
See the discussion at iOS - viewDidLoad is being called BEFORE the didFinishLaunchingWithOptions delegate?
Hope that helps!
I got some logic I want to do in my LaunchScreen, and if the check is alright, I want to segue to a viewController and if not I want to segue to another, is that possible?
I got some logic I want to do in my LaunchScreen
Now that I understand the question, I can answer it: don't. Do your time-consuming logic later. Your job is to launch fast. You need to get out of applicationDidFinishLaunchingWithOptions, get out of viewDidLoad, and launch.
What you show at that point is up to you; if you have time-consuming stuff (in your case, it sounds like you're networking or doing something else that takes time while you load up the data source for a table) and you want to show a special view controller that covers the time with a spinning activity view or something, fine. But during actual launch is not the time to do that.
No you cant code for launchScreen.Storyboard, The reason why :- when your launchScreen.storyboard shows the app is still loading.
Simply put: You cant access your app when it is displaying launchScreen.storyboard, all you can do is make a UI/UX for that not execute any code for it.
Alternative:- Make a viewController that appears as a first viewController check your logic there and do things from there accordingly!
Reference : - https://stackoverflow.com/a/27642160/6297658
Your LaunchScreen is shown while your app is loading. Go to your AppDelgate:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
window.rootViewController = //your root view controller that you have figured out with logic
return true
}
Run your check in didFinishLaunchingWithOptions() and use that to "jump" directly to a specific vc. Here's an example using userDefaults, but of course you can replace that with whatever check you're running.
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Do some logic
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let welcomeVC = storyboard.instantiateViewControllerWithIdentifier("WelcomeNavController")
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window?.rootViewController = welcomeVC
self.window?.makeKeyAndVisible()
}
}
Add this function into the AppDelegate:
func initialVC(storyboardID: String) {
let mainStoryboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewController : UIViewController = mainStoryboard.instantiateViewControllerWithIdentifier("\(storyboardID)") as UIViewController
self.window?.makeKeyAndVisible()
if storyboardID == "tabBarVC" {
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window?.rootViewController = initialViewController
} else {
let navController = UINavigationController(rootViewController: initialViewController)
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window?.rootViewController = navController
}
}
In the didFinishLaunchingWithOptions method inside of the AppDelegate, you can add this:
if currentUser != nil {
initialVC("tabBarVC")
} else {
initialVC("loginVC")
}
You can see in my example, I am either loading the main app VC or the Login VC depending on if the user is logged in. In your case, You can use an if - else statement and do the logic within the initialVC function.
Note: When I call for the loginVC to be loaded, I have to load the navigationController because the loginVC is embedded in a navigationController. For the tabBarVC, I don't embed the navController because it isn't needed.
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.
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
}