Passing Objects using Segue - objective-c

I have trouble understanding segues and how they work and pass objects. Basically I have a calculator and am trying to graph the objects stored in an array. So far I have an object called brain which is an instance of CalculatorBrain. Now brain has an NSArray property that I use as a stack to store variables. Let's say I add the values 3 and 5 to the array and then want to segue. I have my segue selected to a button called "Graph" so when I click the button it segues. How would I pass brain to the new view controller that I am segueing to? I have a property called setGraphingPoint which is defined in the new view controller which I think should accept the passed object. Also, if I pass brain through a segue will the values 3 and 5 be passed along with it or will it create a new object of CalculatorBrain? Here is what I have so far.
This is defined in the new view controller
#property (nonatomic, strong) CalculatorBrain *graphingPoint;
#synthesize graphingPoint = _graphingPoint;
-(void) setGraphingPoint:(CalculatorBrain*) graphingPoint{
_graphingPoint = graphingPoint;
[self.graphingView setNeedsDisplay];
}
This is called from the old view controller which will have button to segue
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:#"Graph"])
[segue.destinationViewController setGraphingPoint:[self.brain program]];

You can use protocols. For instance, you can have your prepareForSegue look like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
id destination = segue.destinationViewController;
if([destination conformsToProtocol:#protocol(GraphPointUsing)])
[destination setGraphingPoint:[self.brain program]];
}
Then you just have to make sure the the ViewController that you are segueing to conformist to GraphPointUsing.
If you do not want to use protocols, but you still want to call methods on GraphPoint you can do this:
//In new ViewController suppose we want to call the method `foo` on `GraphPoint`
[self.graphingPoint foo];
//Or if we want to call a setter we can do
[self.graphingPoint setFoo:5];

Related

How do I find a pointer to a segue (without calling one of the performSegue methods)?

From within one of my tableViewControllers I need to get a pointer to another tableView (which is the destination of the segue from this tableViewController). I don't actually want to perform the segue and pull up the new view onto the screen, i simply need a pointer to the destinationViewController.
The only place i can seem to find a pointer to this object is through one of the performSegue methods...
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
SavedTrackingViewController *stvc = (SavedTrackingViewController*) segue.destinationViewController;
But i cant seem to find a pointer to this segue anywhere else in my view controller.
Any help would be appreciate!

Use IBOutlets on different ViewController storyboard

I have a storyboard app with two different ViewController. On the second ViewController I have a UIImageView and I would like to call that UIImageView on the first one. I've been looking for a solution but I can't find anything that work for me.
#property(nonatomic, retain) IBOutlet UIImageView *pic;
I just want to be able to use that UIImageView on the other viewcontroller. I hope you can help me.
EDIT:
yes you can try to set your UIImageView in prepare for segue. I have tried it and it seems to work. You would do it as follows:
**I am assuming that the controller you are seguing to is called NewViewController
**I am also assuming your NewViewController has a UIImageView called imageView
-(void)prepareForSegue....
{
if ([segueIdentifier isEqual....])
{
NewViewController *newController=(NewViewController *)[segue destinationViewController];
[[newViewController imageView]setImage:self.someUIImageFromCurrentClass];
[[newViewController imageView]setNeedDisplay];
}
}
and yes, you do have to create a UIImage in current class (in this case it's self.someUIImageFromCurrentClass). That's the point is that you're taking an image from current ViewController (current class) and showing it in the ViewController you are seguing to.
ORIGINAL:
-(void) prepareForSegue: (UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"your segue name"])
{
[segue.destinationViewController setYourUIImage:self.someImageFromCurrentClass];
}
}
be sure to import the .h file for the controller you're seguing to.
second, YourUIImage is an UIImage that is publicly declared in the controller you're seguing to (like Michael mentioned).
In the destination controller, you'll want to grab that UIImage and set it as the image for you UIImageView:
[self.myImageView setImage:YourUIImage]; // <-- you can do this in ViewDidLoad or ViewWillAppear
that should get you going
**Also I guess it's worth mentioning is that a segue always creates a new instance of the controller you're seguing to, which doesn't exist before the segue, which is why you have to pass the image to it.
You can't use outlets between view controllers (VCs) because there's no automatic one for one VC to refer to another. You'll have to expose public properties or methods that allow the manipulation you need and find some way to pass a reference to one VC from another.
For example, if you pass from one VC to another via a segue, you can manipulate properties in the destination VC from the source in prepareForSegueWithIdentifier:.

Objective-C (iOS): prepareForSegue won't pass my data into destination VC

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?

Xcode (storyboards and segues): why delegates instead of references?

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.

Simplest way to pass property to a UITableViewController

I have two UITableViewControllers (hooked up to a UINavigationController). When I click on a UITableViewCell on the first controller, I created a segue so that the second controller comes into view. I want to have a variable in the second view controller that contains the text on the that was selected. How do I do this? I have tried passing it directly, but it doesn't work for some reason.
Declare an property (like selectedText - an NSString) on the receiving viewcontroller (in this case we'll call it SecondTableViewController). Here is the code:
In SecondTableViewController.h:
#interface SecondTableViewController : UITableViewController {
}
#property(nonatomic, strong) NSString *selectedText;
In SecondTableViewController.m:
#implementation SecondTableViewController
#synthesize messageDetail;
In your FirstTableViewController.m:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Make sure we are dealing with the proper Segue
if ([segue.identifier isEqualToString:#"idOfMySegue"]) {
SecondTableViewController *svc = segue.destinationViewController;
svc.selectedText = myVarValue; // This can be got from either setting another var in the tableviewcontroller or by just passing the entire object at the selected index of the NSIndexPath.row (if you are populating the tableview with an array. Note you would change the object type of the passed along object from an NSString to whatever the other object is.
}
}
The prepareForSeque method is where you pass along any data or objects from one view to another.