Passing data between table views - ios

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;
}
}

Related

Issue to choose a cell in a section of a TableView

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*)... ) ...

Objective-C prepareForSegue Issue

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.

Search through the array in the searchbar

I took advantage of this article to create a search in my application. Slightly changed the code for yourself. But if I add to the array of multiple values (1-5), the search is working perfectly, but if you add an array of say 15 value, then an error takes off
2015-05-07 12:24:55.852 gfhfgh[5662:60b] *** Assertion failure in -[UITableView dequeueReusableCellWithIdentifier:forIndexPath:], /SourceCache/UIKit/UIKit-2935.138/UITableView.m:5439
2015-05-07 12:24:55.860 gfhfgh[5662:60b] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier SearchResultsTableViewUITableViewCell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'
*** First throw call stack:
(0x2e2fdf83 0x38aaeccf 0x2e2fde5d 0x2ecabd5b 0x30c86dff 0xf00b9 0x30c4d8f7 0x30bf4c27 0x30be7c5d 0x30be7ba3 0x30bf41cd 0x30b3a167 0x30be71eb 0x30bc86fd 0x30bc848d 0x30bc9bf5 0x30c7dcc5 0x30d0c011 0x30c36109 0x30d0abcd 0x30d0aaaf 0x30b50037 0x30b4ffd7 0x30b4ffb1 0x30b3b717 0x30b3acfd 0x30b37805 0x30c16f15 0x30d0a91f 0x30b99127 0x30b993e7 0x30c15c17 0x30e9e579 0x30ca0fed 0x30b4c4e3 0x30ef3aed 0x30b13353 0x30b11a9b 0x30b4ad49 0x30b4a66f 0x30b1f8cd 0x30b1df77 0x2e2c920b 0x2e2c86db 0x2e2c6ecf 0x2e231ebf 0x2e231ca3 0x33137663 0x30b7e14d 0xfa1a9 0x38fbbab7)
libc++abi.dylib: terminating with uncaught exception of type NSException
What is the problem?
My code
#import "StreetTableViewController.h"
#interface StreetTableViewController ()
//#interface StreetTableViewController () <UISearchDisplayDelegate>
// the items to be searched
#property (nonatomic, copy) NSArray *items;
// the current search results
#property (nonatomic, copy) NSArray *searchResults;
#end
#implementation StreetTableViewController
#pragma mark - NSCoding
// set some initial searchable items
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
_items
= [NSArray arrayWithObjects: #"bar", #"foo", #"quux",#"bar2", #"foo2", #"quux2",#"bar3", #"foo3", #"quux3",#"bar4", #"foo4", #"quux4",#"bar5", #"foo5", #"quux5",#"bar6", #"foo6", #"quux6",
#"bar7", #"foo7", #"quux7",#"bar8", #"foo8", #"quux8",#"bar9", #"foo9", #"quux9",#"bar10", #"foo10", #"quux10", nil];
}
return self;
}
#pragma mark - UISearchDisplayDelegate
// register a cell reuse identifier for the search results table view
-(void)searchDisplayController:(UISearchDisplayController *)controller
didLoadSearchResultsTableView:(UITableView *)tableView {
[tableView registerClass:[UITableViewCell class]
forCellReuseIdentifier:#"SearchResultsTableViewUITableViewCell"];
}
// perform the search
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller
shouldReloadTableForSearchString:(NSString *)searchString {
NSPredicate *predicate
= [NSPredicate predicateWithFormat:#"self beginswith [c] %#", searchString];
NSArray *searchResults
= [[self items] filteredArrayUsingPredicate:predicate];
[self setSearchResults:searchResults];
return YES;
}
#pragma mark - UITableViewDataSource
// check if displaying search results
-(NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
if ([[self searchDisplayController] isActive]) {
return [[self searchResults] count];
} else {
return [[self items] count];
}
}
// check if displaying search results
-(UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if ([[self searchDisplayController] isActive]) {
UITableViewCell *cell
= [tableView dequeueReusableCellWithIdentifier:#"SearchResultsTableViewUITableViewCell"
forIndexPath:indexPath];
id item = [[self searchResults] objectAtIndex:[indexPath row]];
[[cell textLabel] setText:item];
return cell;
} else {
UITableViewCell *cell
= [tableView dequeueReusableCellWithIdentifier:#"UITableViewCell"
forIndexPath:indexPath];
id item = [[self items] objectAtIndex:[indexPath row]];
[[cell textLabel] setText:item];
return cell;
}
}
#pragma mark - UITableViewDelegate
// manually perform detail segue after selecting a search result
-(void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if ([[self searchDisplayController] isActive]) {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
[self performSegueWithIdentifier:#"detailSegue" sender:cell];
}
}
#pragma mark - UIViewController
/* prepare for detail scene segue
called after cell selection in the master and
search results table views */
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
UITableViewCell *cell = (UITableViewCell *)sender;
id item = nil;
if ([[self searchDisplayController] isActive]) {
NSIndexPath *indexPath
= [[[self searchDisplayController] searchResultsTableView] indexPathForCell:cell];
item = [[self searchResults] objectAtIndex:[indexPath row]];
} else {
NSIndexPath *indexPath
= [[self tableView] indexPathForCell:cell];
item = [[self items] objectAtIndex:[indexPath row]];
}
UIViewController *detail
= (UIViewController *)[segue destinationViewController];
[[detail navigationItem] setTitle:item];
}
#end
I may understood your problem (or may not...). Check if you wrote the cell identifier (SearchResultsTableViewUITableViewCell) in your SearchResultsTableViewUITableViewCell.nib file (or SearchResultsTableViewUITableViewCell.m file instead).
I think that you didn't reused cells at all. For 5 cells you didn't need that, because the UITableView needed to create 5 cells anyway (the dequeue method create (tableHeight / cellHeight) cells, and all other cell are reused).
Another option, if you registered your class but not your nib file:
add
[self.yourTableView registerNib:[UINib nibWithNibName:#"SearchResultsTableViewUITableViewCell" bundle:nil]
forCellReuseIdentifier:#"SearchResultsTableViewUITableViewCell"];

Seguing data from one UITableViewController to another UITableViewController via performSelector:withObject:

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;
}
}

Search Bar with prepareForSegue for DetailViewController

I'm implementing the search bar for my project. The search part is ok. I can display the raw data and filter the data with the search text. When I tap on the cell of the search result tableView, it didn't transition to the detail view. But for the raw data I can transition to the detail view. I use the prepareForSegue method as I'm using storyboard.
Here's the code so far,
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString: #"Book Detail Segue"]) {
BookDetailsTVC *bookDetailsTVC = segue.destinationViewController; // for the detail view controller
bookDetailsTVC.delegate = self;
if (self.tableView == self.searchDisplayController.searchResultsTableView) { // The if block is not working
NSLog(#"Search Display Controller");
bookDetailsTVC.book = [self.searchResults objectAtIndex: self.searchDisplayController.searchResultsTableView.indexPathForSelectedRow.row];
} else {
NSLog(#"Default Display Controller");
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
bookDetailsTVC.book = [self.objects objectAtIndex: indexPath.row];
}
}
}
When I tried using didSelectRowAtIndexPath method, I can transition to the detail view. But I got error message like that:
Finishing up a navigation transition in an unexpected state. Navigation Bar subview tree might get corrupted.
Unbalanced calls to begin/end appearance transitions for <BookDetailsTVC: 0x69b5300>.
Here's the code for didSelectRowAtIndexPath:
- (void) tableView: (UITableView *) tableView didSelectRowAtIndexPath: (NSIndexPath *) indexPath
{
BookDetailsTVC *bookDetailsTVC = [[BookDetailsTVC alloc] init];
if (tableView == self.searchDisplayController.searchResultsTableView) {
bookDetailsTVC.book = [self.searchResults objectAtIndex: self.searchDisplayController.searchResultsTableView.indexPathForSelectedRow.row];
[self performSegueWithIdentifier: #"Book Detail Segue" sender:self];
NSLog(#"Search Display Controller");
} else {
bookDetailsTVC.book = [self.objects objectAtIndex: indexPath.row];
[self performSegueWithIdentifier: #"Book Detail Segue" sender: self];
NSLog(#"Default Display Controller");
}
}
Thanks for help.
The problem is solved,
Change this in cellForRowAtIndexPath
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: CellIdentifier];
to
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier: CellIdentifier];
and in the prepareForSegue method, you can use the self.searchDisplayController.active to check the current tableview
if (self.searchDisplayController.active) {
NSLog(#"Search Display Controller");
bookDetailsTVC.book = [self.searchResults objectAtIndex: self.searchDisplayController.searchResultsTableView.indexPathForSelectedRow.row];
} else {
NSLog(#"Default Display Controller");
bookDetailsTVC.book = [self.objects objectAtIndex: self.tableView.indexPathForSelectedRow.row];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
self.selectedPerson = [self.searchResults objectAtIndex:indexPath.row];
PersonasDetalle *pd = [self.storyboard instantiateViewControllerWithIdentifier:#"PersonaDetalle"];
pd.persona = self.selectedPerson;
[self.navigationController pushViewController:pd animated:YES];
}
}

Resources