I have a UITableView which when you select a row, it will lead you into the "details" of the selected row. However, the first time I click the row, it goes into the prepareForSegue method, without calling didSelectRowAtIndexPath. When I push the back button on the detail view, the app goes back, and if I select the row again, the method will be called.
I'm NOT using the didDeselectRowAtIndexPath.
This patter will continue and I can't figure out why. When I load my data, I do call [tableview reloadData].
I have also tried to add self.TagsTableView.allowsMultipleSelection = YES; to my viewDidLoad method.
Here is my code:
- (void)viewDidLoad {
[super viewDidLoad];
self.bluetoothManager = [[BlueToothLEManager alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(reloadTableView:) name:#"ScanComplete" object:nil];
self.Tags = self.bluetoothManager.Tags;
NSLog(#"Fetch Tags: %#", self.Tags);
}
...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell* cell = [fetchTagsTableView dequeueReusableCellWithIdentifier:#"tagCell"];
if(cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"tagCell"];
}
FetchTag* newTag = [fetchTags objectAtIndex:indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:#"%# %d", newTag.tagName, indexPath.row];
cell.detailTextLabel.text = [newTag.tagUUID UUIDString];
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
selectedTagFromTable = [fetchTags objectAtIndex:indexPath.row];
[TagsTableView deselectRowAtIndexPath:indexPath animated:YES];
}
...
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
if([[segue identifier] isEqualToString:#"DetailView"]){
TagDetailViewController* ftdvc = [segue destinationViewController];
[ftdvc setSelectedTag:selectedTagFromTable];
}
// Pass the selected object to the new view controller.
}
-(void)reloadTableView:(NSNotification*)notif{
NSLog(#"Reloading Table View");
NSLog(#"Fetch Tags: %#", self.Tags);
[self.TagsTableView reloadData];
}
I would have put the code from the didSelectRowAtIndexPath: to the prepareForSegue: instead Try using the sender parameter to identify a selected tag
Related
I need to delete a row in my tableview to update my changes.
I have a delete button in each cell (tableViewCellController) - look at the picture.
Screenshot
After I click the delete button, the UI button method calls the delegated method in tableViewController. The delete method update the data source (my model) and i want to update also the screen (now the method reload all the data, but I want to update the new change - delete row from screen).
I tried to do this with the following function but i don't have a sender (as i said the button is pressed in the cell, but I actually make the change on the tableView)
Function:
- (IBAction)contactDelete:(id)sender{
[[[ModelUser instance] getUser:self.actualLoggedUser] removeFavUser:self.contactUserId];
NSIndexPath *indexPath = [self.tableView indexPathForCell:(UITableViewCell *)sender.superview];
[self.tableView deleteRowsAtIndexPaths:
[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
}
favoritesTableViewCell:
#import "favoritesTableViewCell.h"
#import "ModelUser.h"
#implementation favoritesTableViewCell
- (void)awakeFromNib {
// Initialization code
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
- (IBAction)favoritesDeleteFromFav:(id)sender {
[[[ModelUser instance] getUser:self.actualLoggedUser] removeFavUser:self.contactUserId];
[self.delegate onFavDeleteClick];
}
favoritesTableViewController:
- (void)viewDidLoad {
[super viewDidLoad];
self.actualLoggedUser = [NSString stringWithFormat:#"2"];
[self reloadData];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)reloadData {
NSLog(#"Favorites tab was loaded");
//get id of my favorite contacts
myFavListId = [[ModelUser instance] getUser:self.actualLoggedUser].contactsFavoriteList;
//get data of my favorites contacts
myFavListContactsData = [[NSMutableArray alloc] init];
for (int i=0; i < [myFavListId count] ; i++) {
User* us = [[ModelUser instance] getUser:([myFavListId objectAtIndex:i])];
[myFavListContactsData addObject:us];
}
[self.tableView reloadData];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (void) viewDidAppear:(BOOL)animated {
[self reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return myFavListContactsData.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
favoritesTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"favoriteCell" forIndexPath:indexPath];
User *us = [myFavListContactsData objectAtIndex:indexPath.row];
//setting cell data
cell.actualLoggedUser = self.actualLoggedUser;
cell.contactUserId = us.userId;
cell.contactName.text = [NSString stringWithFormat:#"%# %#",us.fname,us.lname];
[cell.contactImage setImage: [UIImage imageNamed:us.imageName]];
cell.delegate = self;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
User *us = [myFavListContactsData objectAtIndex:indexPath.row];
UIStoryboard* sb = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
userDetailsProfile* udVC = [sb
instantiateViewControllerWithIdentifier:#"userDetailsProfile"];
udVC.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
udVC.userDetailId = [NSString stringWithFormat:#"%#", us.userId];
[self showViewController:udVC sender:self];
}
- (void)onFavDeleteClick {
[self reloadData];
}
#end
You need to update datasource and delete the cell in your delegate callback that is invoked from your cell class (onFavDeleteClick delegate method of favoritesTableViewCell class, in your case).
The process should be something like this:
In your "favoritesTableViewCell.h", declare a protocol containing onFavDeleteClick method. I think you have already done with this step. What you need to do is to update the method signature as -(void) onFavDeleteClick:(favoritesTableViewCell*)cell.
From "favoritesTableViewCell.m" call favoritesDeleteFromFav method like this:
-(IBAction)favoritesDeleteFromFav:(id)sender {
[self.delegate onFavDeleteClick:self];
}
Now in your view controller where the main UITableView exists implement the callback method like this:
-(void)onFavDeleteClick:(favoritesTableViewCell*)cell {
//update model
[[[ModelUser instance] getUser:self.actualLoggedUser] removeFavUser:self.contactUserId];
//update table view
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
if (indexPath) {
[self.tableView deleteRowsAtIndexPaths:#[indexPath]
withRowAnimation:UITableViewRowAnimationAutomatic];
}
}
And this is everything you need to do to get your desired effect.
i try to use an unwind method to return the value of a row (from a table ) in another view
but the value catch in the first viewController is null.
This is the code in FirstViewController
- (IBAction)retrievePremadeMessage:(UIStoryboardSegue *)segue;(h)
- (IBAction)retrievePremadeMessage:(UIStoryboardSegue *)segue
{
PreStoredMessagesTableViewController *cc = segue.sourceViewController;
self.textViewMsg = [[UITextView alloc] init];
self.textViewMsg.text = cc.valeurCell;
NSLog(#"cell selected: %#", cc.valeurCell);
}
and this is the secondViewController
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Retrieve the value of cell selected
self.valeurCell = [NSString stringWithFormat:#"%#", [tableView cellForRowAtIndexPath:indexPath].textLabel.text];
}
Thank you
In this method:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Retrieve the value of cell selected
self.valeurCell = [NSString stringWithFormat:#"%#", [tableView cellForRowAtIndexPath:indexPath].textLabel.text];
}
Add:
[self performSegueWithIdentifier:#"identifier" sender:[NSString stringWithFormat:#"%#", [tableView cellForRowAtIndexPath:indexPath].textLabel.text]];
and then you can do something like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSString *stringPassedIn = id;
}
Currently I just have a segue hooked up to a UITableView that pops to the next UITableView in the navigation stack.
What I need to do though now instead, is set up an if statement based on the text in the UITableView cell, to decide which of two possible UIViewControllers to pop to.
So ViewController2 needs to be able to pop either":
- back to ViewController1
OR
- forward to ViewController3
based on the text in the UITableView cell that is populated by JSON.
Can you help me out with that? Will post any code needed. Thanks!
Current ViewController:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"standardCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
Spring *spring = [springs objectAtIndex:indexPath.section];
Leaf *leaf = [spring.leafs objectAtIndex:indexPath.row];
cell.textLabel.text = leaf.shortName;
return cell;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
if ([segue.identifier isEqualToString:#"themes"]) {
UITableViewCell *cell = (UITableViewCell*)sender;
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
// Get reference to destination view controller
MRTViewController *tvc = (MRTViewController *)[segue destinationViewController];
}
}
UPDATE
Added code per #troops but still not sure how to pop to VC3
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
NSString *stringToCheck = #"AAA";
if([cell.textLabel.text isEqualToString:stringToCheck])
{
//Pop to VC1
[self.navigationController popToViewController:[[self.navigationController viewControllers]objectAtIndex:0] animated:YES];
} else {
//Pop to VC3, not sure what to put here
// Pass the info
tvc.spring = [springs objectAtIndex:indexPath.section];
tvc.leaf = [tvc.spring.leafs objectAtIndex:indexPath.row];
tvc.buttonNumber = _buttonNumber;
}
}
You could simply check the cell's textLabel's string: If you really want to use segues and not the tableView's delegate method of: didSelectRowAtIndexPath: That's why I based my answer off what your initial question/code looks like.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
NSString *stringToCheck = #"AAA";
if ([cell.textLabel.text isEqualToString:stringToCheck])
{
// Pop to VC1
[self.navigationController popToRootViewControllerAnimated:YES];
}
else
{
// Push to VC3
MRTViewController3 *tvc = [self.storyboard instantiateViewControllerWithIdentifier:#"YourID"];
tvc.spring = [springs objectAtIndex:indexPath.section];
tvc.leaf = [tvc.spring.leafs objectAtIndex:indexPath.row];
tvc.buttonNumber = _buttonNumber;
[self.navigationController pushViewController:tvc animated:YES];
}
}
Use delegate method, get cell, compare text.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if([cell.textLabel.text isEqualToString:#"Your String"])
{
//Pop or present view controller
}
}
So what I am trying to do is I have an NSMutableArray of data I need to pass to another UITableViewController. This NSMutableArray is an array of NSDictionaries that contain the information I want to be displayed in the title of each table view cell. Here is my code before I segue.
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSIndexPath* indexPath = [self.tableView indexPathForCell:sender];
if ([segue.identifier isEqualToString:#"Title Query"]) {
UITableViewCell* cell = [self.tableView cellForRowAtIndexPath:indexPath];
NSString* cellText = cell.textLabel.text;
NSMutableArray* photosToBeShown = [self titleQuery:cellText];
if ([segue.destinationViewController respondsToSelector:#selector(setPhotoTitles:)]) {
[segue.destinationViewController performSelector:#selector(setPhotoTitles:) withObject: photosToBeShown];
NSLog(#"%#", photosToBeShown);
}
}
}
The method setPhotoTitles: that is called by the performSelector: withObject: is the setter of the property (NSMutableArray* ) photoTitles on the UITableViewController that I am seguing to because I wanted to collect the array so I could then call the methods below to set the titles of my table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Photo Title Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.textLabel.text = [self titleForRow:indexPath.row];
return cell;
}
- (NSString *) titleForRow: (NSUInteger) row
{
return self.photoTitles[row];
}
What happens when I run this code is I end up in an infinite loop with calls to my setter method (setPhotoTitles:). Now my question is what is the right conceptual way to get around this problem or how can I implement it this way without ending up in an infinite loop. I have all the information I need in my array but I need to pass the array to the new controller but also be able to use the UITableViewCell method to set the rows titles.
In the prepareForSegue: method, rather than overriding setPhotoTitles:, you should create a NSArray property in the destination view controller, as pass the photoTitles array to the NSArray property of the destination view controller. So your prepareForSegue method would look like this:
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSIndexPath* indexPath = [self.tableView indexPathForCell:sender];
if ([segue.identifier isEqualToString:#"Title Query"]) {
UITableViewCell* cell = [self.tableView cellForRowAtIndexPath:indexPath];
NSString* cellText = cell.textLabel.text;
NSMutableArray* photosToBeShown = [self titleQuery:cellText];
YourCustomViewController *customViewController = segue.destinationViewController;
customViewController.photosArrayProperty = photosToBeShown;
}
}
I'm implementing the search bar for my project. The search part is ok. I can display the raw data and filter the data with the search text. When I tap on the cell of the search result tableView, it didn't transition to the detail view. But for the raw data I can transition to the detail view. I use the prepareForSegue method as I'm using storyboard.
Here's the code so far,
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString: #"Book Detail Segue"]) {
BookDetailsTVC *bookDetailsTVC = segue.destinationViewController; // for the detail view controller
bookDetailsTVC.delegate = self;
if (self.tableView == self.searchDisplayController.searchResultsTableView) { // The if block is not working
NSLog(#"Search Display Controller");
bookDetailsTVC.book = [self.searchResults objectAtIndex: self.searchDisplayController.searchResultsTableView.indexPathForSelectedRow.row];
} else {
NSLog(#"Default Display Controller");
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
bookDetailsTVC.book = [self.objects objectAtIndex: indexPath.row];
}
}
}
When I tried using didSelectRowAtIndexPath method, I can transition to the detail view. But I got error message like that:
Finishing up a navigation transition in an unexpected state. Navigation Bar subview tree might get corrupted.
Unbalanced calls to begin/end appearance transitions for <BookDetailsTVC: 0x69b5300>.
Here's the code for didSelectRowAtIndexPath:
- (void) tableView: (UITableView *) tableView didSelectRowAtIndexPath: (NSIndexPath *) indexPath
{
BookDetailsTVC *bookDetailsTVC = [[BookDetailsTVC alloc] init];
if (tableView == self.searchDisplayController.searchResultsTableView) {
bookDetailsTVC.book = [self.searchResults objectAtIndex: self.searchDisplayController.searchResultsTableView.indexPathForSelectedRow.row];
[self performSegueWithIdentifier: #"Book Detail Segue" sender:self];
NSLog(#"Search Display Controller");
} else {
bookDetailsTVC.book = [self.objects objectAtIndex: indexPath.row];
[self performSegueWithIdentifier: #"Book Detail Segue" sender: self];
NSLog(#"Default Display Controller");
}
}
Thanks for help.
The problem is solved,
Change this in cellForRowAtIndexPath
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: CellIdentifier];
to
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier: CellIdentifier];
and in the prepareForSegue method, you can use the self.searchDisplayController.active to check the current tableview
if (self.searchDisplayController.active) {
NSLog(#"Search Display Controller");
bookDetailsTVC.book = [self.searchResults objectAtIndex: self.searchDisplayController.searchResultsTableView.indexPathForSelectedRow.row];
} else {
NSLog(#"Default Display Controller");
bookDetailsTVC.book = [self.objects objectAtIndex: self.tableView.indexPathForSelectedRow.row];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
self.selectedPerson = [self.searchResults objectAtIndex:indexPath.row];
PersonasDetalle *pd = [self.storyboard instantiateViewControllerWithIdentifier:#"PersonaDetalle"];
pd.persona = self.selectedPerson;
[self.navigationController pushViewController:pd animated:YES];
}
}