I have an app created from Xcode 4.5's boilerplate Master-Detail Application with CoreData, StoryBoards and ARC turned-on that is not calling prepareForSegue. CoreData isn't currently being used, but it will be to cache XML responses. performSegueWithIdentifier works, but prepareForSegue isn't and I'm having troubles passing/access data from the MasterViewController to/in the detailViewController created by performSegueWithIdentifier.
My basic set-up is similar to what's discussed in the thread: Storyboards and UISplitViewControllers. There's an image of a storyboard on page three that's very similar to my set-up (I don't have enough rep to post images).
In a nutshell:
I create a standard splitView arrangement
The MasterViewController builds the main table
Each cell corresponds to a URL that returns XML data that determines what's in the detailView
The fetched XML is parsed using a NSXMLParser operation/class
The fetched XML determines which detail view is needed and the MasterViewController calls the appropriate 'replace' segue (via performSegueWithIdentifier) to kick-off the corresponding detailViewController to display the fetched XML
The problem that I'm having is that prepareForSegue isn't being called, so I can't pass the fetched XML to the detailViewController.
What I need is one of the following:
prepareForSegue to be executed
a way to know segue.destinationViewController inside handleLoadedResponse:notif
a way to get access to the "currentResponse" variable in the MasterViewController from inside the viewDidLoad method of the detailViewControllers
a way to find the MasterViewController via its StoryBoardID
What I've tried:
Putting NSLog() statements in each viewController's prepareForSegue -- none of them are called
Digging through the self.parentViewController chain in the detailViewController to find the MasterViewController that called performSegueWithIdentifier -- I can't find the class variable I'm looking for
Read pretty much every "prepareForSegue not called" post I could find -- They all seem due to some coding/storyboard error that I don't see in my code/storyboard
Could the problem be that I'm calling:
[self.navigationController performSegueWithIdentifier:#"theDesiredSegue" sender:self];
from inside the handleLoadedResponse:notif call-back and the app is trying to call prepareForSegue in my parsing object?
TIA
Ray
Well, chalk this up to staring at the code too long and/or newbie-ness.
After triple-checking everything that I though was obvious, I thought the only thing left was that the segue code didn't behave correctly inside the NSXMLParser call-back, so I switched the handleLoadedResponse:notif routine to store the parsed data and send-out notifications.
While debugging those changes, I realized that my segues were attached to the wrong object. They were actually attached to the navigationController and not my viewController. Visually, things looked like the story board on page three of RWForums: Storyboards and UISplitViewControllers, but they really weren't. The end result being that even though it seemed like my code was calling performSegueWithIdentifier and that it's prepareForSegue method should have been called, it was actually calling the navigationController's segue, so the navigationController's inherited prepareForSegue was being called.
Recommendation: Make sure you re-re-re-check all the connections in the StoryBoard Editor with the Document Outline open.
Ray
Related
So I am trying to get from one viewcontroller to another using the push segue (selected in the storyboard). When I arrive at the second viewcontroller, the viewDidLoad is not firing! Is that a bug or am I doing everything wrong?
It really hard to give suggestion to this kind of question,
Double check below things once.
Is Storyboard View-controller has the Class as the SecondViewController.h
Is the 'View' connected to the outlet as the Seccndviewcontrooler's View.
Performing segue are you sure pushing the SecondViewcontroller only.
Clean build the proj once.
just run the app - Place a NSLog Statment in right after the [super viewDidLoad] and see is that printing logs in console.
try keeping a breakpoint or NSLog at viewWillAppear/viewdidAppear.
What do you mean by pushed to new view? ViewControllers can be pushed not View. ViewDidLoad will be fired only when a ViewController is loaded into the memory. It can also be fired if the VC was unloaded by iOS runtime and is being loaded again.
But thumb rule is that it will fire only when the VC is being loaded not afterwards. Please elaborate your question further if I am interpreting you incorrectly.
In my app I have a manually triggered push segue called "details". It is wired from the source controller to the destination controller and it's identifier is set. In my code I call
[self performSegueWithIdentifier:#"details" sender:sender];
I can see the prepareForSegue function firing and have verified that the destination controller is of the proper type. I pass the destination controller a few bits of data it needs to display correctly. Oddly enough NOTHING happens. The app does not go to the destination controller.
Here are some things to look out for if you are having an issue with performSegueWithIdentifier while using a storyboard
You have correctly hooked up your transitions in your Storyboard
Your Segue Identifier matches the one on your Storyboard
Your ViewController's class has not become deallocated
(This can sometimes occur accidentally by calling from another class)
Try setting sender to "self" rather than sender
Also, if you are planning on passing data between ViewControllers it is suggested that you use the prepareForSegue method, allowing you to pass values before segue'ing.
I'm using SWRevealViewController in my app (to get the slide out side panel) however whenever the user navigates to another viewController like 'settings' and comes back, everything gets reset. I understand this is normal behaviour for storyboards since a new VC is instantiated and viewDidLoad is called each time. I tried to get around this by storing the VC in an array in the AppDelegate and then going back to the original viewController, this prevented viewDidLoad being called when the original VC is initially re-presented but I still found it get's called randomly when moving between veiwcontroller's, resetting all my properties etc.. On researching, the Apple documentation does say not to assume viewDidLoad will only be called once.
Is this behaviour apparent in UITabBarController when switching tabs as I'm thinking of ditching the SWRevealViewController and using that instead if it's going to be less headache.
Should I be handling this differently, ie. storing the 'state' in NSUserDefaults and restoring on viewDidLoad?
Thanks in advance.
viewDidLoad is called exactly once, when the UIViewController is first loaded into memory. This is where you want to instantiate any instance variables and build any UIViews that live for the entire lifecycle of this UIViewController.
In UITabBarController also the viewdidLoad for UIViewController is called once, when you are switching tabs.
viewDidLoad() method is called only once. Its an integral part of the cycle.
It is called then the respective UIViewController class is loaded into memory.
And yes, if you want to initialise any properties or access and modify the NSUserDefaults, it can and should be done in the viewDidLoad method.
As for your app, whenever the user will switch between different UIViewControllers, the viewDidLoad method will be called for every destination UIViewController.
Also, as correctly pointed out, it'll also be called in the case of a memory warning.
I am making simple storyboard application which has 2 UIViewControllers and I am switching between them by using modal segue. Each UIViewController has one UIButton which is used to perform segue to another UIViewController. In viewDidLoad method I animate appearance of that UIButton on each UIViewController. I am using Cross Dissolve Modal segue.
When I press UIButton on 1st UIViewController I navigate to second UIViewController and animation is executed and 2nd UIViewController is shown. After I press UIButton on 2nd UIView Controller, first UIViewController is shown and it's animation is executed. Everything looks fine and viewDidLoad methods are called for each UIViewController when ever I navigate to it. And that's great.
I tried now to change Modal segue type from Cross Dissolve to other two by default offered in XCode Interface Builder. I changed to Cover Vertical, and everything worked just fine, without changes. But when I tried Flip Horizontal Modal segue, I saw a problem.
When performing Flip Horizontal Modal segue, my UIButton on both UIViewControllers is shown, but animation isn't executed. I tried debugging and I am sure that animation commands are being executed, but animation isn't shown.
So that's my first question: Does anyone know is there any difference between these types of Modal segues which may cause my animation not showing up?
Other questions are related to basic theory of segues and memory management. When I perform segue and navigate to some UIViewController, viewDidLoad method is called every time. So, does that mean I created new object instance each time viewDidLoad method was executed?
I also notice that viewDidUnload method is never called. So, if answer to previous question is affirmative (each viewDidLoad execution creates new object instance), does that mean that my UIViewController object instances are never being unloaded and deleted? Or ARC is doing garbage collection behind the scenes?
If someone could explain how things works with storyboard segues and memory management/object lifecycle and why viewDidUnload method is never being called, I'd be very grateful.
[edit #1: Trying to unload UIViewController after performing modal segue]
[update #1: This shouldn't be done, viewDidUnload will be called automatically]
I am making segue in IBAction attached to UIButton click. I have written this peace of code to perform modal segue.
#try
{
[self performSegueWithIdentifier:segueToPerform sender:self];
}
#catch (NSException *exception)
{
NSLog(#"Exception: %#", exception);
}
#finally
{
[self viewDidUnload];
}
I have manually called viewDidUnload in #finally block and I have checked weather viewDidUnload is called in runtime and yes - it is called.
Does this mean I managed to unload my UIViewController object I created when navigating to it with modal segue from another UIViewController and remove it from memory?
Is this method regular as a replacement for:
[self dismissViewControllerAnimated:YES completion:nil];
because this above line returns me to UIViewController from which I navigated to current UIViewController, but that doesn't fit my needs, because I need to perform new segues from current UIViewController to other UIViewControllers (beside returning back to UIViewController from which I navigated to current one)?
[edit #2: Finish]
At the end I changed implementation model and loaded new UIViews under single UIViewController after I created separate XIB files for those UIViews. I have marked answer from #dasblinkenlight as the right one since it contains lots of useful informations and discussion on that answer gives good answers to some doubts about using modal segues.
I do not know the answer to the first part of your question, but once you learn the answer to the second part, I am sure that you would go with a different solution anyway.
viewDidLoad method is called every time. So, does that mean I created new object instance each time viewDidLoad method was executed?
Absolutely. "Modal" segue causes the new view to obscure the old one completely until the new view is closed. If you go back and forth many times, your code will accumulate a whole "stack" of views underneath the current one.
I also notice that viewDidUnload method is never called. So, if answer to previous question is affirmative (each viewDidLoad execution creates new object instance), does that mean that my UIViewController object instances are never being unloaded and deleted?
This is correct, all the view controllers that you create are still there, ready for you to close the views on top of it.
Or ARC is doing garbage collection behind the scenes?
ARC is not a garbage collector, it is a reference counting mechanism with a little automation from the compiler. The objects are still there.
You should change your code to call
[self dismissModalViewControllerAnimated:YES];
in the second controller, rather than using a modal segue that brings you back to the first one.
I hit a similar issue where I had a complex VC navigation web, and the resulting use of segues (no UINavigationController) was sucking up too much memory.
You may want to take a look at this question/answer to see my final solution.
The conversation here between uerceg and dasblinkenlight helped me in finding this solution.
Segues and clearing historical ViewControllers from memory
I may go mad very soon.
This is the reason:
- I started up with Single View Application project with storyboards. Then I set the view controller class name in the storyboard for my viewController.
- Next step I created one pointer for this viewController in AppDelegate method ...didFinishLaunchingWithOpt... and filled it up by calling [myStoryboards instantiate...]. It works pretty good because I can call method like [vc1 setMyName] which does smthng like self.myName = #"Johnny";
- But here it comes. When I create IBAction method joined with button, this method doesn't know anything about "Johhny". And this is because I'm in another instance. When I check the address of "self" it is another one...
WhyWhyWhy??? Please help, how can I use still the same object - the one instantiated in AppDelegate by storyboards and the one from storyboards in "interface builder".
Thank you.
Oh my. I think I really underestamated it...
When we were talking about getting pointer of other viewControllers from storyboard...
I have initialViewController got by calling rootViewContr... And another one connected with segue (modal) where is UITableView. A get data on rootViewController and I want to show them on the other one in the list (UITableView). So I call segue (performSegueWithIdentifier), the other controller is shown but the list is clear. Because the method I call is working with tableView variable which is null :/ Because, again, I'm in another object. That is because I call that method storyboard instantiate... How can I get exactly the same viewController which I'm working in storyboard with. It is quite confusing for me :/
I read something about prepareForSegue and getting the pointer by destinationViewController but that is not what exactly I want. I need the pointer before I call segue method and the viewController is shown...
Thank you.
If you've set up your initial view controller properly in the storyboard, you don't need to assign it to the windows rootViewController property in -applicationDidFinishLaunchingWithOptions: as this is done for you automatically. It sounds like you're creating a second instance. To access the original instance setup by the storyboard simply do this in -applicationDidFinishLaunchingWithOptions:
InitialViewController *viewController = (InitialViewController *)self.window.rootViewController;
viewController.myName = #"Johnny";