Welcome Screen on Launch - ios

I want a way for the user to have a welcome screen / tutorial on the first launch of the app. If it isn't the first launch of the app, then it open as it usually would.
I already have the welcome screen tied to a button function if the app opens normally. I'm using BWWalkThroughViewController. Here's my code for the button function:
#IBAction func showWalkThroughButtonPressed() {
// Get view controllers and build the walkthrough
let stb = UIStoryboard(name: "MainStoryboard", bundle: nil)
let walkthrough = stb.instantiateViewControllerWithIdentifier("walk0") as! BWWalkthroughViewController
let page_one = stb.instantiateViewControllerWithIdentifier("walk1") as UIViewController
let page_two = stb.instantiateViewControllerWithIdentifier("walk2") as UIViewController
let page_three = stb.instantiateViewControllerWithIdentifier("walk3") as UIViewController
let page_four = stb.instantiateViewControllerWithIdentifier("walk4") as UIViewController
let page_five = stb.instantiateViewControllerWithIdentifier("walk5") as UIViewController
// Attach the pages to the master
walkthrough.delegate = self
walkthrough.addViewController(page_one)
walkthrough.addViewController(page_two)
walkthrough.addViewController(page_three)
walkthrough.addViewController(page_four)
walkthrough.addViewController(page_five)
self.presentViewController(walkthrough, animated: true, completion: nil)
}
func walkthroughCloseButtonPressed() {
self.dismissViewControllerAnimated(true, completion: nil)
}
That code is located in the MyTableViewController.swift file.
Here's what I can't figure out:
I want the view controllers to show on first launch. Once the user finishes the tutorial, they can press the Close button and it will close. I have the code to check if it's the app's first launch. It's located in the AppDelegate.swift file. Here's that code:
// First Launch Check
let notFirstLaunch = NSUserDefaults.standardUserDefaults().boolForKey("FirstLaunch")
if notFirstLaunch {
print("First launch, setting NSUserDefault")
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "FirstLaunch")
}
else {
print("Not first launch.")
}
return true
So how do I get the welcome screen to launch on first launch? Do I have to create a function in AppDelegate to handle that, and if so what do I have to do to make the tutorial the initial view controller for just the first launch?

I believe what you need to do is already covered here: Programmatically set the initial view controller using Storyboards. If that doesn't work for you add more notes on why the implementation failed. A google search on "programatically change uiviewcontroller on launch ios" will yield other similar links.

Related

ios - Change tabs and open modal window dynamically

I have an app that sends local notiffications at specific times. When the user taps on the notification, the app should open on a particular tab (a library tab) and then open automatically one of the lessons (without the user having to tap on it).
I can get the app to switch to the Library tab, but the modal with the lesson never appears.
This is the code I'm using:
if let tabBarController = window.rootViewController as? UITabBarController {
// this works
tabBarController.selectedViewController!.dismiss(animated: true, completion: nil)
tabBarController.selectedViewController = tabBarController.viewControllers?[1]
// Segue to particular lesson
let vc = tabBarController.selectedViewController as? LibraryViewController
let lessonVC = LessonViewController()
let les60 = Lesson(lessonNumber: "60")
lessonVC.delegate = vc
lessonVC.lesson = les60
vc?.present(UINavigationController(rootViewController: lessonVC), animated: true)
}
This is the code I use for every item in the Library:
let lessonVC = LessonViewController()
lessonVC.delegate = self
lessonVC.lesson = lessonsArray[indexPath.row]
present(UINavigationController(rootViewController: lessonVC), animated: true)
so I tried to replicate it above but it does not work.

How to solve this error "Warning:Attempt to present whose view is not in the window hirearchy"?

I'm developing an iOS application but I don't have storyboard and I do pure swift code, when I try to check the authentication in the MainViewController and I use perform to go to another ViewController and if token doesn't exist show button in MainViewController I ran into this warning and it won't work.
when I use perform like the snippet below to go to another ViewController by clicking on a button it works just fine.
I've seen all the answer by the title I'm asking here but all the examples has storyboards so it's not related to my question here.
here's a snippet that I'm trying to do in my app.
if defaults.string(forKey: Constants().userTokenKey) != nil
&& defaults.string(forKey: Constants().userTokenKey) != "" {
print("YOU ARE IN ELSE!")
let vc = SelectLocationOnMapViewController()
UIApplication.topViewController()?.present(vc, animated: true, completion: nil)
} else {
UIView.animate(withDuration: 1, animations: {
self.loginRegisterParentView.alpha = 1.0
})
setButtonActions()
}
The problem is here
UIApplication.topViewController()?.present(vc, animated: true, completion: nil)
it seems you use the rootVC which isn't yet fully presented to present another Vc , you need to move this code from viewDidLoad to say viewWillAppear/viewDidAppear

Starting at a certain view if user is logged in iOS Swift

I am currently making a small login app using Firebase. I am currently having problems with my login page.
When my users already are logged in, when they open the app, I want to change the initial view controller, so that the user can go straight to the homepage.
So my question is, what line of code do I have to perform in order to do this?
override func viewDidLoad() {
super.viewDidLoad()
if FIRAuth.auth() ? .currentUser ? .uid == nil {
notLoggedIn()
}
}
func notLoggedIn() {
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let nextViewController = storyBoard.instantiateViewController(withIdentifier: "Startpage") as!ViewController
self.present(nextViewController, animated: true, completion: nil)
}
There's a couple of ways you can do this. If you really want to change the initial view controller, you would want to NOT set an initial view controller in your storyboard, then in your app delegate's application(_:didFinishLaunchingWithOptions:) implementation, you would create a new Window object and set whichever view controller on it you want to present as the rootViewController. Then, you would call makeKeyAndVisible on that window object. Note that if you do it this way, you'll have to separately handle the case when they log out if you want to display your login window again. In that case you would just do the same thing again: make a new window object with your new ViewController object as the rootViewController and present it.
Another option is to check if they are logged in in your initial view controller's viewDidLoad method and then present your login screen if they aren't. This is what I do in one of applications where the app needs some data, either by logging into an account or manually adding it, before it can do anything.
EDIT:
Here's what my viewDidLoad, etc. looks like (note that mine project is in Objective-C, so I'm just kinda guessing without actually testing it what the correct Swift syntax is. You might need to make some adjustments) You have to dispatch the present call to the main queue because in viewDidLoad you (probably) don't have everything in order yet to actually present a new view controller (I did this quite a long time ago, so I don't recall exactly why it has to be dispatched, but because of the fact that we're already in the process of presenting the current view controller, it makes sense that you wouldn't be able to present another one at the same time. Maybe someone else can weigh in on this, because I really don't remember anymore.):
override func viewDidLoad() {
super.viewDidLoad()
if (!userLoggedIn) {
showLoginScreen()
}
}
func showLoginScreen() {
let loginViewController = storyboard?.instantiateViewController(withIdentifier: "Startpage") as! ViewController
DispatchQueue.main.async {
present(loginViewController, animated: true, completion: nil)
}
}
You can use this line of codes.
Keep in mind that you should add storyboard reference with identifier
named respectively for your need - goToLogin - in my case.
Hope It'll be helpful for anyone.
override func viewDidLoad() {
super.viewDidLoad()
Auth.auth().addStateDidChangeListener { auth, user in
if let user = user {
// User is signed in.
print("user signed in")
//Add the rest of the code here because after passig the caluses
// viewdidload will call another funxtions to it can crash
} else {
// User not signed in
self.performSegue(withIdentifier: "goToLogin", sender: Any?.self)
}
}
}

IOS - How to jump between viewControllers in different scenarios

I have 5 viewControllers(A,B,C,D,E), all these viewControllers are programmatically connected, and i could push and pop between them successfully.
Using:
navigationController?.pushViewController(loginVc, animated: true)
_ = navigationController?.popViewController(animated: true)
NOTE: ViewController A appears first as a default initial viewController.
Now, what i want is, when the user installs the App, only for the first time the viewController A must be shown as the initial viewController, rest of the times when the App is opened, the viewController D must be the initial viewController and from there i should be able to jump between previous viewControllers. How can i implement this. Im using Xcode 8.2, Swift 3.0
Thanks in advance.
In order to do that, you simply could add a boolean to your NSUserDefaults, using the following code:
let defaults = UserDefaults.standard
if (!defaults.bool(forKey: "firstTime")) { //will be false if does not exist yet
navigationController?.pushViewController(yourDesiredVC, animated: true) //push desired vc
defaults.set(true, forKey: "firstTime") //set the key so it never executes again
} else {
navigationController?.pushViewController(yourDesiredVC, animated: true) //push the other vc
}
Your question doesn't really say very much without some code, but one suggestion (given the current quality of the question) would be to use UserDefaults. Add your current version of the app to a key called e.g. LatestVersion. Compare it at launch with the apps current version, if they don't match, show ViewControllerA, if not show ViewControllerB.
Another way is just saving launchedForFirstTime. If its not set show ViewControllerA, however the above would take in account future versions of the app where you might want to show that view as well.
You can keep a value in UserDefaults to keep track of the returning users and check if it's there:
if let returning :Bool = UserDefaults.standard.bool(forKey: "initial_controller_shown") {
//returning user flow
} else {
//new user flow
}
A common place to check for this is in the applicationDidBecomeActive or didFinishLaunchingWithOptions
when first time your app launched then use a flag and store some value in it so that next time when your app run then you can check that whether user visit the app for the first time or not .. Now after that go to appDelegate and paste the following code in DidFinishLaunchingWithOption...
if yourFlag == true
{
let mainStoryboard: UIStoryboard = UIStoryboard(name: "MainStoreyBoard", bundle: nil)
let controller = mainStoryboard.instantiateViewController(withIdentifier: "StoreyBoardIdofYourViewController") as! UINavigationController
self.window?.rootViewController = controller
}
This will launch D viewcontroller .....
You can check it in your app delegate.m file whether the app installed first time ViewController A will appear as a initial view controller else view controller D. Check it in the following function:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
//check the initial view controller here//
return true
}
Swift 3:
let launchBefore = UserDefaults.standard.bool(forKey: "launchBefore")
if launchBefore {
print("Not first launch.") } else {
print("First launch, setting UserDefault.")
UserDefaults.standard.set(true, forKey: "launchBefore") }
You are using one Navigation Controller so it will be hard to implement your behavior. It will be much easier for you to use separate View Controllers for View A and D and call them using:
present(vc, animated: true)
and dismiss calling:
dismiss(animated: true)

How to connect Universal Links to UIViewController?

*Disclaimer: I've only been coding in iOS/XCode/Swift for a couple of weeks
I have Universal Links working in that clicking a link outside my app opens up the app and I catching the Url in the AppDelegate class.
So that's all good.
My question is... how do I then redirect to the correct UIViewController AND pass the controller some info from my URL? All Universal Link tutorials stop before they get to that part.
In particular I'm confused about the lifecycles of AppDelegate and how it relates to UIViewController.
My app had two UIViewController sitting under (is this right?) a UINavigationController.
What I've tried
I have tried handling the url event in AppDelegate, and setting a public property, and then in my ViewController getting access to the AppDelegate. HOWEVER, after the Universal Link is clicked, both viewDidLoad and viewWillAppear don't get called again :-/
What's the best way to redirect to a ViewController from AppDelegate? My goal is simply to load the root view controller BUT I need to pass in some data from the URL. How?
First, Read your URL.
Get your parameters from URL
Initiate your target controller
Set your parameter to that controller
Present controller on root view controller
let urlString = url.absoluteString
let queryArray = urlString.componentsSeparatedByString("/")
if queryArray[2].lowercaseString == "yourQuery" {
let queryId = Int(queryArray[3])
if self.window?.rootViewController?.presentedViewController != nil {
self.window?.rootViewController?.dismissViewControllerAnimated(false, completion: nil)
}
let queryVC = self.window?.rootViewController?.storyboard?.instantiateViewControllerWithIdentifier(QUERY_SCENE) as? QueryViewController
queryVC?.urlQueryId = queryId!
self.window?.rootViewController?.presentViewController(queryVC!, animated: true, completion: nil)
}
Edit:
Push a controller say 'PresentedViewController' on navigation controller and if rootViewController is also navigation controller
And on back press on controller 'OnBackPressViewController' present controller 'PresentedViewController'
if self.window?.rootViewController?.presentedViewController != nil {
self.window?.rootViewController?.dismissViewControllerAnimated(false, completion: nil)
}
let navController = self.window?.rootViewController?.storyboard?.instantiateInitialViewController() as? UINavigationController
let presentedVC = self.window?.rootViewController?.storyboard?.instantiateViewControllerWithIdentifier(PRESENTED_SCENE) as? PresentedViewController
//Pass parameters to PresentedViewController
let onBackPressViewController = self.window?.rootViewController?.storyboard?.instantiateViewControllerWithIdentifier(ON_BACK_PRESS_SCENE) as? OnBackPressViewController
navController!.pushViewController(onBackPressViewController!, animated: true)
navController!.pushViewController(presentedVC!, animated: true)
self.window?.rootViewController?.presentViewController(navController!, animated: true, completion: nil)
You can update your code according to that. Always remember that you have to present any view controller on rootViewController.
It depends on your app architecture. You can definitely assume that the AppDelegate has the reference to the rootViewController (window?.rootViewController). You should know what's the type of that controller. Then you can access to the other and so on. This question is, anyway, too much generic in my opinion.

Resources