Issue passing string using segue - iphone

I have one table view controller and another view controller. I want to pass data from selected cell (from table view) to view controller and update a label in view controller. I'm doing this by following way:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"SubRecipeConnectorSegue"])
{
RecipeDetailViewController *destViewController = segue.destinationViewController;
destViewController.recipeName = selectedRecipe;
}
}
The problem is, when, for the 1st time I'm selecting a cell it not appearing in the label; but, when I'm selecting any 2nd value; my 1st selected value is displaying in label.
Similarly when selecting 3rd value, 2nd value is getting showed in label.
Can any one please tell me what is going wrong? Please help.

The way to do this is to trigger the segue manually. In IB, draw the segue from one VC to the other (not from the table cell). Make sure to give it an identifier. In tableView:didSelect, set the selected string and call performSegueWithIdentifier:.
The problem you're seeing is the segue prep gets called before the tableView delegate, so the new VC is presenting the prior selection, which is undefined the first time.

You should get the data from the corresponding cell in the didSelectRowAtIndexPath and then perform the segue grammatically.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *recipeString = [dataArray objectAtIndex:indexPath.row];
[self performSegueWithIdentifier:#"SubRecipeConnectorSegue" sender:recipeString];
}
Then change your prepareForSegue method into
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(NSString*)recipeString
{
if ([segue.identifier isEqualToString:#"SubRecipeConnectorSegue"])
{
RecipeDetailViewController *destViewController = segue.destinationViewController;
destViewController.recipeName = recipeString;
}
}
I don't know how you have stored the data in the cells, so that might differ from the example I wrote. If you need help implementing please let me know.

Related

Xcode/Obj-c - Segue pushes new instance

I have seen similar questions a lot on Stackoverflow and I tried a lot of things but I can't seem to figure this out. I have multiple TableViewControllers and 1 MainViewController. The MainViewController has buttons calling the different TableViewControllers and on selecting a tablecell the tableViewController dismisses.
The problem is that im pushing a new instance of my MainViewController every time I push from either one of my tableViewControllers. I currently use Segues to push between these different controllers.
In short: When switching from TableViewControllers to ViewController I want to prevent the ViewController to get pushed as a new instance because this way its removing my previous data input.
Im pretty sure I have to use either:
[self dismissModalViewController: withCompletion:]
performSegue
prepareForSegue
Or set some global variables in a class and call those, but im not experienced enough yet to implement this correctly.
A simple example of end result would be: 3 textfields in VC. On clicking textfield1 it opens tableview1 and on clicking a cell it updates textfield1. Textfield2 opens tableview2, etc.
Hope im clear enough, could post sample code if needed.
Edit, posting code (keep in mind, segues are performed in storyboard):
TableViewExample.h:
#interface IndustryViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
NSArray *tableViewArray;}
#property (nonatomic, strong) IBOutlet UITableView *tableViewIndustry;
TableViewExample.m:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showIndustry"]) {
NSIndexPath *indexPath = [self.tableViewIndustry indexPathForSelectedRow];
ViewController *destViewController = segue.destinationViewController;
destViewController.industryText = [tableViewArray objectAtIndex:indexPath.row];
destViewController.industryTextName = [tableViewArray objectAtIndex:indexPath.row];
}}
Then in ViewController.m, viewDidLoad:
[industry setTitle:industryText forState:UIControlStateNormal];
These are the most important parts I think.
Is the segue of type "Push"? If so you should try dismissing the table view controllers using:
[self.navigationController popViewControllerAnimated:YES];
If the segue is of type "Modal" instead you should do something like this on your table view controller:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// your logic here
[self dismissModalViewControllerAnimated:YES];
}
As for the data exchange between controllers what I would personally do is creating a public property in the header file of the Table View Controller, like the following:
#property (nonatomic, weak) <Your_UIViewController_Subclass_Here> *mainController
Than, in the main controller, override the prepareForSegue:sender: method to set the newly created property to point to the main controller, like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
<Your_Subclass_Of_UITableViewController_Here> *destinationController = segue.destinationController;
destinationController.mainController = self;
}
Now the Table View Controller will have a pointer to the main controller to send the data basically all you have to do is to implement some public method or property in the Main Controller to be called when the user selects a table view row in the table view controller in order to update the text in the textfields or whatever data model you are using.

xcode ios: prepareForSegue - same data 2 display. 2nd display data error

I'm in the process of learning to use the storyboard to create tables.
When user Tabs on the cell, it goes to View A, while if user Tabs on the arrow icon on the right inside the cell it goes to View B.
Both A & B are to display the same data, just the layout is different. I'm using UIViewController for A and UITableViewController for B. Its just having different layout but same data.
The problem I have is that, view A shows the correct data for each cell, but view B always shows the data of cell[0] no matter which cell I Tab on.
The prepareForSegue code is as follow:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
`if ([[segue identifier] isEqualToString:#"ShowCarDetails"])
{
CarDetailViewController *detailViewController =
[segue destinationViewController];
NSIndexPath *myIndexPath = [self.tableView
indexPathForSelectedRow];
int row = [myIndexPath row];
detailViewController.carDetailModel = #[_carMakes[row],
_carModels[row], _carImages[row]];
}
else if ([[segue identifier] isEqualToString:#"niceDetails"])
{
StaticTableViewController *detailViewController =
[segue destinationViewController];
NSIndexPath *myIndexPath = [self.tableView
indexPathForSelectedRow];
int row = [myIndexPath row];
detailViewController.carDetailModel = #[_carMakes[row],
_carModels[row], _carImages[row]];
}
}
Can someone point out where's the error here?? Thanks!
And otherwise check the data in the StaticTableViewController.
Are you synchronising the data etc?

Different behaviour of prepareForSegue and tableView: didSelectRowAtIndexPath:

I am testing a simple scenario that has a UITableViewController that has the cell segue to another UIViewController. Now during that segue, I need to pass some information to the UIViewController such as name and price of an item. I wanted to test the difference between using prepareForSegue and didSelectRowAtIndexPath: to pass those 2 data points. It seems to work with the didSelectRowAtIndexPath: but not prepareForSegue. After doing some debugging:
1-I am wondering if prepareForSegue happens before any IBOutlet on the destinationViewController are initialized and so you can't effectively set them. So prepareForSegue seems to be more geared towards passing non-IBOutlet properties.
2-If I am using a regular push segue, how can I access the "segue.destinationViewController" from the didSelectRowAtIndexPath: method.
PS: When testing the prepareForSegue, I am setting the segue in IB to push and when testing the didSelectRowAtIndexPath:, I am setting the segue in IB to modal.
PS: All resemblances to real names are incidental!
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"itemDetailSegue"])
{
NSIndexPath *indexPath=[self.tableView indexPathForSelectedRow];
NSString *theItemName=[[self.itemsForSale objectAtIndex:indexPath.row]objectForKey:#"Name"];
NSString *theItemPrice=[[self.itemsForSale objectAtIndex:indexPath.row]objectForKey:#"Price"];
JuanDetailVC* juanDetailVC= (JuanDetailVC*) segue.destinationViewController;
juanDetailVC.itemName.text=theItemName;
juanDetailVC.itemPrice.text=theItemPrice;
}
}
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *theItemName=[[self.itemsForSale objectAtIndex:indexPath.row]objectForKey:#"Name"];
NSString *theItemPrice=[[self.itemsForSale objectAtIndex:indexPath.row]objectForKey:#"Price"];
JuanDetailVC* juanDetailVC= (JuanDetailVC*) self.presentedViewController;
juanDetailVC.itemName.text=theItemName;
juanDetailVC.itemPrice.text=theItemPrice;
}
Thanks
You're right in that you cannot set outlet properties in prepareForSegue. You'll have to pass the properties into another property, then in viewDidLoad of the destination view controller set the outlet's property. For example: you can't set the text of a label, you'll have to create a second property (e.g., an NSString *labelText) and in viewDidLoad set the label's text (self.label.text = self.labelText).
You don't have access to the destination view controller in tableView:didSelectRowAtIndexPath. You'll have to create the instance yourself by alloc/initing it.

next view controller is not loaded when the segue is performed

I am still new to the storyboard stuff. I spent two days debugging this problem. It seems the destination view controller of a segue is not working properly when the -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender is called.
I have a table view and when a cell is tapped, it calls for a segue
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
VAItem *item = [category itemAtIndex:indexPath.row];
[self performSegueWithIdentifier:#"SegueShowDetail" sender:item];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"SegueShowDetail"]) {
VADetailViewController *detailViewController = segue.destinationViewController;
detailViewController.item = (VAItem*) sender; // <-- THIS ASSIGNMENT DOES NOT WORK
}
}
My debugging shows, the item is correctly passed down to prepareForSegue but the detailViewController.item is a wild pointer. It's not initialized and the = assignment does not have any effect.
I put a breakpoint in VADetailViewController's viewDidLoad and found out that the item variable is still the wild pointer address even though the assignment has taken place.
#interface VADetailViewController : UIViewController
// data
#property (nonatomic, retain) VAItem *item;
#end
item is also correctly synthesized.
Any help is much appreciated
Leo
If you're using a storyboard segue to transition to the next viewcontroller you don't need to use tableVeiew:didSelectRowAtIndexPath: in this situation. Depending how you have things set up that could be contributing to the problem.
In your storyboard, make sure you have drawn your push segue from your tableview's Prototype Cell to the detail view controller (i.e. from the cell, NOT from the tableViewController). If you have your storyboard set up that way your segue will be triggered as soon as the user selects a cell. A reference to the cell itself will be passed to prepareForSegue:sender: as the sender parameter. You can then inspect that object to find out which row was clicked in that method.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"SegueShowDetail"]) {
// sender will be the tableview cell that was selected
UITableViewCell *cell = (UITableViewCell*)sender;
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
VAItem *item = [category itemAtIndex:indexPath.row];
VADetailViewController *detailViewController = segue.destinationViewController;
detailViewController.item = item;
}
}
You really only want to use one of
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
to pass your data along to the next view controller. If you want to use the latter in your case, then you need to change your code to something along the lines of:
if ([segue.identifier isEqualToString:#"SegueShowDetail"]) {
VADetailViewController *detailViewController = segue.destinationViewController;
detailViewController.item = [catagory itemAtIndex:[self.tableView indexPathForSelectedRow].row];
}
I think you were getting an error because of what you assigned your sender. Here is the reference to the docs that explain what 'sender' should be.
https://developer.apple.com/library/IOs/#documentation/UIKit/Reference/UIViewController_Class/Reference/Reference.html#//apple_ref/occ/instm/UIViewController/performSegueWithIdentifier:sender:
Hope this helps.

How to display a list of item in tableview when the table did select row at index method is called?

I have an application in which I have tableview in which I return 3 rows in the tableview. When I select a particular row in the table view another tableview should open which should display list of items and when I select a particular item the value of that particular item should be set to the textlabel of previous tableview.
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
if (tableView != secondTableview) {
self.secondTableviewArray =[[NSMutableArray alloc]initWithObjects:#"secondfirst",#"secondSecond",nil];
[secondTableview reloadData];
}
}
Pass the second controller (the one that will appear) a weak reference to the first just before showing it. Once the second view controller has appeared and the user has made the selection (you can catch that in (-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;), send a message to the first giving the value selected.
The first view controller, then can store the new value, and even dismiss/pop the second.
This can also be achieved, by writing your own delegate protocol, so the first conforms to it, and is the delegate for the second.
Take NSString in second view and use the property.from the first view to second view set the string and in second view use that String
If you want to open new table view on click of row then you make new xib file and call like as define in below code:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
[tableView deselectRowAtIndexPath:indexPath animated:NO];
XMLBooksViewController * xmlBooksViewController = [[XMLBooksViewController alloc] initWithNibName:#"DetailView" bundle:nil];
XMLBooksAppDelegate * appDelegate = [UIApplication sharedApplication].delegate;
xmlBooksViewController.author = [appDelegate.authors objectAtIndex:indexPath.row];
[self.navigationController pushViewController:xmlBooksViewController animated:YES];
[xmlBooksViewController release];
}
In next file you define another table view, so the list or items comes as table view.