I have the following code which works out which section/data index to pass to a destination controller based on the clicked cell.
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
self.selectedItIndex = indexPath.row; // define NSInteger selectedItemIndex property in your interface
self.selectedSection = indexPath.section;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"sightSeg"]) {
NSString *s = self.sectionData[_selectedSection];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"siteSection like %#", s];
NSArray *filteredArr = [self.sightsArray filteredArrayUsingPredicate:pred];
NSLog(#"Value of hello = %#", filteredArr);
sightsObject *rightS = filteredArr[_selectedItIndex];
_finNo = _selectedItIndex +=1;
NSLog(#"balls = %#",rightS );
SightViewController *destController = (SightViewController *)segue.destinationViewController;
destController.viewTit = rightS.siteTitle;
}
}
The code works fine first try (as in the correct data is passed relevant to the cell/section clicked) - the issue I have is that if you return to the parent controller and select another cell - the same information is passed again - if you repeat the process selecting a 3rd cell - the data for the second cell clicked is passed - so its as though its a step out of sync!?
Can anyone offer any advice? - Is there a way to only run the sgue code once the values have been set in the didselectpath method?
The sender parameter in prepareForSegue will be the collection view cell you've tapped. Use the following code to derive your index at the point of sending:
NSIndexPath *selectedIndexPath = [self.collectionView indexPathForCell:sender];
You don't need the code in didSelectItem...
Try calling [collectionView indexPathsForSelectedItems] inside prepareForSegue: to get the index path instead of saving it off in collectionView:didSelectItemAtIndexPath:
Related
My Current code for performing the segue to my next view controller is as follows:
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return [arrayOfDescriptions count];
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
CustomCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
[[cell IconImage]setImage:[UIImage imageNamed:[arrayOfImages objectAtIndex:indexPath.item]]];
[[cell IconLabel]setText:[arrayOfDescriptions objectAtIndex:indexPath.item]];
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:#"GroupsHomeSegue" sender:self];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"GroupsHomeSegue"])
{
//IconImage is the name of the image in GroupsViewController i want to pass through. Based upon what each cell is set at anyways.
//logoImage is the name of the image i want to set in GroupsHomeViewController.
//so basically i want to be able to get IconImage and set it as logoImage in a different view controller
}
}
The problem that i am having, is how do i obtain the value of my text from each selected cell individually so that i can post it as another label in a detailed view controller.
The comment lines within my prepare for segue describe exactly what i am trying to achieve. I simply want to obtain the value of an individual UICollectionViewCell Label.text
This may seem similar to previous passing data through view controller posts, but this is different to anything that i have found due to the fact that in these posts the text values are constant i.e the value of the label.text is set to one thing and are not coming from an array.
I would simply just like to know how to find the label value of an individually selected cell and pass it through to my detailed view controller.
To pass information to the next view controller during a segue, you would use the destinationViewController property on the segue parameter passed to prepareForSegue:sender:. You will have to setup properties on that destination view controller to be able to set the values, of course.
To determine what information the user selected, you have a few options. You can create a property on your view controller to store what the user selected and put that value in the property during collectionView:didSelectItemAtIndexPath: based on the indexPath paramter, or you can use the UICollectionView method indexPathsForSelectedItems to get the index path of the selected items during prepareForSegue:sender:. I tend to do the latter.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"GroupsHomeSegue"])
{
NSIndexPath* indexPath = [[someCollectionView indexPathsForSelectedItems] first];
if(indexPath != nil)
{
NSString* selectedDescription = arrayOfDescriptions[indexPath.item];
NSString* selectedImageName = arrayOfImages[indexPath.item];
// Get the destination view controller (the one that will be shown) from the segue and cast it to the appropriate type. Assuming this should be GroupsHomeViewController, but I'm not entirely sure that's correct since I can't see all your code
GroupsHomeViewController* groupsHomeViewController = segue.destinationViewController;
// Set the appropriate properties (Again, I'm guessing here since I can't see your code)
groupsHomeViewController.logoImage = [UIImage imageNamed: selectedImageName];
}
}
}
In "didSelectRowAtIndexPath" pass the indexPath as "sender" (Please see the below code). And in "performSegueWithIdentifier" you will get the indexPath of selected cell.
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
[self performSegueWithIdentifier:#"GroupsHomeSegue" sender:indexPath];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"GroupsHomeSegue"])
{
NSIndexPath *indexPath = (NSIndexPath*)sender;
NSString* iconImage = arrayOfImages[indexPath.row];
}
}
Try using this code-
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:#"GroupsHomeSegue" sender:indexPath];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"GroupsHomeSegue"])
{
UICollectionViewCell *cell = [self.collectionView cellForItemAtindexPath:sender];
NSString *text = cell.IconLabel.text;
UIImage *image = cell.IconImage.image;
GroupsHomeViewController* ghVC = segue.destinationViewController;
groupsHomeViewController.logoImage = image;
}
}
I am trying to send an NSMutableArray of the chosen cell to another view when a cell is clicked. I have the array of the chosen cell, but I can't get it to pass over.
"SearchResultsModalTableViewController.m"
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSMutableArray *aVenue = [self.venues objectAtIndex:indexPath.row];
DetailSearchResultViewController *detailVC =[[DetailSearchResultViewController alloc]init];
detailVC.venueInfo = aVenue;
NSLog(#"%#",aVenue);
}
When you use storyboards, you usually add segues from table cells to a destination storyboard. If that is what you are doing, then the way to deal with the selection is in the prepareForSegue: method of your view controller.
Here is an example. I am assuming that DetailSearchResultViewController in your code is a UINavigationController:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
DetailSearchResultViewController* destinationViewController = (DetailSearchResultViewController*)segue.destinationViewController;
NSIndexPath *p = self.tableView.indexPathForSelectedRow;
NSUInteger idx = p.row;
NSMutableArray *aVenue = [self.venues objectAtIndex:indexPath.row];DetailSearchResultViewController *detailVC =[[DetailSearchResultViewController alloc]init];
destinationViewController.venueInfo = aVenue;
[super prepareForSegue:segue sender:sender];
}
So this issue is similar to the one described here UITableView - IndexPath always returns 0 first & takes 2 clicks to pass correct data to view controller however the answer they suggested does not help to fix the problem so I'm asking specifically with my code. This is my prepareForSegue method:
//send to a detail version of each of the chatters allowing a report a user
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([[segue identifier] isEqualToString:#"pushToDetail"]){
ChatterDetailViewController *chatterDetail = [segue destinationViewController];
UITableViewCell *cell = (UITableViewCell*)sender;
long indexPathSelected = [self.tableView indexPathForCell:cell].row;
Chatter *detailChatter = [_arrayWithoutME objectAtIndex:indexPathSelected];
NSData *fbImageData = [NSData dataWithContentsOfURL:[NSURL URLWithString: detailChatter.url]];
_detailProfile = [UIImage imageWithData:fbImageData];
chatterDetail.detailProfile = _detailProfile;
}
}
Which obviously looks different from the answer suggested on the other question, however I tried that and it did not change anything. Whenever I click on a tableView cell it completes the segue and I see the the profile picture from the first cell (index 0) and not the cell where it was clicked. I tried using
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
however it didn't pass the image value like I needed and also when I tried to return using an unwind segue it said the navigation tree was corrupted (doesn't happen with the current code). Basically I can't seem to find why selecting any cell returns only the first cell's information.
EDIT:
here is some code from Chatter Detail's view did load method:
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.hidesBackButton = YES;
_detailProfilePicture.contentMode = UIViewContentModeScaleAspectFill;
_detailProfilePicture.image = _detailProfile;
[_detailProfilePicture sizeToFit];
}
Again, it does return a picture, however it is only the picture from cell at index 0.
There is an easier way to extract the indexPath for your selected row in a UITableView:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"pushToDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
// section and row return an int like this:
NSLog(#"index path: section %i, row %i", indexPath.section, indexPath.row);
}
}
However, your code does give the correct row value as long, and a long can indeed be used to get an object from an NSArray. I would put a breakpoint or log message at the end of this method and see if the correct value is actually in your array.
I would also check what value you get in your indexPathSelected - this should reflect the selected row.
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.
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 :)