I've got a tableview setup to display data from an sqlite database, now what I am trying to do is setup a view to display further information from said database when the user clicks on a table cell.
From what I've read so far my understanding that this is done mainly in the prepareForSegue method inside the table view's controller?
Anyway, after a lot of googling I've ended up with this from an AppCoda tutorial (link)...
if([segue.identifier isEqualToString:#"pushToMountainInfo"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
UINavigationController *nav = segue.destinationViewController;
MountainInformation *destViewController = [nav.viewControllers objectAtIndex:0];
MountainsObject *mountain = [self.mountains objectAtIndex:indexPath.row];
destViewController.nameLabel = mountain.name;
NSLog(#"%#", mountain.name);
}
I put the NSLog there to test, and it does output the mountain's name to the log but the label remains blank when I run the app.
Can anybody please help?
Thanks..!
Okay to shorten your code a bit..
if([segue.identifier isEqualToString:#"pushToMountainInfo"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
MountainInformation *destViewController = [segue destinationViewController];// do this only if your segue is connected to the next one from previous, or else let it stay as you have, only change the later part
MountainsObject *mountain = [self.mountains objectAtIndex:indexPath.row];
destViewController.myMountain = mountain.name;
NSLog(#"%#", mountain.name);
}
Now create a property in MountainInformation.h file
#property (nonatomic, strong) NSString* myMountain;
Now your property is set to the name you want to set.
Now in viewDidLoad in MountainInformation.m
-(viewDidLoad){
self.labelToShowName.text = self.myMountain;
}
Hope this helps
[EDIT]
In MountainInformation.h Import your mountain class.
#import "MountainObject.h"
Change the property to
#property(nonatomic, strong) MountainObject *selectedMountain
Now in previous view controller.
if([segue.identifier isEqualToString:#"pushToMountainInfo"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
UINavigationController *nav = segue.destinationViewController;
MountainObject *mountain = [self.mountain objectAtIndex.indexPath.row];
destViewController.selectedMountain = mountain;
}
In MountainInformation.h
-viewDidLoad{
NSString *mountainName = self.selectedMountain.name;
NSString *mountainRegion =self.selectedMountain.region;
NSString *mountainHeight =self.selectedMountain.height;
//and other properties. and then you can set it to a single label by using
self.myLabel.text = [NSString stringWithFormat:#" Name - %#, Height- %#, Region- %#",mountainName, mountainHeight, mountainRegion];
}
Related
I am trying to pass in IndexPath through a segue though a navigationController, but it crashes on line:
view.selectedAccount = self.selectedAccountRow;
thanks
-(IBAction)changeButtonPressed:(id)sender {
CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.accountTableView];
NSIndexPath *indexPath = [self.accountTableView indexPathForRowAtPoint:buttonPosition]
self.indexPath = indexPath;
NSLog(#"%ld",(long)indexPath.row);
}
and
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
[super prepareForSegue:segue sender:sender];
ViewController2 *view = [segue destinationViewController];
view.selectedAccount = self.indexPath;
}
answer here: Set NSString Object, prepareForSegue through UINavigationController
I see many errors:
self.selectedAccountRow never gets defined, change to this:
-(IBAction)changeButtonPressed:(id)sender {
CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.accountTableView];
NSIndexPath *indexPath = [self.accountTableView indexPathForRowAtPoint:buttonPosition]
self.selectedAccountRow = indexPath;
}
When using [segue destinationViewController] you need to ensure that the class your assingning is the right one:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
ViewController2 *view = (ViewController2*)[segue destinationViewController];
view.selectedAccount = self.selectedAccountRow;
}
There's no need to declare the as strong, as it is the default one, but it's recommender as it's easier to read, and it's a good coding practice.
#property (strong, nonatomic) NSIndexPath *selectedAccount;
You need to name better your classes and properties, if you declare a viewController subclass, the name should be self explanatory, maybe you can name it detailViewController instead on viewController2.
Also, you should consider using a 2-3 letters prefix for your classes, please read good obj-c practices in here:
Objective-C Good practices
Right click on your view in IB and remove and instances where there is a yellow warning sign. Run and everything should work.
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
In my project I have custom UITableview cells, which happen to have a button in it. When this button is selected, it triggers a segue. In this segue I pass an object from the cell to the destination UIViewcontroller. I'm currently using the following code
else if ([[segue identifier] isEqualToString:#"Add"])
{
SearchAddRecordViewController *addRecordViewController = [segue destinationViewController];
NSIndexPath *path = [self.tableView indexPathForSelectedRow];
SearchCell *cell = [self.tableView cellForRowAtIndexPath:path];
NSLog(#"%#", cell.record);
addRecordViewController.workingRecord = cell.record;
}
It turns out I'm passing null, because the button isn't triggering the selection of the cell, therefore no indexPathForSelectedRow. My question is, how do I get the indexpath for the cell which button has been pressed.
Edit:
Answered
You declare an NSIndexpath variable in your custom cell's .h file and assign the indexPath to that variable in cellForRowAtindexPath. And Retrieve it and use it....Hope it will work
For anyone experiencing a similar problem, this is how I was able to solve this.
I first created the implementation and header files for a UIButton subclass with the NSIndexPath property. I assigned the uibutton in the Storyboard the superclass of this new custom unbutton with the NSIndexPath property. I added the following code to the segue identification step (the key was realizing i could use the sender object as the source of triggering the segue):
else if ([[segue identifier] isEqualToString:#"Add"])
{
SearchAddRecordViewController *addRecordViewController = [segue destinationViewController];
addRecordButton *button = (addRecordButton*) sender;
NSIndexPath *path = button.indexPath;
SearchCell *cell = [self.tableView cellForRowAtIndexPath:path];
addRecordViewController.workingRecord = cell.record;
}
You can do this in following manner, this helps you to uniquely identify which button is tapped and you can pass the appropriate values, I am just passing a string value for example
suppose OutputString is a string property in your destination view controller
if ([[segue identifier] isEqualToString:#"yourSegueIdentifier"]) {
UITableViewCell *clickedCell = (UITableViewCell *)[[sender superview] superview];
NSIndexPath *clickedButtonPath = [self.myTableView indexPathForCell:clickedCell];
[segue.destinationViewController setOutputString:#"XYZ"];
}
An alternative way to determine the indexPath that avoids subclassing UIButton would be:
CGPoint point = [button.superview convertPoint:button.center toView:self.tableView];
NSIndexPath *path = [self.tableView indexPathForRowAtPoint:point];
EDIT:
I've factored this into a nice method you can add to a Category on UITableView:
-(NSIndexPath*)indexPathOfCellComponent:(UIView*)component {
if([component isDescendantOfView:self] && component != self) {
CGPoint point = [component.superview convertPoint:component.center toView:self];
return [self indexPathForRowAtPoint:point];
}
else {
return nil;
}
}
Hey there I have been working on a transit app for some time and have been stuck with this issue for a while now.
I am using iOS 5 and a storyboard. Basically I have a UITableView that displays favorite bus stop locations, when a user selects a row I use:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
Favorite *favorite = [self.favoriteItems objectAtIndex:indexPath.row];
stopString = favorite.title;
routeString = favorite.subtitle;
}
With the stop and route information of the cell the user chose I then prepare for a segue that corresponds to a segue on my storyboard, pushing a detail view controller that uses the stop name and route name to display times from a plist.
I am fairly new to Objective C and iOS so I am using a segue that my friend told me would work, however, it might be the problem. The segue looks like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
UIViewController *destination = segue.destinationViewController;
if ([destination respondsToSelector:#selector(setDelegate:)])
{
[destination setValue:self forKey:#"delegate"];
}
if ([destination respondsToSelector:#selector(setSelection:)])
{
NSString *route = routeString;
NSDictionary *selection1 = [NSDictionary dictionaryWithObjectsAndKeys:route, #"route", stopString, #"stop", nil];
[destination setValue:selection1 forKey:#"selection"];
}
}
After the segue in my DetailViewController I grab the stop and route information in the view DidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
route = [selection objectForKey:#"route"];
stopName = [selection objectForKey:#"stop"];
NSLog(#"stopName: %#", stopName);
NSLog(#"routeName: %#", route);
}
Here is where my problems arise. When I run the simulator and click on an a cell in my table view, I am pushed to the DVC, however, the stopName and routeName are both null, so no information was sent or received. BUT, if I go back to the table and click another cell, the routeName and stopName are filled with the information that should have sent the first time I clicked a cell. If I continue this process it continues to send the information for the cell tapped previously, not currently.
So basically information is sending but only after I go through the segue twice. Obviously I want it to send the information and receive it the first time, but it is delayed and driving me nuts. I appreciate any help someone can give me as I have been searching the internet for days now trying to fix this issue, thank you so much in advance for any assistance!
prepareForSegue: is being called before didSelectRowAtIndexPath:. This is why the values you see always are lagging behind.
The better solution is to get the stopString and routeString values in your prepareForSegue: method (and not use didSelectRowForIndexPath: at all). The key to doing this is to realize that the sender parameter value being passed to prepareForSegue: is the UITableViewCell that was tapped. You can use the UITableView method indexPathForCell to get the cell's indexPath in your table, and then use that to look up the data in your favoriteItems array.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
UITableViewCell *cell = (UITableViewCell*)sender;
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
Favorite *favorite = [self.favoriteItems objectAtIndex:indexPath.row];
stopString = favorite.title;
routeString = favorite.subtitle;
UIViewController *destination = segue.destinationViewController;
if ([destination respondsToSelector:#selector(setDelegate:)])
{
[destination setValue:self forKey:#"delegate"];
}
if ([destination respondsToSelector:#selector(setSelection:)])
{
NSString *route = routeString;
NSDictionary *selection1 = [NSDictionary dictionaryWithObjectsAndKeys:route, #"route", stopString, #"stop", nil];
[destination setValue:selection1 forKey:#"selection"];
}
}
Make sure that you are NOT connecting the segue to the next view controller to the tableView CELL directly. Connect to the whole UITableViewController / UIViewController (whichever you are using) and give a name, say "segueNameInStoryboard".
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
Favorite *favorite = [self.favoriteItems objectAtIndex:indexPath.row];
stopString = favorite.title;
routeString = favorite.subtitle;
/* add this line */
[self performSegueWithIdentifier:#"segueNameInStoryboard" sender:self];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"segueNameInStoryboard"])
{
UIViewController *nextViewController = segue.destinationViewController;
nextViewController.delegate = self;
NSDictionary *selection1 = [NSDictionary dictionaryWithObjectsAndKeys:routeString, #"route", stopString, #"stop", nil];
nextViewController.selection = selection1;
}
}
I am very new to developing in iOS. I am trying to pass some data through a segue in a table view. Here is what I am calling to pass the data:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([segue.identifier isEqualToString:#"ShowContactDetails"]) {
ContactDetailsViewController *dvc = [segue destinationViewController];
NSIndexPath *path = [self.tableView indexPathForSelectedRow];
Contact *c = [jsonResults objectAtIndex:path.row];
[dvc setSelectedContact:c];
}
}
It passes fine and when I do a NSLog on selectedContact, this is what I get:
2012-05-21 15:15:42.410 Test[8352:f803] {
category = Prospect;
id = 19895;
label = "John Doe";
}
The problem is, I don't know how to access those items individually. I have tried to call things like objectForKey but I get an error.
From the output of your NSLog, selectedContact seems to be an NSDictionary object. Try using [selectedContact valueForKey] instead of [selectedContact objectForKey].
For example:
NSLog(#"Name: %#", [selectedContact valueForKey:#"label"]);