I have an application where the user selects a certain row in a TableView, and the contents of that Row will be sent to the next ViewController.
So far, I have connected the TableView Cell/Row to the next ViewController via a Segue. The issue I am facing right now is passing the value of the selected row to the next ViewController. I have written code for the didSelectRowAtIndexPath and the prepareForSegue functions.
Here are the functions (In ViewController.m file, the one with the TableView):
The didSelectRowAtIndexPath function:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"didSelectRowAtIndexPath entered");
//itemNameList is a mutable array where I get my TableView data from
NSMutableString *itemName = [itemNameList objectAtIndex:indexPath.row];
//selectedItem is a global mutable string I use to store the selected item to.
selectedItem = itemName;
UIAlertView *messageAlert = [[UIAlertView alloc]
initWithTitle:#"Row Selected"
message:itemName
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
// Display Alert Message
[messageAlert show];
}
The prepareForSegue function:
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSLog(#"prepareForSegue entered");
if ([[segue identifier] isEqualToString:#"Details"])
{
//DetailViewController is the next ViewController once the user
//selects an item
DetailViewController *detailViewController = [segue destinationViewController];
//the first selectedItem is a property of the second ViewController
//the next selectedItem is the global mutable string
detailViewController.selectedItem = selectedItem;
}
}
The problem is that the prepareForSegue function executes first before the didSelectRowAtIndexPath function. This is a huge issue because that means I don't actually get to pass the value to the second ViewController since the second ViewController has loaded into the screen already.
Does anyone know how to deal with this?
My idea was to somehow get the Row the user selected in the prepareForSegue method but I don't know how to do that either.
Yes, you are right. The prepareForSegue seems to execute before didSelectRowAtIndexPath. So you would need to get selected row in prepareForSegue.
Use the method:
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow]
Inside prepareForSegue to get selected row.
Remove the connection you made in the IB between the UItableViewCell and the ViewControllerB and add a segue between ViewControllera to ViewControllerB.
Add:
[self performSegueWithIdentifier:#"nextVC" sender:nil];
at the end of didSelectRowAtIndexPath.
That way prepareForSegue will be triggered after didSelectRowAtIndexPath finished processing.
In prepare for segue
if ([segue.identifier isEqualToString:#"YourIdentifier"]) {
TheNextViewController *tnvc = segue.destinationViewController;
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
[self tableView:self.tableView didSelectRowAtIndexPath:indexPath];
//then do whatever you have to do
//eg. set property in thenextviewcontroller by tnvc.propertyname = self..
Related
I have a controller with a listview that contain a custom cell.
I have created a modal segue from the cell to the next controller and I gave a name to this segue but when I click on the cell, prepareForSegue isn't called.
I could use the didSelectRowAtIndexPath and performSegueWithIdentifier but I have to send data to the next controller from the cell I've clicked on.
Any idea why the prepareForSegue isn't called? Or how to send data to the controller with another method?
You can get the cell data in didSelectRowAtIndexPath by getting the cell from the index of customCell that you clicked, and after you can performSegueWithIdentifier.
So let's make this easier:
Under your didSelectRowAtIndexPath method, add this line to get the properties of your customCell
YourCustomCell *customCell = (YourCustomCell *)[self.tableView cellForRowAtIndexPath:indexPath];
So now if you have something stored under your customCell you can access it from customCell instance.
After you managed to store or manipulate your data from customCell you do the performSegueWithIndenfier:
[self performSegueWithIdentifier:#"yourSegueID" sender:self];
Hope this helps
When you connect a segue from a cell to another view controller, unless it's a static table view, you'll have to trigger performSegueWithIdentifier from the didSelectRowAtIndexPath
After you execute performSegueWithIdentifier, prepareForSegue would be called.
In prepareForSegue, you can set up your destination view controller, which you can access via segue.destinationViewController
Like so:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
[super prepareForSegue:sender sender:sender];
if ([segue.identifier isEqualToString:#"YOUR_SEGUE_NAME_HERE"]) {
UIViewController *destinationViewController = segue.destinationViewController;
}
}
Or if you need to set things up in the didSelectRowAtIndexPath , you could do (iOS7+ only)
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self performSegueWithIdentifier:#"YOUR_SEGUE_NAME_HERE" sender:self];
UIViewController *destinationViewController = [self.transitionCoordinator viewControllerForKey:UITransitionContextToViewControllerKey];
}
Update:
Turns out #rdelmar is correct, it does work with dynamic cells.
All you have to do then is
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
[super prepareForSegue:sender sender:sender];
if ([segue.identifier isEqualToString:#"YOUR_SEGUE_NAME_HERE"]) {
NSIndexPath *selectedIndexPath = [self.tableView indexPathForCell:sender];
}
}
And you have the index path of the cell that was tapped. Then you can use that to query your datasource and find the object that was used to populate that cell.
I am trying to push data from a UITableViewCell into another view controller. I have tried two separate ways, instantiateViewControllerWithIdentifier and also PrepareForSegue. In both instances the ViewController loads correctly but the data is not being passed across (either null, or the first array value).
Here is my instantiateViewControllerWithIdentifier method, when I log the variable within my ViewController it just returns null.
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *testNumber = [jobNumberArray objectAtIndex:indexPath.row];
NSLog(#"Job is... %#",testNumber);
StandardInfoViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"StandardInfoViewController"];
controller.JobNr = [jobNumberArray objectAtIndex:indexPath.row];
[self.navigationController pushViewController:controller animated:YES];
}
Prepare For Segue
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self performSegueWithIdentifier:#"Details" sender: self];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:#"Details"]){
StandardInfoViewController *controller = (StandardInfoViewController *)segue.destinationViewController;
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
controller.jobNumber = [jobNumberArray objectAtIndex:indexPath.row];
}
}
When I use the prepareForSegue call I get the first row of my Array, which I understand because it doesn't know the cell, but I don't know how to identify the Cell within the prepareForSegue call.
I hope this makes sense, and any help or pointers would be greatly appreciated.
Thanks
If you drag the segue FROM the tableView-cell in the storyboard, you will then get the cell itself as parameter sender.
then you can use tableView indexPathForCell:sender to get the actual index of the selected cell. Then just fetch your object normally.
I.e. you will not need to implement didSelectRowAtIndexPath
If you still want to use didSelectRowAtIndexPath, just pass the cell as the sender parameter manually. I.e:
[self performSegueWithIdentifier:#"Details"
sender:[self.tableView cellForRowAtIndexPath:indexPath]]
Although your first version with instansiateViewController should work, judging by your code.
Another pattern is to subclass the cells themselves, and let them have a property that is the object that they want to display. Then you can just fetch the object directly from the cell without calling the data-array itself.
Is this what you want?
This is how you identify Cell in prepareForSegue method.
**UITableViewCell *cell=[myTable cellForRowAtIndexPath:path];**
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:#"Details"]){
StandardInfoViewController *controller = (StandardInfoViewController *)segue.destinationViewController;
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
**UITableViewCell *cell=[self.tableView cellForRowAtIndexPath:indexPath];**
controller.jobNumber = [jobNumberArray objectAtIndex:indexPath.row];
}
}
I have a simple table view that can segue to an update-view-contoller to edit that row when the user taps on a row. Issue: I would like to segue to the update-view-contoller when the table view is in "edit-mode", otherwise nothing should happen.
I am using Storyboard to create the segue linking the prototype cell to the update-view-controller.
Any idea on how to make the segue work only if the table view is in "edit-mode"?
Here is my prepare for segue code that is invoked when the user taps on a row from the table view contoller. My segue has an identerfied called "ShowUpdate":
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"ShowUpdate"]) {
UpdateViewController *updateviewcontroller = [segue destinationViewController];
NSIndexPath *myIndexPath = [self.tableView indexPathForSelectedRow];
int row = [myIndexPath row];
NSString *selectedRow = [NSString stringWithFormat:#"%d", row];
updateviewcontroller.DetailModal = #[_Title[row], _Description[row], selectedRow];
}
}
Thanks for any help
How about a simple if check for isEditing property of the tableView?
#property(nonatomic, getter=isEditing) BOOL editing
Instead of making a segue from a prototype cell, I would drag it from the ViewController itself, and then check the above property in the didSelectRowAtIndexPath: delegate method and perform the segue in code from there.
Plus, you would need to set allowSelectionDuringEditing property somewhere in viewDidLoad or so.
self.tableView.allowsSelectionDuringEditing = YES;
Code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (self.tableView.isEditing) {
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
[self performSegueWithIdentifier:#"ShowUpdate" sender:cell];
}
}
Segue construction:
I have a popover segue that goes to a view controller. I want to set some properties in the view controller when a cell is clicked. I tried using -prepareForSegue:, but then I realized that it doesn't have the scope of the selected cell. I know that I have to use:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
delegate method, but I'm not sure how that will affect the popover segue. Do I have to programatically create the popover, or is there another way?
Thanks much!
~Carpetfizz
Use self.tableView.indexPathForSelectedRow to get the data from your table's data source, e.g., the array backing the table data. If, for some reason, you don't want to get the data from your data source, then the sender is the UITableViewCell that was tapped to trigger the segue.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"YourSegueID"]) {
YourDestinationViewControllerClass* destinationController = segue.destinationViewController;
destinationController.somePublicProperty = self.yourTablesDataSourceArray[self.tableView.indexPathForSelectedRow.row];
UITableViewCell *cell = (UITableViewCell *)sender;
destinationController.someOtherPublicProperty = cell.textLabel.text;
}
}
So add an NSIndexPath propriety to your VC. Let's call it selectedCell. In your tableView:didSelectRowAtIndexPath method, save the selected index path to the instance variable.
Then in your prepareForSegue method, use the instance variable.
Alternately, in your prepareForSegue method, you could ask the table view for the currently selected row using the table view's indexPathForSelectedRow method.
The best approach is to use the indexPathForSelectedRow property.
For example you can write a code like this:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"YourSegueIdentifier"])
{
NSIndexPath *indexPath = [sender indexPathForSelectedRow];
NextViewController *vc = (NextViewController *)segue.destinationViewController;
vc.yourProperty = #"Some Value";
}
}
Assuming NextViewController as your segue destination controller and yourProperty as the property you want to set during the segue, you can access and set information from your parent controller because indexPathForSelectedRow automatically knows which cell has been clicked.
More information can be found here.
First I need to clarify that when I don't use a detail disclosure button and just the disclosure indicator everything works perfectly.
After quite a research I am struggling with how to pass the correct data from one tableview to another, when the detail disclosure button is tapped. I am using core data to store my data and I have created the appropriate segue (Item Details Segue) from the master UITableViewController (SGProfileTVC) to the detail one SGItemDetailsTVC.
Here is my implementation of prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"Add Item Segue"])
{
NSLog(#"Setting SGProfileTVC as a delegate of SGAddItemTVC");
SGAddItemTVC *sgAddItemTVC = segue.destinationViewController;
sgAddItemTVC.delegate = self;
sgAddItemTVC.managedObjectContext = self.managedObjectContext;
} else if([segue.identifier isEqualToString:#"Item Details Segue"])
{
NSLog(#"Setting SGProfileTVC as a delegate of SGItemDetailTVC");
SGItemDetailTVC *sgItemDetailTVC = segue.destinationViewController;
sgItemDetailTVC.delegate = self;
sgItemDetailTVC.managedObjectContext = self.managedObjectContext;
// Store selected item in selectedItem property
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
self.selectedItem = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSLog(#"Passing the selected item (%#) to SGItemDetailTVC", self.selectedItem.name);
sgItemDetailTVC.item = self.selectedItem;
} else {
NSLog(#"Unidentified Segue");
}
}
I have also implemented the tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath method
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:#"Item Details Segue" sender:[self.tableView indexPathForSelectedRow]];
}
What is happening is that it segues normally, however when the new tableview loads it doesn't contain any data.
Any suggestions would be very much appreciated!!
Thank you!
Does your new Table View have a Fetched Results Controller? It should have one as the fetched results controller manages automatically the data display (e.g. reloads) of your table view.
I guess that when you use an accessory button, the cell isn't selected.
Instead of getting the indexPath with indexPathForSelectedRow, try to get the cell from the sender (which should be the cell itself or the accessory button, I can't test it right now). Once you have the cell, use indexPathForCell: to get its indexPath
Imotep has the correct answer. The code looks like this:
UITableViewCell *cell = (UITableViewCell*)sender;
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];