I am trying to do an ios app but I am stuck at passing data between classes .
This is my second app . The first one was done whit a global class , but now I need
multiple classes . I tried many tutorials , but did not work or the passed value was always zero . Can someone please write me a simple app to demonstrate passing of variables in IOS 5 .
Nothing special , storyboard whith 2 view controllers , one variable .
Thank you for your help .
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here. Create and push another view controller.
FirstViewController *fv;
fv.value = indexPath.row;
NSLog(#"The current %d", fv.value);
FirstViewController *detail =[self.storyboard instantiateViewControllerWithIdentifier:#"Detail"];
[self.navigationController pushViewController:detail animated:YES];
}
here is the code from my main view and i need to send the indexPath.row or the index of the cell that i pressed to the next view
There are several things to do. Depending on the app, you could either add a variable to the AppDelegate class, making it availible to all classes through a shared instance. The most common thing (I think) is to make a singleton. For that to work, you can make a class, say StoreVars, and a static method that returns the object, which makes the class "global". Within the method, you initialize all your variables, like you always would. Then you can always reach them from wherever.
#interface StoreVars : NSObject
#property (nonatomic) NSArray * mySharedArray;
+ (StoreVars*) sharedInstance;
#implementation StoreVars
#synthesize mySharedArray;
+ (StoreVars*) sharedInstance {
static StoreVars *myInstance = nil;
if (myInstance == nil) {
myInstance = [[[self class] alloc] init];
myInstance.mySharedArray = [NSArray arrayWithObject:#"Test"];
}
return myInstance;
}
This will make a singleton. If you remember to import "StoreVars.h" in your two viewControllers, you can access the now shared array like this;
[StoreVars sharedInstance].mySharedArray;
^
This is a method returning a StoreVars object. Within the StoreVars class, you can implement any object and initialize it in the static method. Just always remember to initialize it, or else, all your object will be 0/nil.
If you are not a fan of the UINavigationController and would rather use segues, it's a lot easier, but can make your app rather "messy" imo. There is a method implemented in UIViewController you should overload:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Make sure your segue name in storyboard is the same as this line
if ([[segue identifier] isEqualToString:#"YOUR_SEGUE_NAME_HERE"])
{
// Get reference to the destination view controller
YourViewController *vc = [segue destinationViewController];
// Pass any objects to the view controller here, like...
[vc setMyObjectHere:object];
}
}
source: How to pass prepareForSegue: an object
Do some research before asking questions like this. Read some tutorials, and try out yourself, and then ask questions related to what you are really looking for. It's not everyday people want to do all the work for you, but sometimes you're lucky. Like today.
Cheers.
if you use segue between 2 controllers you must overload prepareToSegue method
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// check if it's the good segue with the identifier
if([[segue identifier] isEqualToString:#"blablabla"])
{
// permit you to get the destination segue
[segue destinationViewController];
// then you can set what you want in your destination controller
}
}
The problem you face is quite perplexing for beginners. "Solving" it wrong way can result in learning a ton of bad habits.
Please have a look at Ole Begemann's excellent tutorial on Passing Data Between View Controllers - it's really worth reading.
Related
hey guys created a custom segue tab bar, using this guys tutorial,
http://www.scott-sherwood.com/tutorial/ios-5-creating-a-custom-side-tabbar-using-storyboards-and-custom-segues/
after trying to figure out why why my app doesn't work, i realised that the technique i was using was about replacing the existing view with the linked ViewController as a subview.
////////////////////////////////////////// the over-written perform method as follows /////////////////////////
-(void) perform {
ViewController *src = (ViewController *)[self sourceViewController];
UIViewController *dst = (UIViewController *) self.destinationViewController;
for (UIView *view in src.placeholderView.subviews ) {
[view removeFromSuperview];
}
src.currentViewController = dst;
[src.placeholderView addSubview:dst.view]; }
////////////////////////////////////////////// /////////////////////// /////////////////////// ///////////////////////
now once i am on the linked ViewControllers i was hoping to add another link to another ViewController which would hold the Editing functions for the information each respective pervious ViewControllers. Now when i try to connect a the ViewControllers via any Segue the app crashes and give me a Thread 1:EXC_BAD_ACCESS. When i use NSZombie its give me this in the console,
[UIStoryboardSegueTemplate performSelector:withObject:withObject:]: message sent to deallocated instance 0x7c3a4d20
no i understand what is happening in theory, the viewController is trying to ad the next one to an empty space (i think the entire placeHolder has been deleted thus giving the viewController nowhere to go) i think, i was wondering if anyone could help with this i mean i a have been looking everywhere for a solution but i keep getting the same error.
i even created a VieController class for the ProfileViewController.m/ProfileViewController.h in which i add
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([[segue identifier] isEqualToString:#"ProfileEditSegue"]){
ProfileViewController *cvc = (ProfileViewController *)[segue destinationViewController];
[cvc.placeholderView addSubview:cvc.view];
}
}
this to leads me to the same errors. I will be glad to send anyone my source files, the same error occurs when i do it on the supplied files from the tutorial.
PS. i am using this method so that i can have a vertical navigation bar, but i want to do it simply so i could also learn how one works and be able to use/develope it further.
any help would be great
The problem wasnt in those methods it was because the currentViewController instances was set to weak instead of strong
VC1 = NewGameViewController
VC2 = GameViewController
NewGameViewController.m
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if( [segue.identifier isEqualToString:#"newGameSegue"]) {
GameViewController *gameVC = (GameViewController *)segue.destinationViewController;
NSArray *array = [self nameArrayForTextFieldArray:self.namePicker.textFieldArray withColon:YES];
gameVC.nameArray = [[NSArray alloc] initWithArray:array];
}
-(NSArray *)nameArrayForTextFieldArray:(NSArray *)array withColon:(BOOL *)bool
basically returns an nsarray of strings given an nsarray of textfields. withcolon is a bool of whether or not you want the strings to have a colon appended to the end.
when i debug my code, the _nameArray ivar in gameVC still reads nil after every line here is called...can anyone help me out here??
The prepareForSegue method is invoked by UIKit when a segue from one screen to another is about to be performed. It allows us to give data to the new view controller before it will be displayed. Usually you’ll do that by setting its properties.
The new view controller can be found in segue.destinationViewController. If GameViewController embed the navigation controller, the new view controller will not be GameViewController but the navigation controller that embeds it.
To get the GameViewController object, we can look at the navigation controller’s topViewController property. This property refers to the screen that is currently active inside the navigation controller.
To send an object to the new view controller you can use this solution using performSegueWithIdentifier:
For example, if we want to perform a segue pressing a UIButton we can do this:
In the MyViewController.h we create a IBAction (connected to UIButton), dragging the button from storyboard to code:
- (IBAction)sendData:(id)sender;
In MyViewController.m we implement the method:
- (IBAction)sendData:(id)sender
{
NSArray *array = [self nameArrayForTextFieldArray:self.namePicker.textFieldArray withColon:YES];
[self performSegueWithIdentifier:#"newGameSegue" sender:array];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"newGameSegue"]) {
UINavigationController *navigationController = segue.destinationViewController;
GameViewController *controller = (GameViewController *)navigationController.topViewController;
controller.nameArray = sender;
}
}
Is GameViewController embedded in a navigation controller? In that case, your destinationViewController property is of type UINavigationController, not GameViewController. You can get to GameViewController by calling [segue.destinationViewController.viewControllers lastObject].
I'm assuming that you've done a NSLog (or examine it in the debugger) of array immediately before setting gameVC.nameArray. You really want to make sure it's being set the way you think it is. It's amazing how many times I've spent debugging something like this only to realize the problem was in my equivalent to nameArrayForTextFieldArray. Or a typo in the name of the segue identifier. Or random things like that.
Assuming that's ok, then a couple of things are possible:
How is your nameArray property defined in GameViewController? If it's not a strong reference (or a copy), then when your array falls out of scope, it will be deallocated. I think this would manifest itself slightly differently, but it's worth confirming.
Also, I've seen situations where a controller like GameViewController might have some confusion between various ivars and properties (which is why I never define ivars for my properties ... I let #synthesize do that).
I assume you're not using a custom setter for nameArray. I just want to make sure. If so, though, please share that, too.
Bottom line, can you show us all references to nameArray in your #interface of GameViewController as well as in its #synthesize statement?
I was wondering how I could access data from another class using Xcode 4.2 and Storyboard?
Say for instance how would I access the text of a text field from another class?
Google hasn't helped and the lesson on MyCodeTeacher.com about this is outdated and doesn't work anymore...
Thanks for bearing with me!
-Shredder2794
Not sure if this is the only or best way, but you can create a property in the destination view's .h file and set it to a value before the segue is performed
in the destination view controller's .h file:
#interface YourDestinationViewController : UIViewController
{
NSString* _stringToDisplay;
//...
}
#property (nonatomic, retain) NSString* stringToDisplay;
//...
and in the presenting view's .m file
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
YourDestinationViewController*viewController = segue.destinationViewController;
viewController.delegate = self;
viewController.stringToDisplay = #"this is the string";
}
Then you can do what you want with the property in whichever of the viewWillAppear/viewDidLoad/viewDidAppear/etc. methods best suits your purpose in the destination view's .m file
And then to check if it works, in the destination view controller's .m file:
-(void)viewWillAppear:(BOOL)animated
{
NSLog(#"self.stringToDisplay = %#", self.stringToDisplay);
...
//and if a label was defined as a property already you could set the
//label.text value here
}
Edit: Added more code, and made it less generic
This isn't specific to Storyboard. There are several ways to do what you are trying to do. You could declare a variable in your AppDelegate (an NSString) and set that in your first class. Then in your second class access the AppDelegate variable and use that to set your label. The code to do this is:
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
label.text = appDelegate.myString;
Another way to do it (probably the easiest) is to declare an NSString in your second class. Then in your first class, before you push the second view set that string variable. Something like this:
MyViewController *vc = [[MyViewController alloc] initWithNibName:#"" bundle:nil];
vc.myString = #"";
The third way to do this is using delegates. This is the most 'complicated' way but is the best. You would create a delegate which gets called when your second view appears. The delegate could then return the value from the first class to you.
You may also be able to use the new completion handler block on the iOS 5 pushViewController: method.
Edit:
Custom init method:
- (void)initWithNibName:(NSString *)nibName bundle:(NSString *)bundle string:(NSString *)myString
And then when you are pushing the view just class this method and set the string through it.
I have read the following tutorial regarding storyboard.
Basically the sample App created in this tutorial let the user navigate between various views and it's created using segue.
In order to navigate between views the tutorial say to create two UITableViewController and when "going" from one to another to specify a delegate:
First controller:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"AddPlayer"])
{
UINavigationController *navigationController = segue.destinationViewController;
PlayerDetailsViewController *playerDetailsViewController = [[navigationController viewControllers] objectAtIndex:0];
playerDetailsViewController.delegate = self;
}
}
Second controller:
#protocol PlayerDetailsViewControllerDelegate <NSObject>
- (void)playerDetailsViewControllerDidCancel: (PlayerDetailsViewController *)controller;
- (void)playerDetailsViewController: (PlayerDetailsViewController *)controller didAddPlayer:(Player *)player;
#end
#interface PlayerDetailsViewController : UITableViewController
#property (nonatomic, weak) id <PlayerDetailsViewControllerDelegate> delegate;
When "going back":
- (IBAction)cancel:(id)sender
{
[self.delegate playerDetailsViewControllerDidCancel:self];
}
My simple question is why this complication? Why use delegates and protocols?
I have changed the code using a "Java" style and now I'm passing to the second controller a reference to the first one, everything is working.
First controller:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
...
playerDetailsViewController.playerViewController = self;
}
Second controller:
#property (strong, readwrite) PlayerViewController *playerViewController;
So, what are the benefits to use delegates instead of simply passing references between ViewControllers?
Thanks!
Guido
Several reasons:
As Leonardo says, using references you couple the two view controllers together unnecessarily. You should just pass the data that's required and not the whole class
This is just how Objective-C apps tend to be constructed. By using a different method you'd be making your app harder to understand by seasoned developers
(You don't always need the delegate in your second class -- for example when it's display only -- so your example code is more complex than is often the case)
Related to the last point, your code is already harder than it needs to be. UIStoryboardSegue has properties pointing to the source and destination view controllers; there's no need to mess with the navigation controller.
To put in a Java manner, if you use a strong type you are tied to a single class.
Instead the delegate is in the end a class conform to a protocol.
So you could pass many class as delegates to playerDetail, as long as they are conform to the #protocol.
Is like casting and passing interface instead of concrete class in java.
You may well know the List interface and all the ArrayList, LinkedList... etc concrete implementations.
One thing I don't understand is why they get destination controller by passing trough the navigation. I always used:
MyDestinationViewController *dvc = [segue destinationViewController];
so that you can use in many situation where you do not have a navigation controller.
I'm stuck with the following. In a program, I'm trying to communicate between different classes (View Controllers with NIB files attached in a TabBar application etc). I want to call a method 'OMFG' in a class called 'ProductViewDetailController'. This class is a UIViewController (SplitViewDelegate). It's loaded programmatically.
Anyways, I've been trying to get the right call to this controller, and I came up with 2 solutions. One is declaring the productviewdetailcontroller in the caller's .h file and .m file, making an IBOutlet, linking it in the Interface builder and calling it directly by the line
[productDetailController OMFG];
When I call this method, it calls the right method in the ProductViewDetailController, but the instance of this viewcontroller differs from the one I programmatically can reach with this code:
for (UIViewController *controller in self.tabBarController.viewControllers) {
NSLog(#"%#", [controller class]);
if ([controller isKindOfClass:[UISplitViewController class]]) {
UISplitViewController *cell = (UISplitViewController *)controller;
for (UIViewController *controller2 in cell.viewControllers) {
NSLog(#"%#", [controller2 class]);
if ([controller2 isKindOfClass:[ProductViewDetailController class]]) {
[controller2 OMFG];
}
}
}
Which one should I use, and why?
edit: When I try to add a SubView to both viewcontrollers, the one where the call is [controller2 OMFG]; actually shows the newly added view, where the [productDetailController OMFG]; doesn't show the newly added view... Why is that? Is there a shorter (and more chique) way to get access to the right ViewController?
You should use a IBOutlet. This makes sure your app can still call the correct target if you later decide to change the hierarchy of view controllers, for example if creating an iPhone compatible setup without a UISplitViewController.
Calling isKindOfClass: in Objective-C is a sure sign that what you are doing is probably wrong. Firstly in Cocoa Touch what you do is always more important than who you are. Secondly what you try to do is probably peeking inside something that should be left private.