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;
Related
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
This question already has answers here:
Passing data between view controllers
(45 answers)
Closed 7 years ago.
Display details in second UITableViewController
My application is a simple Recipebook to understand UITableViewControllers. The application contains two UITableViewControllers. The first UITableViewController contains a UITableView with a list of the recipe names. If you select a cell you will segue to the second UITableViewController. The second UITableViewController contains a UITableView with a list of the ingredients.
The application contains the following classes:
RecipeTableViewController (first)
IngredientTableViewController (second)
RecipeObject
RecipeData
The RecipeObject Class contains two properties. One property of type NSString with the recipe name. The other property is of type NSArray with the ingredients. The RecipeObject objects are in the RecipeData class.
RecipeObject *recipe1 = [[RecipeObject alloc]init];
recipe1.name = #"Fresh Coconut Cake";
recipe1.ingredients = [NSArray arrayWithObjects:#"Coconut cups", #"Milk", #"Baking powder", #"Butter", #"Sugar", #"Eggs", nil];
The RecipeData is called in the RecipeTableViewController to display the recipe names in the tableView.
Message from RecipeData class to RecipeTableViewController:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.recipes = [RecipeData allRecipes];}
Display names in the tableView:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *cellIdentifier = #"recipeCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
RecipeObject *recipes = [self.recipes objectAtIndex:indexPath.row];
cell.textLabel.text = recipes.name;
return cell;
}
How to add the recipe1.ingredients array to the IngredientsTableViewController?
Any help is highly appreciated!
Perform a segue when a recipe is selected. In the prepare for segue-method take the selected recipe and pass the ingredients to the ingredientsTableViewController.
Something quite similar to this:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
[self performSegueWithIdentifier:#"to_Ingredients" sender:self];
}
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"to_Ingredients"]) {
IngredientTableViewController *ingredientsTableViewController = (IngredientTableViewController *)[segue destinationController];
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
RecipeObject *recipe = [self.recipes objectAtIndex:indexPath.row];
ingredientsTableViewController.ingredients = recipe.ingredients;
}
}
If you don't use a storyboard where you set this segue you just need the first method which the should looks like this:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
IngredientTableViewController *ingredientsTableViewController = [[IngredientTableViewController alloc]init];
RecipeObject *recipe = [self.recipes objectAtIndex:indexPath.row];
ingredientsTableViewController.ingredients = recipe.ingredients;
[self showDetailViewController:ingredientsTableViewController sender:self]
}
I'm following a tutorial from a book and everything went fine untill i had to recieve the data from the tableView, i must mention that i modified some of the basic tutorial to fit my needs as i work with JSON values comming from a API. It crashes when i reference the value using self and says that the variables is nil. must also mention that the label has been added as a reference outlet and the customClass also as been created and added referenced with the View. Aditional to this added properly Segue to the detailViewController
UserDetailViewController.h
#interface UserDetailViewController : UIViewController
#property (nonatomic, strong) NSString *labelName;
#property (nonatomic, strong) IBOutlet UILabel *detailLabelName;
#end
UserDetailViewController.m
This is the code that does the transition between Table and detailView but there are a few values that does not belong to the code i have such as recipeName and recipeNames as you can see below:
recipeName is on my code labelName
recipeNames is on my code a dictionary [dictionary objectForKey:#"name"]
and have no idea how to put this part together. :(
// Here is where I handles the Data Transfer
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"userDetailView"])
{
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
RecipeDetailViewController *destViewController = segue.destinationViewController;
destViewController.recipeName = [recipeNames objectAtIndex:indexPath.row];
}
}
According to my understanding, you want to set text for detailLbelName depending on labelName. And this value is coming from your parent view controller.
You must be doing something like this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UserDetailViewController *detailController = [[UserDetailViewController alloc]initWithNibName:#"UserDetailViewController" bundle:nil];
self.detailController.lableName = #"Your Text According to Cell"
[self.navigationController pushViewController:detailController animated:YES];
}
In your UserDetailViewController:
- (void)viewWillAppear
{
self.title = self.labelName;
[self.detailLabelName setText:self.labelName];
}
To receive the data from tableView need to handle tableView delegate did select method and passed all required data to detailLabelName then refresh the screen.
Move the assignment lines in viewDidLoad to viewWillAppear.
- (void)viewWillAppear
{
self.title = self.labelName;
_detailLabelName.text = _labelName; // this is the breakpoint
}
Then, hwever you are storing the string in the table view (i assume you mean the cells specifically) then you may assign the value extracted from the table view cell after creating the detail view controller but before it is pushed onto the navigation stack and crashes your app.
please try this
In
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
of your table view do this.
UserDetailViewController *detailsView = [[UserDetailViewController alloc] initWithNibName:"Your nib name" bundle:[NSBundle mainBundle]];
UITableViewCell *reqDcell = [tableView cellForRowAtIndexPath:indexPath];
detailsView.labelName = //Text from reqDcell goes here. In particular the recipe name
[self.navigationController pushViewController:tableViewControls animated:YES];
Hope this helps.
********** EDIT ************
Ok so inorder to pass values using a dictionary , you would need an array of dictionaries which hold your data.
In terms of the link you posted, i have modified it for your understanding to use dictionaries.
recipeNames = #[[NSDictionary dictionaryWithObject:#"Mushroom" forKey:#"Recipie Name"],
[NSDictionary dictionaryWithObject:#"Hamburger" forKey:#"Recipie Name"],
[NSDictionary dictionaryWithObject:#"Pasta" forKey:#"Recipie Name"],
[NSDictionary dictionaryWithObject:#"Pizza" forKey:#"Recipie Name"],
[NSDictionary dictionaryWithObject:#"Noodles" forKey:#"Recipie Name"]];
The dictionary here will hold the json data that you have.
in - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
// Display recipe in the table cell
NSDictionary *dict = [recipeNames objectAtIndex:indexPath.row];
cell.nameLabel.text = [dict valueForKey:#"Recipie Name"];
and the prepareForSegue function.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showRecipeDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
RecipeDetailViewController *destViewController = segue.destinationViewController;
NSDictionary *dict = [recipeNames objectAtIndex:indexPath.row];
destViewController.recipeName = [dict valueForKey:#"Recipie Name"];
}
}
I hope this is helpful
my code snippet //
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// [tableView deselectRowAtIndexPath:indexPath animated:YES];
if(indexPath.section == 0)
{
if(indexPath.row == 1)
{
WebServiceHandler *handler = [[WebServiceHandler alloc]init];
handler.requestType = eSuburb;
NSMutableURLRequest *searchDetailsRequest = [Service parseGetSuburb:nil];
NSLog(#"user details request of str== %#",searchDetailsRequest);
[handler placeWebserviceRequestWithString:searchDetailsRequest Target:self Selector:#selector(getListOfSuburb:)];
}
after response it jumps into another class with list of all it is data,among all those data I want to populate with it one which user selects
Not sure how you are doing this, but here is how if you are using segues. In this example, each of my table cells contained their relative information in an NSMutableDictionary. The receiving view controller had a NSMutableDictionary property to receive all of the sent data. The last two lines create a ViewController object and has its Dictionary property equal the Dictionary being sent from the selected cell in the table view.
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
NSString *identifier = segue.identifier;
if ([identifier isEqualToString:#"SegueName"]) {
// first get data
NSIndexPath *selectedIndexPath = self.tableView.indexPathForSelectedRow;
NSMutableDictionary *cellData = self.aDictionary[selectedIndexPath.row];
ViewController *nextViewController = segue.destinationViewController;
nextViewController.aDictionary = cellData;
}
}
I would Suggest against using the callback method here. That is not the right way to carry this out.
According to my understanding, what you want to do is once the user selects a row, you wish to pass the selected row's data back to the viewcontroller(lets assume 'vcA') which called the one with the tableview('vcB')...
If this is so you should create a protocol and hence use a delegate to notify the viewcontroller 'vcA' about the selection event and hence pass the required data just as #inertiatic suggested.
I'm using Core Data to manage Thing(s) and then UITableViewController/UITableViewCell to present a list of Things. However, a Thing object has more data than just what I display in the text property of the UITableViewCell, and when I segue from the UITableViewController to a more detailed view controller of Thing, what I really want is a pointer to the Thing object so I can pass it on to the next view controller.
For example:
-(UITableViewCell*)tableView:(UITAbleView*)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
ENTRY_LOG;
// Ask the NSFetchedResultsController for the NSManagedUserObject at the row in question
Thing* thing = [self.fetchedResultsController objectAtIndexPath:indexPath];
// For brevity skip dequeueReusable...
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.textLabel.text = thing.name;
EXIT_LOG;
return cell;
Now, assume I assume the segue connected appropriately:
- (void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender {
ENTRY_LOG;
UITableViewCell* cellSender = (UITableViewCell*)sender;
// I want to do this:
Thing* thingHandle = [cellSender someMessageToGetThingReference];
[segue.destinationViewController setThing:thing];
// and I don't want to do this
// setup NSFetchRequest and predicate to get thing where 'name = cellSender.text'
[segue.destinationViewController setThing:thing];
EXIT_LOG;
}
If I could get the indexPath that the cell is at I could then use my NSFetchResultsController to at least get the objectAtIndexPath again, perhaps call - (NSIndexPath *)indexPathForSelectedRow in the prepareForSegue against the UITableView and proceed from there.
Perhaps I am missing something obvious, but to get the cell's index path, assuming you are segueing from a UITableViewController, can't you use:
NSIndexPath *indexPath = [[self tableView] indexPathForCell:cellSender];