I have a UITableViewController with three sections and a variable number of cells for each one and when I tap on a cell, I have a detail view.
I use a segue to send the information to my other view. But I saw that if I the for example the first cell of the second section I will have the first cell of my first section. I tried to fix that by indicating the section and the cell but my program doesn't recognize indexPath.section.
Here is my code of the segue :
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue isKindOfClass:[TLAnimatedSegue class]]) {
((TLAnimatedSegue *) segue).delegate = self;
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
TVDetailsViewController *destViewController = segue.destinationViewController;
destViewController.details = [[_scheduleList objectAtIndex:indexPath.section]objectAtIndex:indexPath.row];
}
}
I don't understand why I have en error with that because when I po my indexPath, I have two element inside. With the breakpoint, this line seems to be the reason of the crash :
destViewController.details = [[_scheduleList objectAtIndex:indexPath.section]objectAtIndex:indexPath.row];
Thanks for your help.
Thank you #zcui93 and #Rohit KP, you open my mind and eyes so here is the solution to tackle this little issue and now it works :
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue isKindOfClass:[TLAnimatedSegue class]]) {
((TLAnimatedSegue *) segue).delegate = self;
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
TVDetailsViewController *destViewController = segue.destinationViewController;
if(indexPath.section == 0) {
destViewController.details = [_pastScheduleList objectAtIndex:indexPath.row];
}
if(indexPath.section == 1) {
destViewController.details = [_currentScheduleList objectAtIndex:indexPath.row];
}
if(indexPath.section == 2) {
destViewController.details = [_futurScheduleList objectAtIndex:indexPath.row];
}
}
}
Use didSelectRowAtIndexPath method and get cell content using as describe below and perform segue
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
....
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
String id = cell.textLabel.text;
/*... perform segue operation... */
....
}
/* ... for( Customcell *cell = (CustomCell*)... ) ...
Related
I have searched answers for this question but I didn't find any relevant answers. In my table view when I touch one cell, a Detail View is pushed. Source code :
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:#"trajetDetail" sender:tableView];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"trajetDetail"]) {
RechercheDetailViewController *detailViewController = segue.destinationViewController;
if (sender == self.searchDisplayController.searchResultsTableView)
{
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
NSString *destinationTitle = [[self.trajets objectAtIndex:[indexPath row]] name];
[detailViewController setTitle:destinationTitle];
}
}
}
The question is that when users make a search with the UISearchBar (and UISearchDisplayController) that they could also touch the cell and be pushed to the detail view.
Sorry for my english and if it's not clear, just tell me I'll try to make it clearer.
Kindest regards
what wrong i see in your code is in your prepareforsegue method, you are selecting indexpath of standard tableView not the search tableview, this below line needs to be changed
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
the prepareForSegue would become something like this after the change
you should use this code instead
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"trajetDetail"]) {
RechercheDetailViewController *detailViewController = segue.destinationViewController;
if (sender == self.searchDisplayController.searchResultsTableView)
{
NSIndexPath *indexPath = [self.searchDisplayController.searchResultsTableView indexPathForSelectedRow];
NSString *destinationTitle = [[self.trajets objectAtIndex:[indexPath row]] name];
[detailViewController setTitle:destinationTitle];
}
}
}
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;
}
}
I'm having big problems learning how to pass data from the cell accessory button to the detail view controller with segues. If I tap the cell the data is passed to the detail view controller in a NSString *selectedItem; and the label on the other view updates with the word that was in the cell -
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:#"toDetail" sender:self];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"toDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
DrugDetailViewController *destViewController = segue.destinationViewController;
destViewController.selectedItem = [[candyArray objectAtIndex:[indexPath row]] name];
}
}
And i connect a segue on the storyboard from the cell to the detail view with a name "toDeatil" It works when i click the cell it goes to the detail screen, but if i put
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:#"toDetail" sender:self];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"toDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
DrugDetailViewController *destViewController = segue.destinationViewController;
destViewController.selectedItem = [[candyArray objectAtIndex:[indexPath row]] name];
}
}
Almost exactly the same but from the accessorybutton and I connect a segue on the storyboard from the cell accessorybutton to the detail view with a name "toDetail" when I click the cell it goes to the detail screen, but the information is not there, the label dosent get filled out or just uses the first cell detail all the time.
I've tried heaps of combinations. I've tried making my own accessory button, a UIButton in a custom cell but it just uses the first cells details all the time.
The goal is to get didSelectRowAtIndexPath going to one view and accessoryButtonTappedForRowWithIndexPath going to a different view but I need to tell accessoryButtonTappedForRowWithIndexPath what cell was pressed.
Hope this makes sence.
Thank you
You can pass the indexPath as sender to the performSegueWithIdentifier call, like this:
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:#"toDetail" sender:indexPath];//IndexPath as sender
}
And in your prepareForSegue method:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"toDetail"]) {
//Detect sender class and act accordingly
NSIndexPath *indexPath = [sender isKindOfClass:[NSIndexPath class]] ? (NSIndexPath*)sender : [self.tableView indexPathForSelectedRow];
DrugDetailViewController *destViewController = segue.destinationViewController;
destViewController.selectedItem = [[candyArray objectAtIndex:[indexPath row]] name];
}
}
You don't need to implement accessoryButtonTappedForRowWithIndexPath.
When prepareForSegue is fired from an accessory view, the sender:(id)sender parameter is set to the UITableViewCell which contains the accessory view. You can then ask the table view for the index of that cell (not the selected index).
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"toDetail"] && [sender isKindOfClass:[UITableViewCell class]]) {
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
DrugDetailViewController *destViewController = segue.destinationViewController;
destViewController.selectedItem = [[candyArray objectAtIndex:[indexPath row]] name];
}
}
That is because [self.tableView indexPathForSelectedRow] will return a NSIndexPath only if the ROW is tapped, not when the accessory button is tapped.
In accessoryButtonTappedForRowWithIndexPath, you will need to pass indexPath.row to the prepareForSegue method manually. Try this:
NSInteger selectedRow;
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
selectedRow = indexPath.row;
[self performSegueWithIdentifier:#"toDetail" sender:self];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"toDetail"])
{
DrugDetailViewController *destViewController = segue.destinationViewController;
destViewController.selectedItem = [[candyArray objectAtIndex:selectedRow] name];
}
}
What about, if you drive state of what is selected on application delegate! Answer of Raul Juarez do the work, but the target of sender parameter is other!
I have an app that use UITableView, and i need to know what item was touched on accessory button in above screen!
To manage this scenery, i have one property on application delegate, that i update when i touch table view accessory button; so in the next screen, i can retrieve the item touched reading the property of application delegate.
In general, i use de application delegate to code model application (dividing necessary class), so views instances only interact with this model to update necessary states! what i do is focus view work independent of model work!
Maybe some like this:
// Property on Application Delegate
#propety (atomic,readwrite) NSIndexPath* indexTapped;
// Method on UITableViewDelegate instance
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
UIMyAppDelegate* model = (UIMyAppDelegate*)[[UIApplication shared] delegate];
model.indexTapped = indexPath;
[self performSegueWithIdentifier:#"toDetail" sender:self];
}
// Method on another View Controller
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
UIMyAppDelegate* model = (UIMyAppDelegate*)[[UIApplication shared] delegate];
if ([segue.identifier isEqualToString:#"toDetail"]) {
NSIndexPath *indexTapped = model.indexSelected;
DrugDetailViewController *destViewController = segue.destinationViewController;
destViewController.selectedItem = [[candyArray objectAtIndex:[indexPath row]] name];
}
}
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;
}
}
I have the following problem: The segue should only be performed if the UITableViewCellSelectionStyleNone is true.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (cell.selectionStyle == UITableViewCellSelectionStyleNone) {
[self performSegueWithIdentifier:#"Update" sender:indexPath];{
}
}
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"Update"]) {
NSManagedObject *selected = [self.travelA objectAtIndex:[[self.tableView indexPathForSelectedRow] row]];
TravelVC *destViewController = segue.destinationViewController;
destViewController.travel = selected;
destViewController.positionString = _positionStringA;
}
}
But this doesn't work. The segue will also be performed if the cell selection style is not UITableViewCellSelectionStyleNone. Maybe there's a way to combine both methods.
Thanks in advance.
You shouldn't be basing your logic on the cell setting of UITableViewCellSelectionStyleNone. Instead, you should be using the indexPath to interrogate your model and find out if selection is available / sensible for that item.