Inside cellForRowAtIndexPath, saving cell to #property, always last cell in tableview - ios

Using the following delegate method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
PLOTCheckinTableViewCell *cell = (PLOTCheckinTableViewCell *)[self.checkinsTableView dequeueReusableCellWithIdentifier:CheckinCellIdentifier forIndexPath:indexPath];
[cell setSwipeGestureWithView:crossView color:redColor mode:MCSwipeTableViewCellModeSwitch state:MCSwipeTableViewCellState2 completionBlock:^(MCSwipeTableViewCell *cell, MCSwipeTableViewCellState state, MCSwipeTableViewCellMode mode) {
self.indexPathToDelete = [tableView indexPathForCell:cell];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Delete?"
message:#"Are you sure your want to remove this checkin?"
delegate:self
cancelButtonTitle:#"No"
otherButtonTitles:#"Yes", nil];
[alertView show];
}];
}
Then inside the UIAlertView delegate method:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
// No
if (buttonIndex == 0) {
}
// Yes
else {
PLOTCheckinTableViewCell *cell = (PLOTCheckinTableViewCell *)[self.checkinsTableView cellForRowAtIndexPath:self.indexPathToDelete];
[self.checkins removeObjectAtIndex:self.indexPathToDelete.row];
[self.checkinsTableView deleteRowsAtIndexPaths:#[self.indexPathToDelete] withRowAnimation:UITableViewRowAnimationFade];
self.indexPathToDelete = nil;
}
}
However, whenever I hit "Ok" in the alert view, the cell that's deleted is always the last one in the tableview, ie. not the cell the user actually swiped.
Is it something to do with the dequeuing?

Yes. You should not be referencing to your cell. Instead, try to reference to the actual object that caused the cell to exist (i.e. if this is a list of messages, the message that is displayed within that cell).
The cell object is being reused and the UIAlertView wont know about that. Even if you have 1000 items in your list, you are not going to have more than 20 cells. They are always going to be reused thorough your table view scrolling.
You should be looking into removing an element from your data source array instead of removing the cell itself. After removing the element, you can always reload your table view to visually reflect the elemnt removed state.

Related

Update value in UITableViewCell from another methd iOS

I have method called
- (void)redeemOfferService
{
// in this method i want access OffersBoughtCellTableViewCell,
how to access tableview cell here ?
// want to update selected tableview cell values
// here i am getting selected cell details in selectedOfferBoughtObj which
is actually NSObject in that i am having five string values
i want show that values in my tableview cell how can i ?
//
}
The above method is calling from
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex: (NSInteger)buttonIndex
{
if(alertView.tag == 10)
{
if(buttonIndex == 0)
{
[self redeemOfferService];
}
}
}
and this method is calling when i tap on tableview cell i.e.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UIAlertView *logoutAlert = [[UIAlertView alloc]initWithTitle:#"Are you sure you want to redeem?" message:nil delegate:self cancelButtonTitle:#"Yes" otherButtonTitles:#"No", nil];
logoutAlert.tag = 10;
[logoutAlert show];
}
i know there is way, we can send sender from button click but i want in above scenario.
Use a global variable in your class NSIndexPath type and on selected store your selected indexpath
NSIndexPath *selectedIndexPath; //declare it as a global
After assign selected value in didSelect
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath: (NSIndexPath *)indexPath
{
UIAlertView *logoutAlert = [[UIAlertView alloc]initWithTitle:#"Are you sure you want to redeem?" message:nil delegate:self cancelButtonTitle:#"Yes" otherButtonTitles:#"No", nil];
logoutAlert.tag = 10;
[logoutAlert show];
selectedIndexPath = indexPath;
}
And change like this in your function
- (void)redeemOfferService
{
OffersBoughtCellTableViewCell * _selectedCell = [offersTableView cellForRowAtIndexPath:selectedIndexPath]; //Now Its your selected cell Try this .
}
It will work fine try once and revert back
You are surely updating the tableview from some NSMutableArray, meaning you are filling up your tableview with this array.
In your redeemOfferService method, change the value of that Object inside the NSMutableArray with which you are filling up the tableview.
Then call [self.tableviewName reloadData]
You can use the indexPathForSelectedRow property of UITableView to access selected indexPath.
- (void)redeemOfferService
{
OffersBoughtCellTableViewCell *selectedCell = (OffersBoughtCellTableViewCell *)[tableViewObject cellForRowAtIndexPath:offersTableView.indexPathForSelectedRow];
}
This gives you the selected OffersBoughtCellTableViewCell

Last selected Row in UITableView

I have UITableView, in which if any row is selected i am storing some values. If any other row is selected i am displaying an alertView with warning that his previous selection will be gone. This alerView has 2 buttons YES and NO. If YES is selected go on to selecting the new row which is fine. But if no is selected i want reselect the previous row. I can post the code if asked. But it seems more of logical question than code oriented.
Thank you.
first store the clicked tableview index to your global variable in UITableView Delegate like below,
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView==yourTableview) {
yourMemberVariable=[indexPath row];
}
}
then check yourMemberVariable in UIAlertView delegate
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if(buttonIndex == 0)//OK button pressed
{
//do something
}else{
if(yourMemberVariable>someValue){ //perform your own conditions whatever
[myTableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:yourMemberVariable inSection:0] animated:NO scrollPosition:UITableViewScrollPositionTop];
}
}
}
You want to know the last row selected ,you can do this by setting
tableView.remembersLastFocusedIndexPath = true
in viewDidLoad function.
and then calling
tableView.indexPathForSelectedRow
wherever required.
It's simple. Just save previous row into class member. Good luck!
Keep track of the last selected item. On the select delegate for the table, store what was selected in a variable accessible througout the class.
NSInteger selectedRow = 0;
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
selectedRow = [indexPath row];
}
Now you know what element to select if NO is pressed in the UIAlertView delegate
[myTableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:selectedRow inSection:0] animated:NO scrollPosition:UITableViewScrollPositionTop];

How Could I pass the selected Cell on UITableViewController with UIActionSheet

I have UITableViewController which pop UIActionSheet when user click on a cell with accessoryType =UITableViewCellAccessoryDetailDisclosureButton;
Now the method -(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { works fine, except, i dont know in which cell user clicked.
I could , for example, declare a property where I save selected cell in didSelectRowAtIndexPath:(NSIndexPath *)indexPath { but I think there must be a better way.
I usually set the action sheet's tag to be the index path's row, but that only works if you don't care about the section number. If you do need the section as well, creating a property would be the best way to go about it.
You could also mess with a category and associated objects, but for me personally that would be too involved.
If your table only have one section, you could store which row presented the action sheet in the tag, something like this.
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath {
NSString *title = [NSString stringWithFormat:#"Sheet for row %i", indexPath.row];
UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:title delegate:self cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles:#"Option 1", #"Option 2", nil];
[sheet setTag:indexPath.row];
[sheet showInView:self.view];
}
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
int row = actionSheet.tag;
NSLog(#"Selected actionSheet buttonIndex %i for row: %i", buttonIndex, row);
}
It's probably the easiest way, but I wouldn't say it's the best way to do it.
Implement this UITableView delegate method to get the index path of the cell when the accessory button is tapped:
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath];
}

Is this normal behavior for UITableViewCellAccessoryDetailDisclosureButton

I notice when I set MessageTableViewCell accessoryType =UITableViewCellAccessoryDetailDisclosureButton and I tab on the "arrow button" itself, the pop menu that is declared on didSelectRowAtIndexPath: don't show up??!!! it works fine if I tabbed on the other area of cell, except on the "arrow button" itself,
However, if I used UITableViewCellAccessoryDisclosureIndicator cell accessory type instead, it works fine, even if I tabbed on the arrow itself.
I wonder if this normal behavior, a bug, or I did something wrong.
I prefer UITableViewCellAccessoryDetailDisclosureButton because in my opinion its more clear when you want to get attention of user.
- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
static NSString* CellIdentifier = #"MessageCellIdentifier";
MessageTableViewCell* cell = (MessageTableViewCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = /*[*/[[MessageTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] /*autorelease]*/;
}
CastNearAppDelegate *appDelegate = (CastNearAppDelegate *)[[UIApplication sharedApplication] delegate];
Message* message = [appDelegate.dataModel messageWithID: indexPath.row];
if (!message.isSentByUser)
{
cell.accessoryType =/*UITableViewCellAccessoryDetailDisclosureButton; */UITableViewCellAccessoryDisclosureIndicator;
}else
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
message.bubbleSize = [SpeechBubbleView sizeForText:message.text];
[cell setMessage:message];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UIActionSheet *popupQueryOptions = [[UIActionSheet alloc]
initWithTitle:#"Options for Blocking and Spam Reporting"
delegate:self
cancelButtonTitle:#"Cancel"
destructiveButtonTitle:#"Block Sender"
otherButtonTitles:/*#"Block Sender",*/
#"Inappropriate Content",
/*#"Tell a Friend via Facebook",
#"Tell a Friend via Twitter",*/
nil];
popupQueryOptions.actionSheetStyle = UIActionSheetStyleBlackOpaque;
[popupQueryOptions showInView:[UIApplication sharedApplication].keyWindow];
}
They are used for different things. The indicator is just an indicator whereas the button allows you to have a different action e.g. acting like a button.
UITableView has two possible methods that it will call on it's delegate when the cell is tapped.
When the row itself is tapped the following is called
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
When you tap on the accessory then the tableView calls
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
You can also wire these up independently in Interface Builder. So this behaviour is quite deliberate.
When you use the disclosure button, the delegate method tableView:accessoryButtonTappedForRowWithIndexPath: is called instead of tableView:didSelectRowAtIndexPath:.
The disclosure button is usually used for some kind of secondary action of a cell, e.g. in the Wifi settings, it shows options for a network, while tapping the entire cell connects to it.

How to use a UIActionSheet to confirm deletion of a UITableViewCell

I have a standard CoreData/NSFetchedResultsController/UITableView setup. There is no edit button on top, but right-swiping a row will reveal the delete button. If the delete button pressed, I want to pop up a UIActionSheet to confirm the deletion.
Before the UIActionSheet, I just implemented tableView:commitEditingStyle:forRowAtIndexPath: and in it, I called deleteObject: on the managedObjectContext for the indexPath and let the FetchedResultsController overrides handle the visual aspect of it.
Now, I have tableView:commitEditingStyoe:forRowAtIndexPath: create the UIActionSheet and show it. I implemented actionSheet:clickedButtonAtIndex: and put the deleteObject: in the case where the delete button is pressed. The problem is that I need the indexPath of the item to delete and it is no longer in scope.
Do I...
A) delete the ManagedObject in tableView:commitEditingStyle:forRowAtIndexPath: and either save or roll back in actionSheet:clickedButtonAtIndex: ?
B) store the index path as a property so I can set it in tableView:commitEditingStyle:forRowAtIndexPath: and read it in actionSheet:clickedButtonAtIndex:?
C) something else...
Option A seems like more overhead than necessary. Either way it will be deleted, just sometimes it will be rolled back.
Option B seems very hawkish. An external value like that doesn't seem to fit the object oriented model very well.
Is there a generic object in UIActionsSheet for passing in values like this? Or is there something else I'm missing?
I needed a similar solution. I solved this by creating an ivar NSIndexPath * currentPath; and used the following idea for tableView:commitEditingStyle:forRowAtIndexPath:
- (void) tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
UIAlertView * alert;
UITableViewCell * cell;
if (editingStyle == UITableViewCellEditingStyleDelete)
{
// save current indexPath
[currentPath release];
currentPath = [indexPath retain];
// display warning
alert = [[UIAlertView alloc] initWithTitle:#"Really?"
message:#"Are you really really sure you want to delete this item?"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Delete", nil];
[alert show];
[alert release];
// clear delete confirmation from table view cell
cell = [tableView cellForRowAtIndexPath:indexPath];
[cell setEditing:NO animated:NO];
[cell setEditing:YES animated:YES];
};
return;
}
If the user taps the delete button, I then remove the row from the UITableViewCell and remove the entry from the data source. If they tap cancel, I simply ignore the action.
Instead of the indexPath I would store the managed object itself in a property in the -tableView:commitEditingStyle:forRowAtIndexPath: method:
self.deleteCandidate = [self.controller objectAtIndexPath:indexPath]
and then delete that object (or not) in -actionSheet:clickedButtonAtIndex:, and set self.deleteCandidate = nil in either case.
The reason is that the fetched results controller objects might change between those two methods being called.
Another option is to use "Associative References" (objc_setAssociatedObject, objc_getAssociatedObject) to store a reference from the UIActionSheet to the object in question.

Resources