viewdidload is not calling - ios

I have a view controller which is on my story board. The following is the code for view controller;
class SingleLineGraphController: UIViewController {
#IBOutlet var lineGraph: LineGraphView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// Mark: GraphDelegate implementation
func plotLineGraph(xAxisValue:[NSDate],yAxisValue:[Double],displayView:GraphDisplayView, graphTitle:String,graphStartDate:NSDate , graphEndDate:NSDate)
{
lineGraph.plotLineGraph(xAxisValue, yAxisValue: yAxisValue, displayView: displayView, graphTitle: graphTitle, graphStartDate: graphStartDate, graphEndDate: graphEndDate)
}
}
now i am accessing this view controller from another view like this;
let mainStoryboard = UIStoryboard(name: "Menu", bundle: NSBundle.mainBundle())
let singleLineGraphController : SingleLineGraphController = mainStoryboard.instantiateViewControllerWithIdentifier("SingleLineGraphController") as! SingleLineGraphController
let graphData = getGraphData(DashBoardRow.Respiratory, cellTitle: "Respiratory") as! LineGraphModel
singleLineGraphController.plotLineGraph(graphData.xAxisValue, yAxisValue: graphData.yAxisValue, displayView: graphDisplayView, graphTitle: graphData.cellTitle, graphStartDate: graphData.graphStartDate, graphEndDate: graphData.graphEndDate, latestReadingText: graphData.latestObservationText, latestReadingDate: graphData.latestObservationDate)
self.navigationController?.pushViewController(navigationController, animated: false)
The problem is when i instantiate the SingleLineGraphController from story board it doesn't call the viewdidload and hence the lineGraph becomes nil until the point where i call
singleLineGraphController.plotLineGraph(graphData.xAxisValue, yAxisValue: graphData.yAxisValue, displayView: graphDisplayView, graphTitle: graphData.cellTitle, graphStartDate: graphData.graphStartDate, graphEndDate: graphData.graphEndDate, latestReadingText: graphData.latestObservationText, latestReadingDate: graphData.latestObservationDate)
and hence it gives me an exception. I have commented out that line and put a breakpoint on viewdidload and find out that once the below line is executed , it loads the lineGraph.
self.navigationController?.pushViewController(navigationController, animated: false)
Does anyone have any idea how can i force the viewdidload before the above line so that my method doesn't crash.

In your line:
self.navigationController?.pushViewController(navigationController, animated: false)
You are pushing a navigationController, not your singleLineGraphController. Are you sure this is correct or did you leave some code out?
Even if you present the controller correctly, because you trying to plot your data before the view is presented the view is not accessible yet. One way to correct this is to set your data to plot on the controller as a property and only plot that data in your viewDidLoad when you know for sure the view is available.
You could access the view property to force the view to load before the controller is actually presented but this is a wrong solution to this problem.

if you will not push your VC to NavigationController, its viewDidLoad will not called
viewDidLoad will only called before coming on to the screen,
Then viewWillAppear and then viewDidAppear.
VC will only come on to the screen if you push or present or set it as rootVC of NVC and load that NVC as rootVC of window or set it directly to rootVC of window

Just call:
singleLineGraphController.view
After this line:
let singleLineGraphController : SingleLineGraphController = mainStoryboard.instantiateViewControllerWithIdentifier("SingleLineGraphController") as! SingleLineGraphController

Related

presenting whose view is not in the window hierarchy warning

I am new in Programming and swift, and I have tried to read some solutions in stack overflow, but to be honest I don't really grasp with the answer :(
I have 2 view controllers. a homeVC and a LoginVC. homeVC is my initial view controller. in viewDidLoad I have firebase function that can check if the user has logged in before or not. if not, then the user will be send to loginVC. here is my simplified code in the HomeVC
import UIKit
import Firebase
class HomeVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// to check whether the user has already logged in or not
Auth.auth().addStateDidChangeListener { (auth, user) in
if user == nil {
let login = self.storyboard?.instantiateViewController(withIdentifier: "login") as! LoginVC
self.present(login, animated: true, completion: nil)
}
}
print("user enter homeVC")
}
}
and here is my loginVC
import UIKit
import Firebase
import GoogleSignIn
class LoginVC : UIViewController, GIDSignInUIDelegate {
#IBOutlet weak var googleButton: GIDSignInButton!
#IBOutlet weak var emailButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// delegate declaration
GIDSignIn.sharedInstance().uiDelegate = self
}
#IBAction func googleButtonDidPressed(_ sender: Any) {
GIDSignIn.sharedInstance().signIn()
}
}
the app can perform as I expected. but there is a warning in my debugging area :
Warning: Attempt to present LoginVC: 0x7fc315714f40 on
HomeVC: 0x7fc3155095c0 whose view is not in the window
hierarchy!
of course the problem is in this lines of code
let login = self.storyboard?.instantiateViewController(withIdentifier: "login") as! LoginVC
self.present(login, animated: true, completion: nil)
as far as I know, if the view is stacked in the layer of navigation controller, then if I want to move to another view controller I have to use perform segue method.
But for this case, between homeVC and LoginVC are not stacked in the same navigation controller. so no hierarchy. thats why I use that line of code to move to another view controller (loginVC). but I don't understand why it is said "view is not in the window hierarchy!"
So what should I do to omit that warning?
Move code to viewDidAppear
override func viewDidAppear(_ animated:Bool) {
super.viewDidAppear(true)
// to check whether the user has already logged in or not
Auth.auth().addStateDidChangeListener { (auth, user) in
if user == nil {
let login = self.storyboard?.instantiateViewController(withIdentifier: "login") as! LoginVC
self.present(login, animated: true, completion: nil)
}
}
print("user enter homeVC")
}
Your LoginVC is perfectly fine.
However, you need to change your HomeVC as #Sh_Khan suggested and move the testing code from viewDidLoad to viewDidAppear:
import UIKit
import Firebase
class HomeVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
// HomeVC.view was added to a view hierarchy
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// to check whether the user has already logged in or not
Auth.auth().addStateDidChangeListener { (auth, user) in
if user == nil {
let login = self.storyboard?.instantiateViewController(withIdentifier: "login") as! LoginVC
self.present(login, animated: true, completion: nil)
}
}
}
}
Explanation
Your viewDidLoad method gets called before the viewController gets presented, so it at that moment it cannot really present another view controller (since it itself is not presented), viewDidLoad documentation:
Called after the controller's view is loaded into memory.
This method is called after the view controller has loaded its view hierarchy into memory. This method is called regardless of whether the view hierarchy was loaded from a nib file or created programmatically in the loadView() method. You usually override this method to perform additional initialization on views that were loaded from nib files.
In that moment the viewController is not in the window hierarchy yet.
viewDidAppear however gets called when the view is presented and becomes a part of the window hierarchy, viewDidAppear documentation:
Notifies the view controller that its view was added to a view hierarchy.
You can override this method to perform additional tasks associated with presenting the view. If you override this method, you must call super at some point in your implementation.
Don't forget to call super.viewDidAppear during overriding it.
TLDR; You should move your code to viewDidAppear
viewDidLoad()
This method is called after the view controller has loaded its view hierarchy into memory. This method is called regardless of whether the view hierarchy was loaded from a nib file or created programmatically in the loadView() method. You usually override this method to perform additional initialization on views that were loaded from nib files.
Apple docs
So the view is only in memory yet and not in the hierarchy. You should move it to viewDidAppear
viewDidAppear()
Notifies the view controller that its view was added to a view hierarchy. Apple docs
As Sh_Khan said, move the lines:
let login = self.storyboard?.instantiateViewController(withIdentifier: "login") as! LoginVC
self.present(login, animated: true, completion: nil)
in viewDidAppear() method; when viewDidLoad() gets called, the view controller it's not added on the view hierarchy yet, it's not visible and it cannot present another view controller.
The reason for such kind of error is: You are trying present (open) two view controllers simultaneously (view of first presenting view controller is just started and you may be trying to present second view controller).
You should move your code (for view controller presentation/navigation) to viewDidAppear. Your main view of existing view controller (from where you are presenting new view controller) is not ready/loaded.
You should move it to viewDidAppear.
Here is sample code:
Swift 4
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// instantiate your view controller either using storyboard or class or any other way....
if let newVC = self.storyboard?.instantiateViewController(withIdentifier: "NewViewController") as? NewViewController {
self.present(newVC, animated: true, completion: nil)
}
}
In your case/code, solution is:
override func viewDidLoad() {
super.viewDidLoad()
// Move your code from here (viewDidLoad) to viewDidAppear
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// to check whether the user has already logged in or not
Auth.auth().addStateDidChangeListener { (auth, user) in
if user == nil {
let login = self.storyboard?.instantiateViewController(withIdentifier: "login") as! LoginVC
self.present(login, animated: true, completion: nil)
}
}
print("user enter homeVC")
}
Look at the difference between both view controller life cycle.
viewDidLoad
This method is called after the view controller has loaded its view hierarchy into memory. This method is called regardless of whether the view hierarchy was loaded from a nib file or created programmatically in the loadView() method. You usually override this method to perform additional initialization on views that were loaded from nib files.
See more about: viewDidLoad
viewDidAppear
Notifies the view controller that its view was added to a view hierarchy.
See more about: viewDidAppear
At this point in your code, the view controller's view has only been created but not added to any view hierarchy. If you want to present from that view controller as soon as possible you should do it in viewDidAppear to be safest.

Getting a signal SIGABRT when using instantiateViewController with UIVewController

I hope someone can help me with this problem I am getting with swift.
I am trying to add an array of UIViewControllers to a UIPageViewController. However, whenever I try to access a view controller through the method instantiateViewController, I get a SIGABRT error.
let vc: UIViewController = self.storyboard?.instantiateViewController(withIdentifier: views[index]) as! UIPageViewController
Here is my entire ViewController.swift file just for the reference.
import UIKit
class ViewController: UIViewController {
var pageViewController: UIPageViewController!
var views: [String] = ["view1","view2"]
override func viewDidLoad() {
super.viewDidLoad()
self.pageViewController = self.storyboard?.instantiateViewController(withIdentifier: "pageViewController") as! UIPageViewController
var arr: [UIViewController] = []
for i in 0..<views.count{
arr.append(assignView(index: i))
}
self.pageViewController.setViewControllers(arr, direction: .forward, animated: true, completion: nil )
self.addChildViewController(self.pageViewController)
self.view.addSubview(self.pageViewController.view)
pageViewController.didMove(toParentViewController: self)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func assignView (index: Int) -> UIViewController{
let vc: UIViewController = self.storyboard?.instantiateViewController(withIdentifier: views[index]) as! UIPageViewController
return vc
}
}
Can someone tell me why my code is throwing me this error?
Thank you so much!
From the line below:
self.storyboard?.instantiateViewController(withIdentifier: "pageViewController") as! UIPageViewController
It seems that storyboard is treated as optional. It may be that the storyboard variable is getting nil when you are trying to access it.Ideally it should not be nil and should not be optional.
If the optional is purposefully done then you can apply a "if let" to check before you access.
Let me know if you need further clarification.
SIGABRT is a controlled crash and the app terminated mostly on purpose. It mostly resulted from programmer's fault.
Look for following as there is two possible causes to crash.
self.storyboard doesn't properly got initialised as there is no corresponding storyboard or incorrectly referred.
View controller's identifier is incorrectly referred.
Consider the following example
var mainView: UIStoryboard!
mainView = UIStoryboard(name: "Main", bundle: nil)
let samplecontroller : UIViewController = mainView.instantiateViewController(withIdentifier: "iPhone")
In the above example mainView is my instance referring to StoryBoard which is implicitly unwrapped(this is done on purpose to specify that this cannot be nil)
samplecontroller is the instance of the scene in the storyBoard. "iPhone" is the name of the StoryBoardID of the scene.
This snippet will ensure that the mainView will never be nil provided my storyboard is setup correctly with the Ids and names used.
After this you can still have a safety check like
if let test = mainView {
//Do your stuff
}
Let me know if this helps.

View hierarchy error while presenting a viewcontroller

I have a following class:
class HomeViewController: UIViewController {
override func viewdidload() {
super.viewdidload()
callOtherVC()
}
func callOtherVC() {
let viewController = StepsViewController()
let rootViewController = UINavigationController(rootViewController: viewController)
self.presentViewController(rootViewController, animated: true, completion: nil)
}
}
StepsViewController is just another viewcontroller. In StepsViewController, I try to dismiss current StepsViewController and present other viewcontroller. Following is code.
class StepsViewController: UIViewController {
override func viewdidload() {
super.viewdidload()
callSecondOtherVC()
}
func callSecondOtherVC() {
let vc = ViewController()
self.addChildViewController(vc)
self.parentViewController!.dismissViewControllerAnimated(true, completion: nil)
vc.callOtherVC()
}
}
I initialize ViewController() because I need to call same function callOtherVC from ViewController. Basically the model in ViewController changes but I'm essentially calling same UINavigationController from callOtherVC function.
Whenever I do this, I get an error like below:\
Warning: Attempt to present (UINavigationController: 0x7d991600) on
(HomeViewController: 0x7a6e00a0) whose view is not in the window
hierarchy!
UINavigationController is from callSecondOtherVC and HomeViewController is as it is.
How should I order the VCs? And if someone can more explain about the view hierarchy, I would greatly appreciate.
I think what you need to do here, is call your method from viewDidAppear, rather than viewDidLoad. The reason for this is that the view is not in the view hierarchy at the time of viewDidLoad.

iOS Storyboard: Not able to instantiate view controller properly

I'm really having trouble instantiating a custom view controller.
This is my storyboard set up. The third view controller is the one I'm trying to present.
I've tried both of these methods.
1: This results in black screen.
var searchController: SearchController = SearchController()
self.presentViewController(searchController, animated: true, completion: nil)
2: This results in a white, empty view controller popping up.
let mainStoryboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
let searchController : UIViewController = mainStoryboard.instantiateViewControllerWithIdentifier("searchController") as! UIViewController
self.presentViewController(searchController, animated: true, completion: nil)
Here is the code for the actual view controller:
class SearchController: UIViewController {
lazy var searchBar: UISearchBar = UISearchBar(frame: CGRectMake(0, 0, 200, 20))
override func viewDidLoad() {
super.viewDidLoad()
var leftItem = (UIBarButtonItem)(customView: searchBar)
self.title = "Search"
self.navigationItem.leftBarButtonItem = leftItem
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
It's very confusing because another custom view controller I am is presented properly while using method #1 above. Why wouldn't it work for this controller?
Thanks a lot everyone.
When using Storyboards you don't need to use presentViewController or instantiate your view controllers manually. Instead it's best to use a segue to move from one UIViewController to the next.
1. In your case, you want a show segue, which it looks like you've already done judging by your storyboard.
2. You need to give your segue an Identifier which you can do by selecting your segue and going to the Attributes Editor.
3. To perform your segue just call the following from your first view controller (instead of presentViewController).
self.performSegueWithIdentifier("YourIdHere", sender: self)
This will cause the storyboard to instantiate your SearchViewController and present it in the way that you've defined for that segue.
4. If you want to pass any values to your SearchViewController, you can override prepareForSegue in UIViewController. In your first view controller:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let searchViewController = segue.destinationViewController where segue.identifier == "YourIdHere") {
// Now you can set variables you have access to in `searchViewController`.
}
}
And that should be it!

Segue instantiated ViewController with instantiateViewControllerWithIdentifier

Is it possible to use segues in Views of ViewControllers instantiated with instantiateViewControllerWithIdentifier?
Here is a minimal example of what I'm trying to do (I need something like that in a biger project):
In the Main Storyboard I have a rootViewController, a secondViewController with the StoryboardID "secondViewControllerID" and a thirdViewController. The secondViewController is connected with the thirdViewController via button through a show-Segue.
In the class of the rootViewController I instantiate the secondViewController and set its view as subview of my rootViewController:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let storyboard = UIStoryboard(name: "Main", bundle: nil);
var vc = storyboard.instantiateViewControllerWithIdentifier("secondViewControllerID") as UIViewController
self.view.addSubview(vc.view)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
When I now execute the programm I see correctly the subview (secondViewController). But now the segue doesn't work anymore (when I click on the button I don't get to the thirdViewController)
Does anyone has an idea why?
(It is not possible in my project just to use the secondViewController without the instantiateViewControllerWithIdentifier because the secondViewController is part of a pageViewController that is managing its viewControllers via instantiateViewControllerWithIdentifier )
You are adding one view controller's view as a subview of another view controller. But by default, taps and gestures are handled by your main view controller and are not passed on to the subsidiary VC - hence your problem. There are some functions you need to call to get this to work. Take a look at the Apple Docs here.
Look at the section on "Creating Custom Container View Controllers". In summary, you need to wrap your addSubview method call with self.addChildViewController(vc) and vc.didMoveToParentViewController(self) function calls.

Resources