Change view in code swift - ios

i have this Swift project where i have a tab bar Controller Scene, and i would like to add a view controller scene to act as a "Login scene", when the user is loged i want to switch the view to the tab bar controller scene.
I have implemented facebook login, so i have a method that is called when the login is completed.
I have done this:
func loginDone(){
let vc = self.storyboard?.instantiateViewControllerWithIdentifier("dashboardScreen") as! UITabBarController
self.presentViewController(vc, animated: true, completion: nil)
}
But that code brings me this error:
Attempt to present <UITabBarController: 0x7f87a37e8550> on <Project.LoginViewController: 0x7f87a352fca0> whose view is not in the window hierarchy!
What should i do?
Thanks!

Call it in viewDidAppear(), that way you can be sure that the login view is part of the app's view hierarchy.
You need to make sure the presenting view controller is in the app's view hierarchy. See the section 'Responding to View Events' in the UIViewController Class Reference
If you need more information on working with view controller's you can look at the View Controller Programming Guide

You should not call loginDone from init, viewDidLoad methods and others that are called before view is shown. Call it from viewDidAppear.

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)

Save all subsequent view controller states in UINavigationViewController

I have a container UIViewController that hosts a single UINavigationController. The container view controller has a button that opens a new view controller by calling present(newViewController, animated: true, completion: nil).
The newViewController has its own UINavigationController and also contains a button. That button can present another view controller that itself has a UINavigationController and another button and so on.
I want to keep that pattern going for as many iterations as possible and save the states of all of them. Is that possible? To close the current view controller I call _ = navigationController?.popViewController(animated: true) but that erases all the data from the previous view controller as well.
You asked two questions
1) want to keep that pattern going for as many iterations as possible and save the states of all of them.
Ans. : you can not continuously presenting view on already presented view controller. for that first you need to dismiss previous presented view.
2) To close the current view controller I call
_ = navigationController?.popViewController(animated: true)
but that erases all the data from the previous view controller as well.
Ans. If you present any view controller then don't use popviewcontroller but use dismisViewController.

UIViewController.dismiss() not calling presentingViewController.dismiss()

apple documentation on UIViewController dismiss(animated:completion:) says that
The presenting view controller is responsible for dismissing the view
controller it presented. If you call this method on the presented view
controller itself, UIKit asks the presenting view controller to handle
the dismissal.
but when I override the presenting controller's dismiss
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil)
it is not called
ViewController1 -> present ViewController2
ViewController2.dismiss -> dismiss from ViewController2 is called and ViewController1.(override)dismiss is not called
According to the documentation of present(_:animated:completion:):
The object on which you call this method may not always be the one
that handles the presentation. Each presentation style has different
rules governing its behavior. For example, a full-screen presentation
must be made by a view controller that itself covers the entire
screen. If the current view controller is unable to fulfill a request,
it forwards the request up the view controller hierarchy to its
nearest parent, which can then handle or forward the request.
In case of iPhone (horizontally compact environment) it uses FullScreen mode by default. So here ViewController1 may not be the 'presentingViewController'.
[Spoiler - Work-around]
I had a similar issue with a modal vc and ended up working things out differently.
My modal style was initially:
modalVC.modalPresentationStyle = .overCurrentContext
and changing that to
modalVC.modalPresentationStyle = .fullScreen
triggers the life cycle calls to fire on the presentingVC when I dismiss the modal so that did the trick for me. I can now make sure code runs whenever the modal gets dismissed without having to bother with delegation etc...
I hope that can help someone too in the future.

ViewController Modal Presentation not appearing

I have a view controller that is a child view controller of my window's root view controller. That child view controller has a table view and when i select a row it tells the parent view controller to present a view controller modally. The modal view controller, however, never appears. I created a bare bones test view controller that just prints viewDidLoad and viewWillAppear. What I notice is that when I call parentVC.present(testVC, animated:true, completion:nil), viewDidLoad is run, but viewWillAppear is not. viewWillAppear is only then called when I interact with the UI in some way. Whether tapping, panning, scrolling or whatever.
I've spent hours trying to debug this. It doesn't seem like the main queue is blocked and I've reduced the problem to its bare bones. The modally presented view controller's viewWillAppear is simply not called until I interact with the UI again.
What could be causing this symptom?
In comments, you mention that you're instantiating your view controller with
let vc = TestVC()
where TestVC is presumably a (largely empty) UIViewController subclass.
A view controller needs a view created either via either storyboard scene (using instantiateViewController), a NIB or, in very rare cases, a view you create in loadView (which you shouldn’t be confused with viewDidLoad).
I’d suggest creating a storyboard scene (assuming you are using storyboards), give it a storyboard ID, and then use instantiateViewController:
let vc = storyboard.instantiateViewController(withIdentifier: "foo")
But just having a UIViewController subclass called TestVC and instantiating it with TestVC() won’t work.
In our discussion, you said you wanted to do this programmatically with no NIB nor storyboard. If so, use loadView. See https://stackoverflow.com/a/37964249/1271826 for an example.

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