indexPath has no value in Table View - ios

I have a table view displaying a list of contacts and when you click a cell, a detail view controller pops up with labels showing the contact name, number, fax, and address. My problem is that every time I click a cell, the first value in the plist pops up no matter which cell i click. I found my error in indexPath when NSLog(#"%#",indexPath); returned null everytime. I think the problem is in this method but it looks the same in other classes and works fine.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"showCountyInfo"])
{
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
NSLog(#"%#",indexPath);
TSPBookCoverViewController *destViewController = segue.destinationViewController;
destViewController.countyName = [[self.books objectAtIndex:indexPath.row] objectForKey:#"Name"];//error
destViewController.phoneName = [[self.books objectAtIndex:indexPath.row] objectForKey:#"Phone"];
destViewController.faxName = [[self.books objectAtIndex:indexPath.row] objectForKey:#"Fax"];
destViewController.addressName = [[self.books objectAtIndex:indexPath.row] objectForKey:#"Address"];
}
}
When I enter integers instead of indexPath.row, it works like it should, so the problem is here I just can't find it. Sorry if it's obvious I've tried looking for awhile now!
Edit:
Here is didSelectRowAtIndexPath method:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
// Perform Segue
[self performSegueWithIdentifier:#"showCountyInfo" sender:self];
}

The issue is with the following code:
[tableView deselectRowAtIndexPath:indexPath animated:YES];
You are deselecting the cell inside the didSelectRowAtIndexPath: method. So the indexPathForSelectedRow will always return nil

Best way for you to resolve it is to get the Dictionary from self.books array in
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
and then perform the segue and get values from that dictionary in prepareForSegue

Related

Is using tableView:willSelectedRowAtIndexPath: correct way to pass variable?

Hello i am trying to pass variable with segue.
I am getting variable to pass with tableView:willSelectedRowAtIndexPath: is this correct way? If it is not, how should i achieve this? (Note: It is working like this.)
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
selectedCoffeeShop = [coffeeShops objectAtIndex:indexPath.row];
return indexPath;
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"coffeeShopDetailSegue"]) {
CoffeeShopDetailViewController *controller = (CoffeeShopDetailViewController *)segue.destinationViewController;
[segue destinationViewController];
controller.coffeeShop = selectedCoffeeShop;
}
}
If your segue is made from the cell itself, then there is no need to implement either willSelectRowAtIndexPath or didSelectRowAtIndexPath. You only need prepareForSegue:sender: since the sender argument will be the cell, and you can use that to get the indexPath you need,
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(UITableViewCell *)sender {
if ([segue.identifier isEqualToString:#"coffeeShopDetailSegue"]) {
NSInteger row = [self.tableView indexPathForCell:sender].row;
CoffeeShopDetailViewController *controller = segue.destinationViewController;
controller.coffeeShop = coffeeShops[row];
}
}
That way to do it is absolutely fine.
Another way would be to remove the automatic segue trigger from storyboards and instead implement:
tableView:didSelectRowAtIndexPath: to call performSegueWithIdentifier:sender:.
It could look like this:
- (NSIndexPath *)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
selectedCoffeeShop = [coffeeShops objectAtIndex:indexPath.row];
[self performSegueWithIdentifier:#"coffeeShopDetailSegue" sender:self];
return indexPath;
}
In that case you still need your implementation of prepareForSegue:sender:.
You could also do it completely without segues, using UINavigationController, but then you'd have to instantiate the CoffeeShopDetailViewController programmatically as well.
Your approach is perfectly fine though!
As noted in the comments, you can remove [segue destinationViewController];, since this returns the destination view controller which you already saved in the variable controller in the line right above. :)

TableViewController's cells from .plist file

I am currently making a tableView for my app, and I've got my cells from a .plist file. However I want to be able to enter my second view controller no matter, what cell I click on, the difference should be that the UIText for my second viewController will depend on which cell I click.
So far, I have imagined something like;
if(tableview.tag == 1){
myUIText.text = #"a";
}else if(tableview.tag == 2){
myUIText.text = #"b";
etc.......
Will this work using the same view controller, same UIText? If not, then how?
And how can I set the tags for my array in my .plist file?
I would appreciate your answers
EDIT: Here is a sample of two methods;
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.items count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString * id = #"plistdata";
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:id forIndexPath:indexPath];
if(cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:id];
}
cell.textLabel.text = self.items[indexPath.row];
return cell;
}
More information is certainly needed by I will make some assumptions on your code and see if it helps any. I assume the the text you want to send is the title of the cell the person clicked on. Now how you send that to the next VC will differ depending on if you are using a segue or not. If you are using a segue you can do something like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"mySegue"]) {
UITableViewCell *cell = (UITableViewCell *)sender;
UpcomingViewController *viewController = segue.destinationViewController;
viewController.textView.text = cell.chapterText;
}
}
However if you are using didSelectRowAtIndexPath then:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UpcomingViewController *viewController = [[UpcomingViewController alloc] init];
viewController.textView.text = self.arrayOfChapterText[indexPath.row];
[self.navigationController pushViewController:viewController animated:YES];
}
Now again because your question wasn't very clear on your scenarios I have taken some liberties with what I think you are probably trying to do. I assume that the view controller that will show after you choose an item will have the UITextView as a public property so that you can set it before pushing or segueing to your new view. I also assume in the didSelectRowAtIndexPath that you are using a UINavigationController.
Hope this helps.

didSelectRowAtIndexPath and prepareForSegue implementation

I have two methods :
- (void)tableView: (UITableView *)tableView didSelectRowAtIndexPath: (NSIndexPath *)indexPath
{
testNumber=indexPath.row;
testNumber=testNumber+1;
NSLog(#"Test number : %i",testNumber);
}
then
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([[segue identifier] isEqualToString:#"testStart1"])
{
tViewController *tvc = [segue destinationViewController];
tvc.testNumberTVC=testNumber;
}
}
I have got also a segue who is triggered by selecting a row in my UITableView.
My problem is when I select a row, prepareForSegue is acting before didSelectRowAtIndexPath so the new value of testNumber is not transferred.
I would like to implement prepareForSegue only when didSelectRowAtIndexPath is done or better:
Transfer the value of testNumber using only didSelectRowAtIndexPath method and so removing prepareForSegue method.
I've seen few topics about transfering data from UITableView to DetailView but with the new xCode 5, when an error message appears, I don't really know if this is because solution is outdated or if there is a real error is my code.
You could create your segues between the view controllers rather than from cell selection and set the identifier of the segue in the storyboard. Then you could do something like this:
- (void)tableView: (UITableView *)tableView didSelectRowAtIndexPath: (NSIndexPath *)indexPath
{
testNumber=indexPath.row;
testNumber=testNumber+1;
NSLog(#"Test number : %i",testNumber);
[self performSegueWithIdentifier:#"mySegueIdentifier" sender:self];
}
Another option would be to check out the answer to this link here. Basically you can get the cell indexPath in prepareForSegue like this:
UITableViewCell *cell = (UITableViewCell*)sender;
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];

How to pass and collect info what was pressed in UITableView

I need more help with my first Table View app.
I have .plist with continents, countries and other info:
I work with a StoryBoard and I have 3 View Controllers (MasterVC - for continents, MiddleVC - for countries, DetailVC - for detailed info). I have already continents displayed in UITableView on my MysterVC. Now I want to pass information about what was pushed to the MiddleVC, for example that Europe was pushed and the to display European countries in a table view on my MiddleVC.
I guess that I should do it in didSelectRowAtIndexPath:
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
//is it correct way to catch the info about which continent was pressed?
NSString *ContinentName = [[world allKeys] objectAtIndex:indexPath.row];
//I don't know how to pass the info forward
}
Or maybe I should use segue for passing data?
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showMiddle"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
NSDate *object = _objects[indexPath.row];
//??is everything what needed here??
[[segue destinationViewController] setDetailItem:object];
}
}
And I am not sure how to deal with the passed info from MasterVC, here in MiddleVC. Although I prepared some initial coding:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
//I think countries arrays will be needed here
NSArray *namesOfAfricanCountries = [africa allKeys];
NSString *AfricanCountryName = [namesOfAfricanCountries objectAtIndex:indexPath.row];
NSArray *namesOfEuropeanCountries = [europe allKeys];
NSString *EuropeanCountryName = [namesOfEuropeanCountries objectAtIndex:indexPath.row];
//how to feel cells only with the proper continent's countries?
cell.textLabel.text = ???;
return cell;
}
Will appreciate any help.
Are you using a UINavigationController? If so:
Setup a 'push' segue from your Master VC to your Detail VC and have a property in your details VC to hold the 'continent' dictionary.
implement 'prepareForSegue', here you can use the indexPathForCell: on the sender argument to find out which cell was tapped.
Use the row in the index path to locate the country in your dictionary
In your prepare for segue, use segue.destinationViewController to get a handle on your detail VC and set it's continent property to the country dictionary from your data source. (you only want to pass forward the continent dictionary containing the countries in that continent, not your whole data source).
Then in the detail VC' cellForRowAtIndexPath:
then if you only want to display the country name in the cell assuming the names are keys...
NSString *key = [self.continentDict allKeys][indexPath.row];
cell.textLabel.text = key;

How to segue from a UISearchBarDisplayController result to a detailViewController

So, using storyboard you can create a segue from the UITableViewCell from the first tableViewController to a detailViewController.
Not too complicated, however, when a UISearchBarDisplayController is introduced into the storyboard mix, how can you segue the results cell to the detailViewController?
I am able to search without a problem, I followed this tutorial: http://clingingtoideas.blogspot.com/2010/02/uitableview-how-to-part-2-search.html
All I can do is select a row from the search, it turns blue and doesn't go to the detailViewController.
I have implemented the method prepareForSegue, which works for the non searched cells, but can't figure out this one.
Here's the solution that's based on the comment by #James Chen. Also using a different IndexPath depending on which state the table is in.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"toDetail"]) {
Person *person = nil;
if (self.searchDisplayController.active == YES) {
NSIndexPath *indexPath = indexPath = [self.searchDisplayController.searchResultsTableView indexPathForSelectedRow];
NSLog(#"segue - section: %d, row: %d", indexPath.section, indexPath.row);
person = [self.filteredPersonArray objectAtIndex:indexPath.row];
}
else {
NSIndexPath *indexPath = indexPath = [self.tableView indexPathForSelectedRow];
NSLog(#"segue - section: %d, row: %d", indexPath.section, indexPath.row);
person = [self.personArray objectAtIndex:indexPath.row];
}
[[segue destinationViewController] setPerson:person];
}
}
I tried your solution and found that prepareForSegue is called twice
due to the life cycle and didSelect... -> performSegueWithIdentifier.
self:prepareForSegue: object on destination controller is set
(with wrong index) because
dest:viewDidLoad: the destination controller view is loaded after which
self:didSelectRow...: the index is known and properly set.
self:prepareForSegue: object is now correct but has no side effect.
I then focused on didSelect... and came up with this solution where I deleted the segue and pushed the view programmatically:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
DestTableViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"DestViewController"];
CustomObj *custObj = nil;
if (tableView == self.searchDisplayController.searchResultsTableView) {
custObj = [filteredListContent objectAtIndex:indexPath.row];
} else {
storeToDetail = [self.fetchedResultsController objectAtIndexPath:indexPath];
}
controller.custObj = custObj;
[self.navigationController setNavigationBarHidden:NO];
[self.navigationController pushViewController:controller animated:YES];
// presentViewController::animated:completion is always full screen (see problem below)
}
I then experienced some problems going back because I follow a segue
from a mapView, which lead to:
//DestinationViewController
- (IBAction)back:(id)sender
{
[self.navigationController popToRootViewControllerAnimated:YES]; // list
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil]; // map
}
which is not the way to do it but for now it works.
However, the first part is easy and clean, and maybe it works for you too?!
Ok I think I got it, it seems like a bit of a hack but it works for my purposes:
I am using storyboard:
I have a UITableview controller with UISearchBarDisplayController directly on top of it. No code just drag and drop.
From there, I followed this tutorial to get the search bar to search correctly http://clingingtoideas.blogspot.com/2010/02/uitableview-how-to-part-2-search.html
However prepareForSegue: would only let me display a cell from the original array, not with the search array.
So I used didSelectRowAtIndexPath:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (savedSearchTerm) {
searchRowSelected = indexPath.row; //<-- the trick that worked
[self performSegueWithIdentifier:#"ShowDetail" sender:self];
}
}
searchRowSelected is an int variable that I declared at the top of the class.
didSelectRowAtIndexPath: knew which row I was selecting, but prepareForSegue didn't. Thats why I needed that variable.
This is how I used it in prepareForSegue:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"ShowDetail"]) {
dvc = [segue destinationViewController];
NSIndexPath* path = [self.tableView indexPathForSelectedRow];
int row = [path row];
if (savedSearchTerm){ //set the detailViewController with the searched data cell
myDataClass* c = [searchResults objectAtIndex:searchRowSelected];
dvc.myDataClass = c;
}else{ //set the detailViewController with the original data cell
myDataClass* c = [array objectAtIndex:row];
dvc.myDataClass = c;
}
}
}
Also use this code to clean up savedSearchTerm
-(void) searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller{
[self setSavedSearchTerm:nil];
}
If anyone has a better solution I'm all ears :)

Resources