Swift 2 self.presentViewController vs segue for simple navigation - ios

I am new to iOS Development and I am using swift2 to develop a simple web application whereby I am unsure of the behaviour of self.presentViewController.
In my MainViewcontroller I have the following codes to redirect user to login viewController
let storyboard = UIStoryboard(name: "Main", bundle: nil);
let signinViewController = storyboard.instantiateViewControllerWithIdentifier("SigninViewController") as! SigninViewController;
self.presentViewController(signinViewController, animated: true, completion: nil);
This set of codes work the way I want it to, which will redirect the user to signin page upon starting the application.
However in android development, I have to do a "finish();" to destroy my current stack, and I am unsure whether if I have to follow similar procedure in iOS Development.
Next, I have done some research on self.presentViewController versus segue as followed in the given article
UIStoryboardSegue versus presentviewcontroller?
So am I save to say that my approach in redirecting users from (Main_VC to SignIn_VC) is correct?

Your code is correct. You should not destroy presenting view controller in any way, it needs to be there when you get back from presented view controller.
If you do not plan to ever return the previous controller, you can simply replace it with new view controller as your window's (or navigation controller's) root. Since you are presenting sign in controller, this is probably not what you might want to do, just mentioning in for sake of completeness

Related

Exiting app and going back to home screen using swift 4

I am using a UITabbarController. I have 3 items/tabs inside the UITabbarController. Each tab depicts separate View controller. The list of views are as under
Tab 1: List View Controller
Tab 2: Favorite View Controller
Tab 3: Contacts View Controller
Now suppose I am on Tab 3 (Contacts View controller). And here I am showing my user a UIAlertController that has a button "Exit App".
When User will click on the Exit app, the user is supposed to go out from our application and will be taken to the home screen.
I have tried following snippets from SO but nothing is working for me.
self.view.window!.rootViewController?.dismiss(animated: true, completion: nil)
I even read to do exit(integer number) to exit the app but I read it also that it should not be done.
So I am really confused about what I need to do to exit app? How can I quit app and go back to home screen using Swift 4?
It's not a good idea to exit the app. The URL provided by #Cristik in comments also suggests that if apple will get to know about it they will send you a notice to remove it.
Better just keep the proper UIAlert message to tell the user what are the things required to use the app. Also proper UI navigation to first get all the required things for you business logic so that the user don't miss anything before using the app.
If you still want go with this then as #Cristik commented try this solution:
How does Booking.com close their app programmatically?
You can use:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let loginVcObj = storyboard.instantiateViewController(withIdentifier: "SignInVc") as! SignInVc
var vcArray = (applicationDelegate.window?.rootViewController as! UINavigationController).viewControllers
vcArray.removeAll()
vcArray.append(loginVcObj)
(applicationDelegate.window?.rootViewController as! UINavigationController).setViewControllers(vcArray, animated: false)

How to segue to a different view controller on the last page in swift?

I have two different view controllers. One is programmatically created(slideshow) and the other is with the interface builder(Login page). On the slideshow, once the user reaches the last page, it should perform segue to the login page.
How can I make this work?
First you can't create a segue between a VC created in code and another created in IB ( as both should be in IB ) , you must present it like this
let login = self.storyboard?.instantiateViewController(withIdentifier: "loginID") as! loginViewController
and use
self.present(login, animated: true, completion: nil)
OR use this to completely clear the stack of shown VCs
UIApplication.shared.keyWindow?.rootViewController = login
If two view controller don't need to transmit data, you can just change window root view controller to login page. Of course, with animation would be better.

Proper Login Flow and TabBar VC

I am having a few issues accessing the tab bar from the App Delegate to setup the Home Screen Quick Actions. Here is the line of code that I am using to access the tab bar. It is returning false.
guard let tabBarController = self.window?.rootViewController as? UITabBarController else {return false}
My tab bar is not my initial VC when the app launches. I have a loading screen during which we authenticate the user token and then it goes to either the login screen or the tab bar controller (which is also the main part of the app) depending on whether the token gets authenticated.
What is best practice for setting up an app with a login screen? The way we are doing it now works fine but I can change it if there is a better way. We are also using Branch for deep linking.
This is a opinion based question and might exist multiple answer to it each of which might be suitable for some specific scenario.
Approach 1 :
This is my personal Favorite does not mean that this is the only proper way of doing it. I prefer replacing the application's rootView Controller either with LoginVC or TabBarVC based on wether the token is valid or not. There are multiple answers in SO explaining how can u replace the application's rootVC with proper animation. Pasting the same code would be redundant here.
Why I use this approach?
Keeps my applications navigation controller stack clean and I don't keep any additional VC's in memory than whats actually required.
Approach 2 :
This is what many people use for the simplest reason that its simple to use but I personally doesn't prefer it. Modally present either Login VC or Tab bar (both of them might be embedded with UINavigationController obviously and you modally present their NavController's which obviously loads its embedded view controller).
Pros:
Easy to code.
You can always be sure that app's rootVC is always fixed and it has presented either LoginVC or TabBarVC. So parsing and accessing the VC's becomes fairly simple.
Cons:
The Landing VC which modally presents unnecessarily remains in Applications Navigation stack through out the apps life cycle. I clearly don't favor this.
EDIT :
As OP has clearly mentioned that he is using approach 2 and wants to know how to access specific VC in tab bars selected index am updating the code show the same.
Code is not intended for Copy paste, code might contain syntactic error. Code provided below is only intended for providing the idea.
Assuming your LandVC does not have UINavigationController embedded to it.
if let landVC = UIApplication.shared.keyWindow?.rootViewController {
if let presentedVC = landVC.presentedViewController {
if presentedVC is LoginVC {
//this is login VC
}
else if presentedVC is UITabBarController {
let currentlySelectedVC = (presentedVC as! UITabBarController).viewControllers?[(presentedVC as! UITabBarController).selectedIndex]
//now check what type VC it is and use it accordingly
}
}
}

How to pop from 3rd ViewController to the initial ViewController without NavigationControllers?

I know this is probably an easy task but I can't get my head around how to solve it. I'm not using a NavigationController by the way. Basically I have 3 view controllers in my app:
-LoginVC (this has a register button. when tapped, it goes to the SignupVC)
-SignupVC (if user signs up, it will push to the MainAppVC)
-MainAppVC (has a logout button)
For the transitions, I use the method: present(viewController, animated:, completion:)
When the user logs in via the LoginVC, he'll be presented the MainAppVC as expected. When he logs out, I'll dismiss the current VC (which is the MainAppVC) to send him back to the LoginVC.
Now here is the case where I have questions about. When the user does not have an account and signs up, this is the VCs he will pass through (LoginVC > SignupVC > MainAppVC). Once he registers successfully, he'll be presented the MainAppVC. Now once he logs out, he'll be transitioned from the MainAppVC to the SignupVC because I used the same dismiss method.
What I want to do is to send the user back to the LoginVC from the MainAppVC. How do I accomplish that without using a navigation controller in my project? How do popular apps (Facebook, Twitter, Instagram, etc) handle this in their apps?
One way I can think of is to perform a segue from 3rd to 1st VC but I think that's a dirty way since it just adds to the stack, instead of popping it off which is a potential issue.
There are three solutions.
1.Use window root view controller.Set root view controller between login and main view controller.
let loginSb = UIStoryboard(name: "Login", bundle: nil)
let loginVc = loginSb.instantiateInitialViewController()
window!.rootViewController = loginVc
2.Dismiss the sign up view controller the first time you present main view controller.And present the main view controller from login view controller.
3.Try to dismiss the sign up view controller in the completion block when you dismiss the main view controller.
I think the first one is best.
If are forced to NOT use a rootViewController (with UINavigationController), you should check in MainAppVC which one is the previous VC (Pseudocode in the example) and do:
Note: Swift 3 Code.
// If previous VC is LoginVC
dismiss(animated: true)
// else if previous VC is SignupVC
// here's what you want
presentingViewController?.presentingViewController?.dismiss(animated: true)
Hope that helped.
Another option is to use unwind segues.
In the ViewController that you want to go back to, implement a method as follows
#IBAction func unwindToLogin(segue: UIStoryboardSegue) {
}
Then in Storyboard, from the last ViewController right click and drag from the button (for example) to Exit and choose unwindToLoginWithSegue.
Note that you can also do this programatically. Here is a good tutorial.
Best approach is :
Dismiss SignUpVC once User Register successfully, Then from LoginVC present MainVC.
In SignupVC :
self.dismissViewControllerAnimated(true, completion: {
self.LoginVC!.present(MainVC, animated: true, completion:nil)
})
This approach is used mostly.

Calling custom function inside UIStoryboard-instantiated UIViewController (Swift)

I think my problem is best expressed by first giving an outline of what I'm trying to accomplish and then giving my implementation, followed by the problem I've ran into.
Goal
I'm attempting to make a profile page within my iOS app that can be instantiated from the Storyboard, given a user ID, and then fetch all the meta data from the server.
Implementation
In my storyboard, I have designed the ViewController and linked the ImageViews, buttons et cetera as #IBOutlets. The user's (person using the app.) homepage is instantiated by the Storyboard, but the rest are pushed in by code. In order to grab the data, I have a function loadDataForID(ID: Int)
Now, the problem I have is that #instantiateViewControllerWithIdentifier(String) returns a UIViewController that I cannot upcast to my ProfileViewController, in order to call the data fetching function.
So, my next thought was to manually create the ProfileViewController, call the helper function and then push the view, but then I ran into another problem: since all the views are #IBOutlets, and this controller wasn't instantiated by the Storyboard, they're all nil references.
As I'm new to iOS development, I'm almost certain there's a better way to implement this, but I'm completely stuck; I don't know where to go from here. Is there a way around these issues or should I do things a different way?
You can cast instantiateViewControllerWithIdentifier(String) as ProfileViewController as shown into below code:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let profileVC = storyboard.instantiateViewControllerWithIdentifier("ProfileViewControllerID") as! ProfileViewController
//Now you can access property of ProfileViewController here
self.presentViewController(profileVC, animated: true, completion: nil)

Resources