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.
Related
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];
}
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. :)
This will probably take two seconds to answer, but my search skills have not gotten me very far on this issue. I am performing a segue but I'm not sure how to grab the id on the destination. Here is my code on the tableview controller.
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath: NSIndexPath *)indexPath{
NSLog(#"reaching accessoryButtonTappedForRowWithIndexPath:");
[self performSegueWithIdentifier:#"leads_calls_to_detail" sender:[[self.leads_calls objectAtIndex:indexPath.row] objectForKey:#"ID"]];
}
What do I have to create on my destination view controller to be able to grab the id that I'm attempting to pass, or is the way I'm performing my segue incompatible with what I am attempting?
You should just pass values to the destinationViewController inside prepareForSegue: and pass self as the sender.. try using something like:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([[segue identifier] isEqualToString:#"leads_calls_to_detail"])
{
YourViewController *controller=(YourViewController *)segue.destinationViewController;
NSIndexPath *path = [self.tableView indexPathForSelectedRow];
//Or rather just save the indexPath in a property in your currentViewController when you get the accessoryButtonTappedForRowAtIndexPath callback, and use it here
controller.yourPropertyToSet = [self.leads_calls objectAtIndex:path.row];
}
}
And also according to Rob Mayoff, you can grab the index path for the row that the accessory was tapped at by using something like this:
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];//where sender is the sender passed in from prepareForSegue:
How to find indexPath for tapped accessory button in tableView
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;
}
}
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 :)