I hope I'm not asking something that's been already answered (but I found no answer to this, so hopefully I'm not).
I have an app in the current xcode version, using segues and navigationController. I need to pass data from one view to the other - what's the easiest way to do this? I ran onto some sharedData thing that could be possibly hooked onto the performSegueWithIdentifier method but don't know how to use it (or whether it is the right choice to do it like this).
Thanks
A segue has two view controllers: sourceViewController and destinationViewController. When UIKit executes a segue, it sends a prepareForSegue:sender: message to the source VC. You can override that method in your view controller subclass to pass data to the destination VC.
For example, suppose you have a master view controller with a table view of movies, and when the user clicks a row in the table view, you want to segue to a detail view controller for the movie.
#implementation MasterViewController
...
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
DetailViewController *detailVC = segue.destinationViewController;
NSIndexPath *selectedPath = [self.tableView indexPathForSelectedRow];
detailVC.movie = [self movieForIndexPath:selectedPath];
}
This is explained in the Introducing Interface Builder Storyboarding video from WWDC 2011.
It's also worth noting that when the segue's origin is a table view cell, or the accessory button of a table view cell, the sender argument of prepareForSegue:sender: is the table view cell.
I think the best way is to import header for the view controller that will be shown in controller that is performing segue. And then use it's accessors or other methods to pass needed data inside prepareForSegue:
// In FirstViewController.h
#import "SecondViewController.h"
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"SegueToSecondViewController"]) {
// Get destination view controller and don't forget
// to cast it to the right class
SecondViewController *secondController = [segue destinationViewController];
// Pass data
secondController.dataArray = self.someDataArray;
secondController.name = #"Fancy name";
}
}
When you want data back from second to first, I suggest to use delegate:
// In FirstViewController.h
#import "SecondViewController.h"
#import "SecondViewControllerDelegate.h"
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"SegueToSecondViewController"]) {
SecondViewController *secondController = [segue destinationViewController];
// Declare first view controller as a delegate
secondController.delegate = self;
// Pass data
secondController.dataArray = self.someDataArray;
secondController.name = #"Fancy name";
}
}
// Second controller's delegate method,controller
// ie. used to return data after second view is dismissed
- (void)secondControllerFinishedSomeTask:(NSArray *)someReturnedData {
// Do something with returned data
}
When you want data back from second to first, better way is to use Unwind Segues.
Related
I'm completely new to objective c and ios development, I'm simply trying to navigate to a new scene on my storyboard, and upon navigating change the text of a UIlabel to something.
The way I have this set up is I have a View controller and a tableview controller, each with their own header and implementation files. I'm using a segue from a button on the view controller to navigate to the tableview.
I did read this stack overflow post (How do I pass information between storyboard segues?) which explains how to pass values on a segue and have implemented it as such:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
NSLog(#"prepareForSegue: %#", segue.identifier);
if([segue.identifier isEqualToString:#"navtotablebut"])
{
TableViewController *testcontroller = segue.destinationViewController;
testcontroller.testpass.text = #"Testing123";
NSLog(#"%#",[NSString stringWithFormat:#"testpass has been changed to %#", testcontroller.testpass.text]);
}
}
The logs verify that this segue method does get called, and it is able to correctly identify the id I gave it (navtotablebut). I then attempt to set the value of my UIlabel's text property on my tableview controller to a simple string "Testing123". However not only does this change not reflect on the new screen, but my print statement afterwards seems to think testcontroller.testpass.text is equal to (null).
Am I doing something horribly wrong here?
You are setting the value of label testcontroller in TableViewController before the view of `TableViewController ' is drawn.
Please note: update the view in viewdidLoad or viewDidLayoutSubviews if using Autolayout.
Best way is pass your value as a string to other view controller and then populate.
Simply grab a reference to the target view controller in prepareForSegue: method and pass any objects you need to there. Here's an example...
- (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];
}
}
also with performSegueWithIdentifier:sender: method to activate the transition to a new view based on a selection or button press.
// When any of my buttons are pressed, push the next view
-(IBAction)buttonPressed:(id)sender
{
[self performSegueWithIdentifier:#"MySegue" sender:sender];
}
// This will get called too before the view appears
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"MySegue"]) {
// Get destination view
SecondView *vc = [segue destinationViewController];
// Get button tag number (or do whatever you need to do here, based on your object
NSInteger tagIndex = [(UIButton *)sender tag];
// Pass the information to your destination view
[vc setSelectedButton:tagIndex];
}
}
In TableViewController, You need to create property,
TableViewController.h
#property (nonatomic, strong) NSString *subject;
you pass value like that,
In ViewController,
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([[segue identifier]isEqualToString:#"Detail"])
{
TableViewController *testcontroller = segue.destinationViewController;
testcontroller.testpass.text = #"Testing123";
detailView.subject = testcontroller.testpass.text;
}
}
i am facing a situation that i am loading data correctly to a uitableviewcontroller. also i am loading data correctly to the detail view controller.
what i am using:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"ShowPaymentDetails"])
{
DetailPaymentViewController *detailViewController = [segue destinationViewController];
NSIndexPath *myIndexPath = [self.paymentsTableView indexPathForSelectedRow];
detailViewController.payment = [arrayOfPayment objectAtIndex:[myIndexPath row]];
}
}
the problem i have is that when i am going back (through normal drag and drop swipe) the uitableviewcontroller is reloaded. since this takes data from a web service ... i don't want to reload the data.
Can you help me on any ideas on this.
Thnx in advance.
I'm guessing your segueing to the TableViewController from the DetailViewController. I think your creating a new TableViewController and not going back to the original one. You can check this in the debugger by setting a breakpoint in viewWillApear and looking at the value of self.
(lldb) po self
$0 = 0x1ddd2480
You should do this in your DetailViewController to go back to the TableViewController.
[self.navigationController popViewControllerAnimated:YES];
I have created a simple unwind segue using the storyboard tools. I have created the following event handler in the view I want to unwind to:
-(IBAction)quitQuiz:(UIStoryboardSegue *)segue {
NSLog(#"SEGUE unwind");
}
This fires correctly and unwinds the segue (the message gets logged).
When the user quits the quiz I would like to pass some data back and have been struggling with how to achieve this. Can anyone advise?
Thanks Jeff. After watching WWDC video 407 I have a clear solution.
In the view controller that is the target of the unwind you should create a method that takes a single UIStoryboardSegue parameter and returns an IBAction. The UIStoryboardSegue has a method to return the source view controller! Here is the example taken from the video (credit to Apple).
- (IBAction)done:(UIStoryboardSegue *)segue {
ConfirmationViewController *cc = [segue sourceViewController];
[self setAccountInfo:[cc accountInfo]];
[self setShowingSuccessView:YES];
}
Getting data back from an unwind segue is explained very nicely in this apple talk, second half of the presentation (edit: starts from 37:20)
In particular, in an unwind segue the [segue sourceViewController] is the still active view controller from which the unwind event originated, so just access your properties as usual.
Add the function prepareForSeque in the controller being closed.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
This function is called before the unwind segue is called (in your example you called it quitQuiz).
As you can see, it also has a sender parameter so that you can find out who called the unwind and collect the relevant data accordingly.
For example of the WWDC 407 video, if you clicked the reset button you would not set the accountInfo and if you clicked the done button you would.
Set up a delegate and inform your source view controller about quitting the quiz and send back the data. Don't forget to set the source view controller as the delegate of the destination view controller.
// DestinationViewController.h
#protocol DestingationDelegate;
#interface
...
#property (assign) id<DestinationDelegate> delegate;
...
#end
#protocol DestinationDelegate
-(void)didQuitQuiz:(NSDictionary*)infoDict;
#end
// DestinationViewController.m
-(IBAction)quitQuiz:(UIStoryboardSegue *)segue {
NSLog(#"SEGUE unwind");
if (self.delegate) [self.delegate didQuitQuiz:infoDict];
}
// SourceViewController.h
#import DestinationViewController.h
#interface SourceViewController : ViewController <DestinationDelegate>
....
// SourceViewController.m
-(void)didQuitQuiz:(NSDictionary *)infoDict {
if (infoDict) {
// do something with the data
}
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
...
destinationViewController.delegate = self;
}
Yes,
For that, you will need to make properties, which holds your data to be sent from another view controller:
- (IBAction)unwindSelectFriendsVC:(UIStoryboardSegue *)segue
{
if ([segue.sourceViewController isKindOfClass:[ChildVC class]]) {
ChildVC *child = (ChildVC *) segue.sourceViewController;
//here we are passing array of selected friends by arrSelectedFriends property
self.arrFriendList = child.arrSelectedFriends;
[self.tableView reloadData];
}
}
Passing data between view controllers is frequently accomplished using protocols. Here's an example:
In your quiz view controller header, declare a similar protocol definition:
#protocol JBQuizViewControllerDelegate <NSObject>
#required
- (void)quizController:(id)controller didQuitWithState:(NSString *)state;
#end
In your presenting view controller's prepareForSeque: method, wire up the delegate:
JBQuizViewController *destination = (JBQuizViewController *)segue.destinationViewController;
destination.delegate = self;
Then, in your presenting view controller, handle the delegate protocol's quizController:didQuitWithState: method.
Finally, once the user quits your quiz, you should notify the delegate using the protocol, passing in the state or whatever data you want to expose.
I am able to pass data from one view to other view without any problem. But here is my situation i am facing problem.
Let's say
-> Passing data to other view
ViewA->ViewB This working perfectly fine
ViewB->ViewC This working perfectly fine
ViewC->ViewB Here is the problem.
I tried using push segue ,it goes to ViewB but when i press back button it goes to ViewC->back button->ViewB->back button->View A. From ViewB when i press back button it must goes to ViewA.
Tried using modal segue, it goes to ViewB but then i can't go any where.
As i am new to iOs I am really haven't got any idea about how to achieve this?
How to pass data back to ViewB from ViewC?
I think you guys can help me.
Edit
In View A i am calling ViewB like this
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"RemixScreen"])
{
if([self.recipeFrom isEqualToString:#"ViewB"])
{
ViewB *reciepe = [segue destinationViewController];
//somedata i will pass here
}
}
}
In View B i am calling like this
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"ViewA"])
{
ViewA *data = [segue destinationViewController];
//some data
}
}
Thanks for your help guys.
The correct way to do this is to use delegates. You use properties in prepareForSegue to pass data forward and delegates to pass data back.
The way it works is to have a delegate property in ViewC that is set by ViewB in prepareForSegue. That way ViewC can communicate with ViewB via a protocol you set up.
EDIT: Adding code to demonstrate:
ViewControllerBInterface:
#protocol ViewBProtocol
- (void)setData:(NSData *)data;
#end
#interface ViewBController: UIViewController <ViewBProtocol>
...
#end
Here we make ViewBController follow our protocol that ViewCController will communicate to.
Next up is ViewCController interface:
#interface ViewCController: UIViewController
#property (nonatomic, weak) id<ViewBProtocol> delegate;
...
#end
Now we look at ViewBController's prepareForSegue:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
UIViewController* controller = [segue destinationViewController];
if ([controller isKindOfClass:[ViewCController class]])
{
ViewCController* viewCController = (ViewCController *)controller;
viewCController.delegate = self;
}
}
As you can see we link the ViewC controller to the ViewB one via the delegate property. Now we do something in ViewC:
- (void)sendData:(NSData *)data
{
[self.delegate setData:data];
}
And you can use that in your viewWillDisappear method of ViewCController if you wanted to.
The way I have gotten around this problem in a recent project is the following;
View A is the parentViewController, so can be accessed from View B and View C at any time like this;
ViewAClass *parentView = (ViewAClass *)self.parentViewController;
You can then read and write to a property of the View A, like;
NSString *string = parentView.someStringProperty;
or
parentView.someStringProperty = #"Hello World";
EDIT - "To go back from view B to ViewA with out back button"
[parentView popViewControllerAnimated:YES];
Are you using navigation controller? If yes, you can easily pass data with Singleton, which is my favorite way to do that and also its easy to use. Otherwise, if you're trying to navigate through views with buttons or something, try to set a identifier to your segue, then call the method "performSegueWithIdentifier"
is there a way to identify the segue identfier used to navigate, inside the destination viewcontroller.
Well in that case in the viewController you are coming from override prepareForSegue: and pass some data in to the destination viewController that will allow it to decide what specific code to run.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"SegueIdentifier"]) {
YourViewController *vc = segue.destinationViewController;
vc.yourData = yourData;
}
}
the in viewDidLoad of the destination check the value of yourData and execute your custom code