I have declared an array in viewcontroller.h:
#property (nonatomic, strong) NSMutableArray *array;
In viewcontroller.m I put this code inside the implementation of the controller:
#synthesize array = _array;
In viewDidLoad-method I have this at the beginning:
if (_array == nil){
NSLog(#"arr was null");
_array = [[NSMutableArray alloc] init];
}
After the code above, I add an object to the mutable array this way:
[_array addObject:#"1"];
However, when I go from a controller to another in the simulator, the _array is always nil which allocate the array from scratch and all the old values disappear. What am I doing wrong?
Updated:
Im not using any navigation controller. Im using modal push. In ctrl2 I have override the method prepareForSegue putting this relevant code:
ViewController1 *ctrl1 = segue.destinationViewController;
ctrl1.anothervar = 3;
The real "redirection" to ctrl1 is done inside the method "tableView" in ctrl2 (when a use select an option). The code looks like this:
[self performSegueWithIdentifier:#"select_game_to_ctrl1" sender:self];
The ctrl1 has been given the identifier "select_game_to_ctrl1" (through storyboard)
Your problem is the way you are navigating.
If you go from VC Ctrl1 to ctrl2 with a segue, you should NOT return from ctrl2 back to Ctrl1 via another normal segue. What that does is to create a new, blank instance of Ctrl1 and add that on top of your other view controllers. Since you create a new instance of Ctrl1, its viewDidLoad method gets called, and it creates a new, different array. The other copy of Ctrl1 is still there also, covered by ctrl2 and the new instance of Ctrl1 you just created.
How to fix it depends on what kind of segue you're using. If you're going from Ctrl1 to ctrl2 with a push segue (managed by a navigation controller), you should set up the return from ctrl2 back to ctrl1 as a pop.
If you've going from Ctrl1 to ctrl2 via a presentModalViewController call, then you want to go back using dismissViewControllerAnimated:completion:.
The third option is to set up an unwind segue, but that's not really needed/appropriate when you're simply returning directly to the view controller you just came from.
If you need more help then tell us what kind of segue you are using to get from Ctrl1 to ctrl2, and post your prepareForSegue method from Ctrl1.
EDIT: The solution to your problem is in bold above.
Related
I have 2 view controllers . First one is UIview controller and second one is table view controller.
I want to send data 2nd (table view controller) to first(uiview Controller) after the selection of rows of 2nd view controller.
For this i have written a delegate protocol.
But my delegate protocol is not working...
I figured out the problem.The object of second view controller that i am creating.
address = [[second_viewcontroller alloc] init];
address.delegate = self;
is different from self of second_viewcontroller view controller page.
How to make this two same.
self = [[second_viewcontroller alloc] init];
your problem my delegate protocol is not working... I figured out the problem.The object of second view controller that I am creating. address=[[second_viewcontroller alloc]init]; address.delegate=self; is different from self of second_viewcontroller view controller page.
It's clear say that way you create the second_viewcontroller object is not right.
You have to create the object from ViewController storyboard identifier.
First give the Storyboard ID to ViewController from Storyboard.Follow this step to Giving the Storyboard ID.
Select the particular ViewController in Storyboard.
Go to IdentityInspector.
Under IdentityInspector, There is identity section and add the Storyboard ID In "Storyboard ID" Field.
Syntax For Creating a ViewController Object.
Second_viewController *aVC = [self.storyboard instantiateViewControllerWithIdentifier:#"Second_viewController"];
aVC.delegate = self;
I assume that you are calling the Second_ViewController from storyboard instead of doing programmatically.
In that case, the correct instance of Second_ViewController can be accessed in prepareForSegue. For that, you need to set a Storyboard segue identifier, eg "Second_ViewController"
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"Second_ViewController"]) {
SecondViewController *aSecVC = segue.destinationViewController;
// Register the Delegate to self.So when we call the delegate method from secondVC, SendMessage will be call of ViewController
aSecVC.delegate = self;
}
}
If you use alloc-init or instantiateViewControllerWithIdentifier, when you are using a storyboard push segue, it will create another instance.
Yes as your instantiating a new instance of the second view controller. From what I could make out from your question, I guess if you obtain the instance of your secondViewController from the Navigation Stack it should work
I created a sample project for you to get the basic knowledge of how to pass data backward using NSUserDefaults. try this in GitHub. hope this will help to you. project url Pass data backward using NSUserDefaults in Objective-C
I feel ignorant for asking this because I know it's simple. Goal: Retain a variable on a view after leave then returning to it.
For instance: Let's say we have MainView, CategoryView and (drumroll) ProjectView
Application opens to MainView it displays a table - user selects they want to pick a category. This segue's them to CategoryView. Once a selection is made I send the chosen category back to MainView. via
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
No problem here. MainView receives variable and displays the chosen category as subtext in the 'Category' section. Next the user wants to pick a project name (this is also a predefined list) They select the item and I send the variable back the MainView.
-- Now can somebody explain to me (gently) why when I return to the main view the NSString variable that was previously holding the 'chosen' category is now null?
So my NSString selectedProject is not being retained correct? What is the correct implementation I should be doing here? Or what am I missing? I'm really trying to understand whats going on so anything would be a great help.
MainView Interface
#interface MainViewController : UIViewController {
NSString *selectedProjectName;
NSString *selectedCategory;
}
#property (nonatomic, retain) NSString *selectedProjectName;
#property (nonatomic, retain) NSString *selectedCategory;
#end
MainView Implementation
#implementation MainViewController
#synthesize selectedCategory, selectedProjectName;
and if you need it..
ProjectView Implementation
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"masterSegue"]) {
MainViewController *vc = (MainViewController *)[segue destinationViewController];
LogValues *lv = [LogValues alloc];
lv.project = #"Test Project Name";
vc.selectedProjectName = lv.project;
}
}
Ok, your problem is that you're not returning to the same instance. You've missed one very very important thing about segues -- they ALWAYS instantiate new view controllers. So, when you use a modal segue to go "back" to the main view controller, you're just creating a new instance of that controller. You should never go backwards (to earlier controllers) in a storyboard using anything other than an unwind segue. Unwinds are the exception to the rule about segues always creating new instances.
So, you have 2 choices. You can either use an unwind segue, which is nice, because you can still implement prepareForSegue to send data back to the destination controller, or you can not use a segue at all, and use dismissViewControllerAnimated:completion: to go back, and use a delegate to send back the data.
So of course immediately after posting I see a thread in the side that I believe helps. The solution was to store the values in the AppDelegate which is working fine.
I feel however there are other ways to achieve the same result without having to rely on the AppDelegate and would love to hear other solutions.
I am using Storyboard in my app and I want to pass data from one view to another view.
Instead of using segues I am using instantiateViewControllerWithIdentifier. In this case I am instantiate from my first TableViewController to a NavigationController which has a second TableViewController attached because I need the navigation in the second TableViewController. Now I want to pass data from my first TableviewController, depending which row was clicked, to my second TableviewController. In this case newTopViewController would be my NavigationController but my problem is now how to pass data from firstTableViewController to the secondTableviewController.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *identifier = [NSString stringWithFormat:#"%#Top", [menuArray objectAtIndex:indexPath.row]];
UIViewController *newTopViewController = [self.storyboard instantiateViewControllerWithIdentifier:identifier];
}
If you instantiate a navigationController, you can use the viewControllers property to get the inner viewController of the navigation controller.
Something like this:
UINavigationController *navigationController = [self.storyboard instantiateViewControllerWithIdentifier:identifier];
MBFancyViewController *viewController = navigationController.viewControllers[0];
// setup "inner" view controller
viewController.foo = bar;
[self presentViewController:navigationController animated:YES completion:nil];
newTopViewController.anyVariableToShow= anyVariableToSend;
I do this pretty often on a few of my apps...
//Create new VC
CookViewController *detailViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"CookVC"];
//Set recipe
[detailViewController setRecipe:recipe];
//Pop over VC (can be pushed with a nav controller)
[self presentPopupViewController:detailViewController animationType:MJPopupViewAnimationFade];
If you aren't using a navigation controller or segues, then I think you need to reconsider your app design.
Actually it's not just a data pass problem as this is a program control and data transfer question together.
Even you would have to rethink about your app's concept, as you'd like to use storyboard without the meaning of storyboard, it's up to you and I hope you have good reason to do what you do.
So when you decided not to use segue you lost the new and comfortable way of instantiating a new controller and transferring data with it and you have to do the transfer of control and the data in two distinct steps. When you instantiate another scene in storyboard (like you do with instantiateViewControllerWithIdentifier:) you just instantiated a new controller and transferred the control but not the data. Just think about it as you instantiated a new controller from a xib in an old way (so you have to use initWithCoder: or awakeFromNib in the second view controller as the storyboard will not call initWithName:bundle:), but did not do anything more.
So you will have a new controller (it named in the identity part of the second storyboard) which is hanging in the universe without any relationship or connection with anything else (as the storyboard picture illustrates it nicely) and you could do with it what you'd like.
So you'd like to do something and you need data from the previous storyboard (ViewController). What you need is making available those data to the second storyboard(ViewController), and as you know there are lot of solution for this which were available long time before even storyboard is existed.
So regarding your code, the "data transfer" is depending on your design, whether the two controllers are subclasses of each other or whatsoever...
If you don't like to deal with subclassing and like to decoupling them as much as possible, the best way just make a property of your data in the first controller and refer to them from the second (after importing the first's .h file) and just refer to it in it's viewDidLoad or in initWithCoder: or anywhere where you need them, as
secondViewControllerdata = firstViewControllerdata.thatDataProperty
Of course you can do the same in reverse and make a property of the second controller and refer to it in your first view controller.
You can define some parameter in UIViewController to receive data:
#property (assign) int param1;
#property (retain) NSMutableArray *param2;
and use below to pass the data:
[newTopViewController setParam1:XX];
[newTopViewController setParam2:XX];
I've made a tabbar controller with two views (standard setup from the template). First view is accessing an array within an dataController object. When I tap to the second view on the tabbar, I want the dataController reference to be set on the second viewController's dataController property.
I use call
SecondViewController *vc = [[self storyboard] instantiateViewControllerWithIdentifier:#"SecondViewController"];
vc.dataController = self.dataController;
However, when I call my countArrayObjects on the secondViewController's dataController property, I always get 0 back - eventhough there should be objects in the array and I can't figure out why?
I've set the identifier on the secondViewController in MainStoryBoard and I have no crashes, the property just isn't set (I think).
For testing purposes I call the instantiateViewControllerWithIdentifier from a simple button, before tapping on to the secondView. And I call the countArrayObjects from viewDidLoad on secondViewController.
Any ideas?
Hmmm... I was having the same problem. At the moment, the only way I got around this is to test for the views superview being nil - which as its accessing the view selector of the class fires up the view did load event...
both of us are doing something wrong here though...
This is the test code I wrote...
SummaryViewController *sc = [self.childViewControllers objectAtIndex:0];
if(sc.view.superview == nil) sc.checkInTime.alpha = 0;
//-- by inspecting the value, it will call the viewDidLoad method of the controller.
It works, but it's wrong.
Here's what I've got:
I have a root view controller with an array of images. I have a detailViewController which simply has a fullscreen UIImageView for displaying a full screen image. I also have a segue defined that presents the detailViewController. This segue is programmatically invoked when the user selects an image in the rootViewController like so:
- (void)carousel:(iCarousel *)carousel didSelectItemAtIndex:(NSInteger)index
{
[self performSegueWithIdentifier:#"fullScreenSegue" sender:self];
}
The problem is that the detailViewController has an image property that it sets as the image for the UIImageView it displays and I need a way to pass the image to it to display. How do I do this? According to apple docs, I should implement the prepareForSegue method in the calling controller, but at that point, I no longer have access to the index above. The images are in an array in the root view controller and I need that index to know which image to pass to the detailViewController. I also set the detailViewController to have a protocol that sets the calling controller as the delegate and then calls a "setImage" method in the init() method, but that doesn't even seem to work and seems like a crazy implementation anyway...
I thought about declaring a property in the rootViewController like #property int selectedIndex, then setting that property in the above method and then in the prepareForSegue method doing something like:
detailViewController.imageForDisplay = [images objectAtIndex:selectedIndex];
That would probably work, but that approach rubs me wrong...
Thanks in advance for the help!
Two ways to handle it. One use prepareForSegue and save your index somewhere before you call the segue...
self.selectedIndex = index;
[self performSegueWithIdentifier:#"fullScreenSegue" sender:self];
then in the prepareForSegue use:
if([[segue identifier] isEqualToString:#"fullScreenSegue"])
{
DetailViewController *detailViewController = segue.destinationViewController;
detailViewController.imageForDisplay = [self.images objectAtIndex:self.selectedIndex];
}
Second way is to use your delegate and protocol, but make the setImage call in your viewDidLoad method, not in your init.
Hope that helps. It seems like you were getting pretty close.