I am using the prepareForSegue with my UISplitViewController and I have a NSMutableDictionary populating the UITableView with sections. What I am trying to do is pass the value of the selected value in the selected section to the method in my detailViewController, this is what I got so far:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"showDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
NSString *strPOIndex = [self.tableData[indexPath.row] valueForKey:#"Vendor"];
LHContactsDetail *controller = (LHContactsDetail *)[[segue destinationViewController] topViewController];
[controller setDetailItem:strPOIndex];
controller.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem;
controller.navigationItem.leftItemsSupplementBackButton = YES;
}
}
but the issue with this, it returns the numbered item of the first section. Example, if I click on section 2 item 1, it returns section 1 item 1, if I click on section 3 item 2, it returns section 1 item 2. I hope this makes sense. THis is how I am populating my tableview:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
NSString *sectionTitle = [contactSectionTitles objectAtIndex:indexPath.section];
NSArray *sectionContacts = [contactDirectoryFinal objectForKey:sectionTitle];
NSString *contacts = [[sectionContacts objectAtIndex:indexPath.row] valueForKey:#"Vendor_Name"];
cell.textLabel.text = contacts;
return cell;
}
How can I get the correct value to send to my detailController method ?
I wonder why your data source is different in cellforrow and prepare for segue.
If you manage the same way as sections and rows.
You need to get data from datasource firstly by indexpath.section and then indexpath.row change this in prepareforsegue method.
Related
I'm trying to pass data (array) with PrepareForSegue into a detailed VC. The destination VC has a label that receives a string. I thought I could transform the array into a string, and the rest was straightforward. This was achieved, though, but kind of wrongly, because, obviously, the text I get, regardless the row I click, returns me the whole object...And my goal is to get details of a specific row...Can someone help me? Guess this is basic stuff, but this is my first app...The method PrepareForSegue:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"detail"]) {
NSString *places = [_spots componentsJoinedByString:#" "];
DetailViewController * destination = segue.destinationViewController;
destination.detailedText = places;
}
EDIT:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
NSManagedObject *ls = [self.spots objectAtIndex:indexPath.row];
NSLog(#"Table Spots are: %#", ls);
[cell.textLabel setText:[NSString stringWithFormat:#"%#", [ls valueForKey:#"name"]]];
cell.imageView.image = [UIImage imageNamed:#"city.jpg"];
return cell;
}
As far as I understood, you want to performSegue() on a row selection and you want to pass a data specific to the selected row. In order to do this you can use the tableView.indexPathForSelectedRows() method, which is going to return an array with the indexPaths of all selected rows. Then you can get the UITableViewCells for this indexPaths and get their data.
You can pass the string value to the perforSegue method as the sender:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:#"detail" sender:[_spots objectAtIndex:indexPath.row]];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"detail"]) {
DetailViewController * destination = segue.destinationViewController;
destination.detailedText = sender;
}
}
I can't seem to find how to display another 'UIViewController' view when one row is clicked. So like when the row is clicked it goes to the next view and displays more information about row.
I think I need to use something like the - (void) tableView: (UITableView *) tableView didSelectRowAtIndexPath: (NSIndexPath *) indexPath delegate. Then my second question is how do I pass the data that was in that row, like the title, to the next view controller?
Thanks
If you are using a storyboard:
.h
#interface YourTableViewController : UIViewController
{
// you just need int selectedRow inside of #interface
int selectedRow;
}
.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// this method is going to give you NSIndexPath *indexPath
selectedRow = indexPath;
[self performSegueWithIdentifier:#"nameOfYourSegue" sender:self];
}
You might already have this method, add the code inside of it if that's the case. This method is called when a segue is about to perform.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([[segue identifier] isEqualToString:#"nameOfYourSegue"]){
//Pass data from one controller to the next
[[segue destinationViewController] setRowIndex:selectedRow];
}
}
selectedRow is going to have the index of which row was selected with 0 being the first row.
In your next view controller's class:
.h
- (void)setRowIndex:(int)rowIndex;
.m
- (void)setRowIndex:(int)rowIndex
{
//assign your variable to rowIndex
}
Once you have this working, you can edit this code to pass an array of information.
CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
NSString *title = cell.textLabel.text;
Like #rmaddy said, Its better if you do some research, This is very basic, I would recommend you the:
http://www.raywenderlich.com/
Site for great tutorial, they really have every thing you need there.
Here is a hypothetical example:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath];
MyCustomViewController *vc = [[MyCustomViewController alloc]initWithTitle:selectedCell.textLabel.text];
// Assuming your UITableViewDelegate has a navigationController
[self.navigationController presentViewController:vc animated:YES completion:nil];
}
use the method:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
you can get the cell by the index path:
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
the code:
- (void)tableView:(UITableView *)tblView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
}
after getting the cell data youc can use it
answer to question 2:
you can connect in the interface builder your cell with the next vc by seque and in the method prepareforseque just pass the data to the
segue.destenationViewController.yourPoperty
now in the viewdidload method:
self.title = self.property
Currently I just have a segue hooked up to a UITableView that pops to the next UITableView in the navigation stack.
What I need to do though now instead, is set up an if statement based on the text in the UITableView cell, to decide which of two possible UIViewControllers to pop to.
So ViewController2 needs to be able to pop either":
- back to ViewController1
OR
- forward to ViewController3
based on the text in the UITableView cell that is populated by JSON.
Can you help me out with that? Will post any code needed. Thanks!
Current ViewController:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"standardCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
Spring *spring = [springs objectAtIndex:indexPath.section];
Leaf *leaf = [spring.leafs objectAtIndex:indexPath.row];
cell.textLabel.text = leaf.shortName;
return cell;
}
- (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:#"themes"]) {
UITableViewCell *cell = (UITableViewCell*)sender;
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
// Get reference to destination view controller
MRTViewController *tvc = (MRTViewController *)[segue destinationViewController];
}
}
UPDATE
Added code per #troops but still not sure how to pop to VC3
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
NSString *stringToCheck = #"AAA";
if([cell.textLabel.text isEqualToString:stringToCheck])
{
//Pop to VC1
[self.navigationController popToViewController:[[self.navigationController viewControllers]objectAtIndex:0] animated:YES];
} else {
//Pop to VC3, not sure what to put here
// Pass the info
tvc.spring = [springs objectAtIndex:indexPath.section];
tvc.leaf = [tvc.spring.leafs objectAtIndex:indexPath.row];
tvc.buttonNumber = _buttonNumber;
}
}
You could simply check the cell's textLabel's string: If you really want to use segues and not the tableView's delegate method of: didSelectRowAtIndexPath: That's why I based my answer off what your initial question/code looks like.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
NSString *stringToCheck = #"AAA";
if ([cell.textLabel.text isEqualToString:stringToCheck])
{
// Pop to VC1
[self.navigationController popToRootViewControllerAnimated:YES];
}
else
{
// Push to VC3
MRTViewController3 *tvc = [self.storyboard instantiateViewControllerWithIdentifier:#"YourID"];
tvc.spring = [springs objectAtIndex:indexPath.section];
tvc.leaf = [tvc.spring.leafs objectAtIndex:indexPath.row];
tvc.buttonNumber = _buttonNumber;
[self.navigationController pushViewController:tvc animated:YES];
}
}
Use delegate method, get cell, compare text.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if([cell.textLabel.text isEqualToString:#"Your String"])
{
//Pop or present view controller
}
}
I've got a problem, I created a UITableView using CoreData to pass data between the views (UITableView to DetailView of a row) I used this method:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"UpdateData"]) {
NSManagedObject *selectedDevice = [self.datas objectAtIndex:
[[self.tableView indexPathForSelectedRow] row]];
DetailViewController *destViewController = segue.destinationViewController;
destViewController.datas = selectedDevice;
}
}
and everything works fine. But later I added a UISearchBar that didn't work with NSManagedObject so i created another NSMutableArray where I saved all the NSStrings composing the cell.textLabel.textand it works. But now I need to modify the prepareForSegue in order to perform a segue whenever I select a row from the SearchBar tableView.
The problem is that to perform this kind of segue connecting with the CoreData I need to use the NSManagedObject, so my question is:
How can i get the indexPath.row from the UISearchBar inside my UITableView and make it correspond to the right indexPath.row of my self.data (that is the array I used to perform the normal segue in the UITableView, see code) ?
I thought I could compare the strings of the cell (cell.textLabel.text) but
don't know how to do.
there could be a problem if there are 2 rows with same name I suppose.
any advice?
EDIT: I added the UISearchBar from the storyboard and this is the code i use to filter the main table view:
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
[_searchDati removeAllObjects];
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"SELF contains[cd] %#", searchText];
_searchDati = [NSMutableArray arrayWithArray:[_tableData filteredArrayUsingPredicate:resultPredicate]];
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller
shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString
scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar
selectedScopeButtonIndex]]];
return YES;
}
Where _searchDati is the array i created for the UISearchBar and _tableData is the array i created to store the name of the cells from the NSManagedObject that contains the CoreDatas.
and i also added this
- (void)searchDisplayController:(UISearchDisplayController *)controller didLoadSearchResultsTableView:(UITableView *)tableView
{
[tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"Cell"];
}
//Handle selection on searchBar
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
[self performSegueWithIdentifier: #"UpdateData" sender: self];
}
}
The first because i used this and i need to register the class or gave me an error
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
and the second one to handle the selection of the UISearchBarTableView.
In here i load the tableView as i said before
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (tableView == self.searchDisplayController.searchResultsTableView) {
// see filter method
cell.textLabel.text = [_searchDati objectAtIndex:indexPath.row];
} else {
NSString *string;
NSManagedObject *data = [self.datas objectAtIndex:indexPath.row];
string = [NSString stringWithFormat:#"%#", [data valueForKey:#"name"]];
[cell.detailTextLabel setText:nil];
cell.textLabel.text = string;
//store the string to the tableView array
[_tableData addObject:string];
}
return cell;
}
EDIT#2:
i Wrote this code
NSIndexPath *indexPath = nil;
NSIndexPath *indexPath2 = nil;
if ([self.searchDisplayController isActive]) {
indexPath = [self.searchDisplayController.searchResultsTableView indexPathForSelectedRow];
for(int i = 0 ; i < _tableData.count ; i++){
if([self.searchDati[indexPath.row] isEqualToString:_tableData[i]]){
indexPath2 = [NSIndexPath indexPathForRow:i inSection:1];
}
}
NSManagedObject *selectedDevice = [self.datas objectAtIndex:indexPath2.row];
destViewController.datas = selectedDevice;
when i click on a row on the UISearchBar and it WORKS only for the first time! I make the search and click on the row and it goes to the right DetailView, but if i do it again it crashes giving me this error
Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 5 beyond bounds [0 .. 4]'
i think the problem is that does not start from the indexPath.row == 0 but create a sort of pile after the first time.
Any suggestion? Thanks in advance :)
Try this,
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"UpdateData"]) {
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
NSManagedObject *selectedDevice = [self.datas objectAtIndex:indexPath.row];
DetailViewController * destViewController = [segue destinationViewController];
destViewController.datas = selectedDevice;
}
}
So what I am trying to do is I have an NSMutableArray of data I need to pass to another UITableViewController. This NSMutableArray is an array of NSDictionaries that contain the information I want to be displayed in the title of each table view cell. Here is my code before I segue.
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSIndexPath* indexPath = [self.tableView indexPathForCell:sender];
if ([segue.identifier isEqualToString:#"Title Query"]) {
UITableViewCell* cell = [self.tableView cellForRowAtIndexPath:indexPath];
NSString* cellText = cell.textLabel.text;
NSMutableArray* photosToBeShown = [self titleQuery:cellText];
if ([segue.destinationViewController respondsToSelector:#selector(setPhotoTitles:)]) {
[segue.destinationViewController performSelector:#selector(setPhotoTitles:) withObject: photosToBeShown];
NSLog(#"%#", photosToBeShown);
}
}
}
The method setPhotoTitles: that is called by the performSelector: withObject: is the setter of the property (NSMutableArray* ) photoTitles on the UITableViewController that I am seguing to because I wanted to collect the array so I could then call the methods below to set the titles of my table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Photo Title Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.textLabel.text = [self titleForRow:indexPath.row];
return cell;
}
- (NSString *) titleForRow: (NSUInteger) row
{
return self.photoTitles[row];
}
What happens when I run this code is I end up in an infinite loop with calls to my setter method (setPhotoTitles:). Now my question is what is the right conceptual way to get around this problem or how can I implement it this way without ending up in an infinite loop. I have all the information I need in my array but I need to pass the array to the new controller but also be able to use the UITableViewCell method to set the rows titles.
In the prepareForSegue: method, rather than overriding setPhotoTitles:, you should create a NSArray property in the destination view controller, as pass the photoTitles array to the NSArray property of the destination view controller. So your prepareForSegue method would look like this:
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSIndexPath* indexPath = [self.tableView indexPathForCell:sender];
if ([segue.identifier isEqualToString:#"Title Query"]) {
UITableViewCell* cell = [self.tableView cellForRowAtIndexPath:indexPath];
NSString* cellText = cell.textLabel.text;
NSMutableArray* photosToBeShown = [self titleQuery:cellText];
YourCustomViewController *customViewController = segue.destinationViewController;
customViewController.photosArrayProperty = photosToBeShown;
}
}