Popping existing view controller and loading parent view controller - ios

I have a use case where a view controller (e.g Source) loads other view controller (Login) using performSegueWithIdentifier
As name suggests LoginViewController is supposed to take user credentials, authenticate the user and load SourceViewController .
I created a delegate LoginDelegate which is implemented by SourceViewController. LoginViewController can successfully call delegate.onSuccessfulLogin() and delegate.onFailedLogin(). However, SourceViewController is not appearing.
Since, I didn't run any statements which give control of screen back to SourceViewController, I think I am running into this issue.
What is the best way, in such scenarios, to pop existing controller (LoginViewController) and give control (screen space) back to Source controller (SourceController)

Call dismissViewControllerAnimated(true, completion: nil) or navigationController?.popViewControllerAnimated(true) from the LoginViewController after calling delegate.onSuccessfulLogin() or delegate.onFailedLogin()
It will make you go back to the SourceViewController

Related

dismiss more than one View Controllers to return to the root ViewController in ios13

I have three view controllers written programmatically, the first VC is Sign in but if the user forgets the password this will led him to another two view controllers I need to return to the sign in VC directly form the third VC after the user finish the specific procedures.
you could do like this
self.presentingViewController?.presentingViewController?.dismiss(animated: true, completion: nil)
since are both vc to dissmiss it will return to login vc.
every presented viewController has a property called
PD: presentingViewController, is hold as reference to the viewController that is responsable for present it, so 2dn VC has a reference to 1st VC as presentpingviewcontroller, but also 3er VC have a reference to 2dn vc that has a reference to 1st VC so you call the above method chaining two presenting and ending in first VC, so you could present as many VC as you want as long as you know how many have been presented you could return to what ever you want.
You can use navigation controller, push your view controllers to navigation like
self.navigationController?.pushViewController(viewController: vc, animated: true)
and when you need to close all controllers use
self.navigationController?.popToRootViewController(animated: true)

EXC_BAD_ACCESS in UISplitViewController after unwind segue, then returning

This is an application who interface consists of a tab controller (which has the rest of the interfaces residing in tabs), then two UISplitViewControllers with tables in them, and lastly a simple UIView with a button that allows the user to logout. The logout is implemented with an unwind segue back to the initial view controller (which is a login screen), and setting the root view controller of the app to a freshly instantiated version of the initial view controller (the login interface). After using this logout, then logging back in, the application immediately crashes with an EXC_BAD_ACCESS exception.
I have tried multiple different ways of implementing the behavior of the logout button (to segue back to the login screen) but every attempt has failed with the same results.
By following the stack trace, I was able to determine that the crash is occurring in the setCollapsedState method of the UISplitViewController. This is not a custom implementation of a split view controller, just the default one.
In case this is relevant, I am setting one of the child view controllers of the parent view controller as the delegate, however when I tried changing this and not setting the delegate, nothing improved.
I am not sure what other information I can provide, other than this code snippet of the logout button. If there's any other information that I can supply, please let me know and I will post edits.
#IBAction func signoutButtonPressed(_ sender: Any) {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.signOut()
var rootVC = appDelegate.window?.rootViewController
rootVC?.removeFromParentViewController()
rootVC = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController()
appDelegate.window!.rootViewController = rootVC
}

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.

advice on UIViewController hierarchy and flow

I'm spending a lot time reading apple View Controller Programming but still can't get a clue of how to code for my flow.
I'm using ViewController, PageViewController, and Storyboard.
My rootViewController is a ViewController
I want two more ViewController
first, PageViewController to show pages of tutorials and last page has signup with presentViewController login
secondViewController, ViewController is the main content of the app
if case user has token in keychain will go to secondViewController.
After signup or login lead to secondViewController.
I didn't use navigation controller. Wonder this can be done by container view controller?
current problem In signupViewController self.parentViewController is pageViewController but I can't get the rootViewController, to dismiss and add secondViewController
P/S but In loginViewController self.presentingViewController I got the rootViewController!! it is out of my expectation.. I wonder why???
Set secondViewController as your rootViewController of UIWindow in AppDelegate. SecondViewController contains the base view content without data about login, so you can launch as soon as you can.
If user has token in keychain, just to update SecondViewController view with login status.
If user has no token in keychain, show PageViewController on SecondViewController, with presenting a viewController or just addSubview. After PageViewController, present loginViewController, when login finish, dismiss the loginViewController, update SecondViewController view with login status which is same with step2
You use storyboard, so I assume you'll also use segue. Your PageViewController can be the entry point, with a segue to secondViewController.
If you have the token in the keychain, you can programatically perform the segue using:
[self performSegueWithIdentifier: #"MySegue" sender: self];
Otherwise users stay on PageViewController to serve its original flow.

Issue loading View Controller from UITabBarController

In the past my app has had only 1 main view controller (MainViewController) and a login view controller (LoginViewController) but now I am moving to a Tab Bar Controller.
Before I was able to do a simple check viewDidLoad of MainViewController for the existence of a username and password in the key chain. If a username and password was not present I used a segue to pop up a modal login view controller.
With the new setup of using a Tab Bar Controller I still only have 1 view controller (MainViewController) which is the root view controller (as of now) and I am trying to do the same thing where it pops up modal of the login screen.
Now when I call the segue in the viewDidLoad of MainViewController:
[self performSegueWithIdentifier:#"loadLoginView" sender:nil];
I am getting this error:
Warning: Attempt to present <LoginViewController: 0x1757cd80> on <UITabBarController: 0x17571e50> whose view is not in the window hierarchy!
But if I associate a button to a method that loads the LoginViewController by way of a segue it works fine. I am doing that in the MainViewController like this:
-(void)loadLogin
{
[self performSegueWithIdentifier:#"loadLoginView" sender:nil];
}
I can see from the error message that when I try to perform the segue from the viewDidLoad of MainViewController it's trying to load the LoginViewController from UITabBarController.
Why can I not load the LoginViewController from the viewDidLoad of MainViewController?
Any help with this would be great.
Thanks!
It looks like -viewDidLoad is getting called before your view controller stack is added to the window. Two things you could try are:
Delaying until the next time through the run loop (this should give the view controllers time to get in place) [self performSelector:#selector(loadLogin) withObject:self afterDelay:0];. This method won't allow you to call a method with two arguments directly
You could use -presentViewController: animated: completion:. This will cause your login controller to slide in from the bottom.

Resources