I have a simple push segue that goes from one UITableViewController to another. At the time of initing the other UITableViewController, I want to pass an id that will be used to perform an sqlite query and populate the table. I could use a public property and reload the table in a custom setter, but the design seems better if it is done on initing. After searching around for quite a while I haven't been able to find any clear examples. I have a seen a mentions of initWithCoder and initWithFrame but they haven't been clear enough to connect it to what I am trying to do.
Here is what the segue looks like from the first table:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"ShowFlashcards"]) {
//pass variable to custom init somewhere around here?
FlashcardsTableViewController *flashcardsTableViewController = [segue destinationViewController];
}
}
Here is what the custom init looks like:
#property (nonatomic, strong) NSNumber *listId;
#property (nonatomic, strong) NSArray *flashcards;
- (id)initWithListId:(NSNumber *)listId {
self = [super init];
if (self ) {
self.listId = listId;
Database *db = [[Database alloc] init];
self.flashcards = [db getWordsFromList:[listId integerValue]];
}
return self;
}
You are over-thinking this, and coming to some wrong conclusions.
Adding a property to the destination VC (view controller) and setting it in prepareForSegue is exactly what you want to do.
You can't use a custom init in this situation. Invoking the segue allocates and initializes the destination VC (actually, I believe it is created using the initWithCoder method.) The destination VC's view won't have been loaded yet when prepareForSegue is called, so you can set properties and they will be set up by the time the destination VC's viewDidLoad method gets called.
#AJHacker's answer shows how to give the destination VC a pointer to the source VC, which is useful when the destination needs to be able to send data back.
You would need to add a property that contains a reference of the first view into the second view. For example:
#interface AutoRenewDrop
#property(weak, nonatomic) AddR *callerView;
#end
And then in the 'done' method of the second view you can just update the variables in the caller view like so:
-(IBAction)done:(UIStoryboardSegue *)segue {
[self.navigationController popViewControllerAnimated:YES];
callerView.renewDate.text = transferData;
}
Of course when you initiate the second view you will have to set the reference, like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"Check Mark Segue"])
{
NSLog(#"Transfering Data");
AutoRenewDrop *controller = segue.destinationViewController;
controller.transferData = self.renewDate.text;
controller.callerView = self; //Here, you are passing the reference to this View
}
}
Hope that helps
Related
I am passing a data from one viewController to another viewController. But it is throwing NSException.
Here is my code.
viewcontroller.m
NSString *empid1 = [dataDict objectForKey:#"EmployeeId"];
NSLog(#"empid %#",empid1); //it prints 161.
DashBoardViewController *dash = [[DashBoardViewController alloc]initWithNibName:#"DashBoardViewController" bundle:nil];
dash.empid = empid1;
DashBoardViewController.h
#property (strong, nonatomic) NSString * empid;
DashBoardViewController.m
NSString *strURL=[NSString stringWithFormat:#"http://192.168.1.1:8002/api/Case/GetCaseByEmployeeID/?empId=%#",_empid];
It throws data is nil.NSException.Any help will be appreciated.
plz use this you are using storyboard
pass your value like this
[self performSegueWithIdentifier:#"DashBoardViewController" sender:[dataDict objectForKey:#"EmployeeId"]];
use this method when you use performSegueWithIdentifier
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
if ([segue.identifier isEqualToString:#"DashBoardViewController"])
{
DashBoardViewController *dash= segue.destinationViewController;
dash.empid =(NSString *)sender;
}
}
YOURCONTROLLER *chatdetail=[[YOURCONTROLLER alloc] initWithNibName:#"YOURCONTROLLER_IDENTIFIER" bundle:nil];
chatdetail.strTitle = "YOUR VALUE"
[self.navigationController pushViewController:chatdetail animated:YES];
Next Controller
#property (weak, nonatomic) NSString *strTitle;
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"%#",self.strTitle);
}
Reason
It is happening because the DashboardViewController Which you are instantiating and setting the property empid is different from the DashboardViewController instance on which segue is performed. You are instantiating it with the following code
DashBoardViewController *dash = [[DashBoardViewController alloc]initWithNibName:#"DashBoardViewController" bundle:nil];
dash.empid = empid1;
But the DashBoardViewController which is being used by iOS is being created in prepareForSegue method and is different from dash created by you. For debugging purpose put some default value in empid property and then call performSegueWithIdentifier.It calls prepareForSegue method.ViewController on which segue will happen will be passed as destinationViewController as a parameter in this method whic will be of type UIViewController.Override this method.Downcast destinationViewController as DashBoardViewController.Log empid property.It will be having default value.Now log empid property of dash.It will be having correct value.So your instance is of no use here.
Solution
Set the empid property in prepareForSegue as answered by #balkaran singh or if you want to use your dash variable then embed a navigationController and push dash on it without calling performSegueWithIdentifier as suggested by #HImanshu Moradia in previous answers.
I have a UIViewController controlling several different views in Xcode. I want to set a label on the incoming view using information from the current view. I know labels and other UI elements get reset when the view is loaded, but my non UI #property is being set to nil as well.
Here is my property:
#property (strong, nonatomic) NSString* username;
Here is the code that sets it:
//NSLog(#"%#",dict);
if ([dict[#"login"] intValue]){
[self performSegueWithIdentifier:#"JoinSuccess" sender:sender];
self.username = dict[#"user"];
Here is the code that tries to use it:
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"%#", self.username);
self.welcomeLabel.text = [NSString stringWithFormat:#"Welcome, %#",self.username];
}
I've tried setting username both before and after performSegueWithIdentifier, but in either case when I try to reference it in viewDidLoad the value is nil. Any idea as to why it's reseting my #property? My guess is maybe it's creating a new instance of my viewController. If that's the case, how can I pass the needed variable?
As you said the viewController will be created again when ever you call performSegueWithIdentifier. You could set the value in the new instance. You can do this in
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
YourViewController *vc = [segue destinationViewController];
vc.username = dict[#"user"];
}
you have to add another method to pass value from one view controller to another view controller .
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
nextViewcontroller *nextViewcontrollerObjt = [segue
destinationViewController];
// apply setter to set value of next view
nextViewcontrollerobject.title = nextViewControllerTitleString;
nextViewcontrollerobject.variable_1= currentView_variable1;
nextViewcontrollerobject.variable_2 =currentView_variable2;
}
iOS - How can I pass information between two view Controllers linked through a Manual Segue?
I have two View Controllers each assigned to their respective Views in the Storyboard. I Control-Click Drag from on to the other and select a "manual" "push" segue.
From what I understand I can execute this segue form anywhere in the code by giving it an identifier and using:
performSegueWithIdentifier:#"identifierA" sender:self
What would be the best way to send information about something that was selected to the newly created View Controller?
Whenever you perform a segue, this method - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender notifies the controller that a segue is about to be performed.
So from this method you get to know about the destination view controller and you can set any property/ pass value to any property of destination view controller.
Something like this.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
if([segue.identifier isEqualToString:#"yourIdentifier"] ){
YOUR_Destination_Controller *vc = (YOUR_Destination_Controller *) segue.destinationViewController;
vc.someProperty = YES; //in example someProperty is a BOOL
}
else {
//Do whatever accordingly
}
}
Hope this helps
I am slightly confused about your question.
Let's assume you have VC1 that opens VC2.
If you want to pass information from root controller(vc1) to new one(vc2)
With segues best you can do is create public property in VC2 and set it before method executes. You can attach just before method executes in prepareForSegue method. So implementation will be something like this:
//
// VC1.m
// StackOverflow
#import "VC1.h"
#import "VC2.h"
#implementation VC1
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if( [segue.identifier isEqualToString:#"yourSegueId"] )
{
if( [segue.destinationViewController isKindOfClass:[VC2 class]] )
{
[(VC2*)segue.destinationViewController setMyPassedString:#"YOUR STRING FROM VC1"];
}
}
}
#end
//
// VC2.h
// StackOverflow
#import <UIKit/UIKit.h>
#interface VC2 : UIViewController
#property(nonatomic, strong) NSString* myPassedString;
#end
I personally don't like this approach as you are creating public properties on VC2, which may not be needed at all. However this is a limitation on how storyboard works, and only way to avoid this is to use good old fashioned xib's and designated initializers where you can put your params.
If you want to pass information from new controller(vc2) back to root(vc1)
Here you could basically use two approaches: by passing weak reference to vc2, store it, and then use it when needed to update something on vc1. This is called delegate pattern, however it could be used in much much more powerful and encapsulated way called BLOCKS.
Here is simple implementation with blocks:
//
// VC2.h
// StackOverflow
#import <UIKit/UIKit.h>
#interface VC2 : UIViewController
#property(nonatomic, copy) void(^vc1UpdateBlock)(NSString* string);
#end
//
// VC2.m
// StackOverflow
#import "VC2.h"
#implementation VC2
-(void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
_vc1UpdateBlock(#"PUT YOUR PASSED STRING HERE");
}
#end
//
// VC1.m
// StackOverflow
#import "VC1.h"
#import "VC2.h"
#implementation VC1
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if( [segue.identifier isEqualToString:#"yourSegueId"] )
{
if( [segue.destinationViewController isKindOfClass:[VC2 class]] )
{
[(VC2*)segue.destinationViewController setVc1UpdateBlock:^(NSString * stringFromVC2) {
NSLog(#"I'm printing in VC1 string %#, passed from VC2", stringFromVC2);
}];
}
}
}
#end
Again if you use xib files directly you can use designated initializers and hide block property, however with storyboards you must create your block publicly available.
The relevant part of my storyboard appears as follows:
You can see the custom "Container Controller" view houses two Container Views, one which links to a Navigation Controller via embedded segue, and another that links to a custom "Master View Controller" (which implements a Table View Controller) via embedded segue. The Navigation Controller component further has a relationship with a custom "Location Filter Controller."
I need to implement delegation such that when one of the UISteppers in the Location Filter Controller is incr./decr., the table view in the Master View Controller knows to update the data it displays accordingly.
I am not unaccustomed to working with protocols/delegates, but this unique situation of talking between views housed in segues is really tricking me! For the most part I have had success following the example here: Passing Data between View Controllers. In this case however, I am not able to directly link instantiated views as he indicates to do in 'Passing Data Back' step 6.
I had considered using a singleton object from which each of these views could get/set the necessary data, but the issue here is that the table view would not necessarily know when to update its contents, despite having data with which it could/should update.
Here is a code snippet from ContainerController.m where I setup the embedded segues to function:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
DataHold *data = [[DataHold alloc] init]; // <-- this actually is a singleton object
if([segue.identifier isEqualToString:#"locationEmbedSegue"])
{
}
else if([segue.identifier isEqualToString:#"tableEmbedSegue"])
{
[[segue destinationViewController] setDelegate:data.detailTableViewController];
// ^ This part actually sets up a delegate so that the table view (Master View Controller)
// delegates to the detail view controller of the overarching split view controller
// and tells it what to display when a row is pressed.
}
}
Thanks for any help!
I think you are on the right track setting the table view delegate to your Location Filter Controller.
I found that a simple way to work with embeded view controller is to add "placeholders" property for them, and set these property when the segue is "performed".
// MyContainerController.h
#property (strong, nonatomic) MyLocationFilterController *detailViewController;
#property (strong, nonatomic) UITableViewController *masterViewController;
// MyContainerController.m
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"locationEmbedSegue"])
{
UINavigationViewController *dest = (UINavigationViewController *)segue.destinationViewController;
self.detailViewController = dest.topViewController;
}
else if([segue.identifier isEqualToString:#"tableEmbedSegue"])
{
self.masterViewController = (UITableViewController *)segue.destinationViewController;
[self.masterViewController.tableView setDelegate:self.detailViewController];
}
}
I came to this question recently and found there may be one problem with the answer above.
Move the setDelete: method out. This makes sure no controller is nil.
Then code becomes:
// MyContainerController.h
#property (strong, nonatomic) MyLocationFilterController *detailViewController;
#property (strong, nonatomic) UITableViewController *masterViewController;
// MyContainerController.m
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"locationEmbedSegue"])
{
UINavigationViewController *dest = (UINavigationViewController *)segue.destinationViewController;
self.detailViewController = dest.topViewController;
} else if([segue.identifier isEqualToString:#"tableEmbedSegue"])
{
self.masterViewController = (UITableViewController *)segue.destinationViewController;
}
[self.masterViewController.tableView setDelegate:self.detailViewController];
}
I have a Navigation Controller and 2 Views Controllers. The first View Controller is associated with a UIViewController named ViewController. The second is connected to a UIViewController named BookVC. BookVC has a UITextField and is connected via an outlet:
#property (strong, nonatomic) IBOutlet UITextField *textFieldContent;
The connection is made with a button that uses a segue. I want to pass some data between the two and am using the following code which fails:
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
BookVC* nextPage = [[BookVC alloc] init];
nextPage = [segue destinationViewController];
nextPage.textFieldContent.text=#"Some content";
}
How should I pass data between the View Controllers?
I think the problem is that textFieldContent doesn't exist at that point. You'll need to add a property to BookVC that can hold the text you want to put into textFieldContent... we'll call it #property NSString *textFieldContentText. So then,
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
BookVC *nextPage = [segue destinationViewController];
[nextPage setTextFieldContentText:#"Some content"];
...
}
And then, in the -viewDidLoad method of BookVC:
- (void)viewDidLoad
{
[textFieldContent setText:[self textFieldContentText]];
}
You don't need this line:
BookVC* nextPage = [[BookVC alloc] init];
That creates a brand new BookVC which you then go and overwrite in the next line.
Since you are using [segue destinationViewController] you should be good to go. What's happening when you segue to nextPage?