UITableViewCell not updating after selection on iOS 7 - ios

So I am having a really weird issue. My code works great on iOS 8 but doesn't on iOS 7 and I can't figure out why.
I have a tableview which has a list of items which when you select an item a checkmark is added to that item and if you select it again the checkmark is removed. Pretty simple right? :-p Like I said it works great on iOS 8, but when I run against iOS 7.1 the cell highlights, adds a checkmark, and removes the old title and replaces it with the default of Title. Afterwards, no matter how many times I tap on the cell, it never changes (but the underlying data does change).
Before Selection
After Selection
If I leave the screen and come back to it, the rows are displayed properly. I've verified that cellForRowAtIndexPath is being called and the correct values are being added to the cell.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return [[parkFinderSingleton.data valueForKey:#"amenities"] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"amenityFilterCell" forIndexPath:indexPath];
Amenity *currentAmenity;
NSArray *amenities = [parkFinderSingleton.data valueForKey:#"amenities"];
if (amenities != nil) {
currentAmenity = amenities[indexPath.row];
cell.textLabel.text = currentAmenity.amenityTitle;
NSLog(#"Cell Title %#", cell.textLabel.text);
if (currentAmenity.amenitySelected) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:#"amenityFilterCell" forIndexPath:indexPath];
Amenity *currentAmenity;
NSArray *amenities = [parkFinderSingleton.data valueForKey:#"amenities"];
if (amenities != nil) {
currentAmenity = amenities[indexPath.row];
if (currentAmenity.amenitySelected) {
currentAmenity.amenitySelected = NO;
cell.accessoryType = UITableViewCellAccessoryNone;
} else {
currentAmenity.amenitySelected = YES;
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
}
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
}
Any thoughts as to what might be happening?

Normally, dequeueReusableCellWithIdentifier:forIndexPath: should not be called in tableView: didSelectRowAtIndexPath:. If you want the cell for a specific indexPath, use [tableView cellForRowAtIndexPath:indexPath].
For:
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
If you just want to deselect a cell, use [tableView deselectRowAtIndexPath:animated:].

Related

UITableviewCell height not getting reset on scroll

I have a tableview which can be expanded on selecting the cell and collapses on selecting again. When you select, the cell should expand and display a label and when you select again it collapses and hides the label . The expanding and collapsing works fine, but if i scroll the tableview after expanding a cell it behaves weird. Once it goes out of the view and comes back , the cell will have the expanded cell's height but the label which is supposed to be shown in expanded cell is hidden.If i select the cell again it collapses and displays the label. I use ,
- (CGFloat)tableView:(UITableView *)t heightForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self tableView:t cellForRowAtIndexPath:indexPath];
if([self cellIsSelected:indexPath])
return cell.frame.size.height+35;
return cell.frame.size.height;
}
- (BOOL)cellIsSelected:(NSIndexPath *)indexPath {
// Return whether the cell at the specified index path is selected or not
NSNumber *selectedIndex = [self.selectedIndexes objectForKey:indexPath];
return selectedIndex == nil ? FALSE : [selectedIndex boolValue];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Deselect cell
NSLog(#"Select cell:%#",indexPath);
[self.tableView deselectRowAtIndexPath:indexPath animated:TRUE];
if([self pickTaskForIndexPath:indexPath].productSpecialMessage){
BOOL isSelected = ![self cellIsSelected:indexPath];
NSNumber *selectedIndex = [NSNumber numberWithBool:isSelected];
[self.selectedIndexes setObject:selectedIndex forKey:indexPath];
PickTaskTableviewCell *cell= [self.tableView cellForRowAtIndexPath:indexPath];
cell.message.hidden=false;
cell.messageLabel.text=[self pickTaskForIndexPath:indexPath].productSpecialMessage;
cell.messageLabel.lineBreakMode=NSLineBreakByTruncatingTail;
cell.messageLabel.numberOfLines=3;
if(cell.messageLabel.hidden==true){
cell.messageLabel.hidden = false;
} else {
cell.messageLabel.hidden = true;
}
NSLog(#"message:%#",cell.messageLabel.text);
[cell layoutIfNeeded];
}
self.tableView.rowHeight=UITableViewAutomaticDimension;
[self.tableView beginUpdates];
[self.tableView endUpdates];
}
indexPath is added to the selectedIndexes on didSelectRowAtIndexPath
Please help me
Cells should be configured only within cellForRowAtIndexPath. When a state change occurs that makes a cell need to look different, just reload that cell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
PickTaskTableviewCell *cell = (PickTaskTableviewCell *)[tableView dequeueReusableCellWithIdentifier:#"cell"];
// everything else you do to configure the cell goes here, then ...
// check the logic here, we want one condition that tells us whether to show the labels
if([[self cellIsSelected:indexPath] && self pickTaskForIndexPath:indexPath].productSpecialMessage){
// don't need these here
//NSNumber *selectedIndex = [NSNumber numberWithBool:isSelected];
// [self.selectedIndexes setObject:selectedIndex forKey:indexPath];
// PickTaskTableviewCell *cell= [self.tableView cellForRowAtIndexPath:indexPath];
cell.message.hidden=false;
cell.messageLabel.text=[self pickTaskForIndexPath:indexPath].productSpecialMessage;
cell.messageLabel.lineBreakMode=NSLineBreakByTruncatingTail;
cell.messageLabel.numberOfLines=3;
cell.messageLabel.hidden=NO;
} else {
cell.message.hidden=YES;
cell.messageLabel.hidden=YES;
}
NSLog(#"message:%#",cell.messageLabel.text);
// don't need this here
// [cell layoutIfNeeded];
return cell;
}
Selection (and presumably deselection) cause the need to update the cell, so...
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// don't deselect it here, just reload it
// more on this later...
[self.selectedIndexes setObject:selectedIndex forKey:indexPath];
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
// probably do the same in didDeselectRowAtIndexPath:
One last (optional) point. There's no need to maintain your own list of selected index paths, UITableView does that for you, so you could delete your selectedIndexes property and just use the table view methods, e.g....
- (BOOL)cellIsSelected:(NSIndexPath *)indexPath {
// Return whether the cell at the specified index path is selected or not
return [[self.tableView indexPathsForSelectedRows] containsObject:indexPath];
}

Multiple checkMark when row selected in UITableView IOS

I have a UITableView that displays checkmarks when a row is selected. The problem is that When i select a row in didSelectRowAtIndexPath and add a checkmark on the selected row it adds an additional checkmark. Here's my code
Any help would be very much appreciated.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
// Configure the cell...
cell.textLabel.text=[[Model.category objectAtIndex:indexPath.row] categoryName];
cell.imageView.image=[[Model.category objectAtIndex:indexPath.row]categoryImage];
//cell.detailTextLabel.text =#"Breve DescripciĆ³n de la categoria";
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
if ([self.tableView cellForRowAtIndexPath:indexPath].accessoryType == UITableViewCellAccessoryCheckmark) {
[self.tableView cellForRowAtIndexPath:indexPath].accessoryType =UITableViewCellAccessoryNone;
[self.cellSelected removeObject:indexPath];
}else {
[tableView cellForRowAtIndexPath:indexPath].accessoryType=UITableViewCellAccessoryCheckmark;
[self.cellSelected addObject:indexPath];
}
[self checkMark];
[tableView reloadData];
}
- (void)checkMark{
for (NSIndexPath * indexPath in self.cellSelected) {
[self.tableView cellForRowAtIndexPath:indexPath].accessoryType=UITableViewCellAccessoryCheckmark;
}
}
[self.tableView cellForRowAtIndexPath:indexPath] call in the didSelectRowAtIndexPath will not return the exact cell. It can be same cell, new cell or reused cell. If it is a reused cell at its accessory view has a checkmark, you will end up having two cells with checkmark.
Its better to store in the array and use it accordingly. If you are planning to have multiple selections, Use the code example below.
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.cellSelected = [NSMutableArray array];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//Cell Initialisation here
if ([self.cellSelected containsObject:indexPath])
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
//if you want only one cell to be selected use a local NSIndexPath property instead of array. and use the code below
//self.selectedIndexPath = indexPath;
//the below code will allow multiple selection
if ([self.cellSelected containsObject:indexPath])
{
[self.cellSelected removeObject:indexPath];
}
else
{
[self.cellSelected addObject:indexPath];
}
[tableView reloadData];
}
Try this:
Declare in your .m file:
#interface MyViewController () {
NSIndexPath *__selectedPath;
}
In tableView:cellForRowAtIndexPath: check if given indexPath is the same as stored in ivar:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//configure cell
if ([__selectedPath isEqual:indexPath]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
In tableView:didSelectRowAtIndexPath store pointer to selected NSIndexPath or nil if cell has been deselect
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (cell.accessoryType == UITableViewCellAccessoryNone) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
__selectedPath = indexPath;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
__selectedPath = nil;
}
}
I wrote it without checking in Xcode, so there could be some typos, but I show main idea.
In my opinion you shouldn't call [self checkMark]; in your tableView:cellForRowAtIndexPath: method.
Moreover if you want to have only one selected cell at a time, you should't create NSMutableArray to store NSIndexPath. It seems yours cellSelected stores 2 NSIndexPaths at a time, that why you have this strange behaviour.

Get Checkmarked Rows and Send to Console

All I'm looking to do is get the selected (checkmarked) rows from my UITableView and show them in my console log. Doesn't seem like it should be so difficult. I've found two methods that I'll display below. Neither work despite the logic mostly making sense to me. Which would you suggest and how can I tweak to make it work?
My TableView Code:
I don't think this is completely necessary to the issue, but I know it sometimes helps to see the whole picture.
#pragma mark - tableView datasource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [self.places count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NSDictionary *tempDictionary= [self.places objectAtIndex:indexPath.row];
cell.textLabel.text = [tempDictionary objectForKey:#"name"];
if([tempDictionary objectForKey:#"vicinity"] != NULL)
{
cell.detailTextLabel.text = [NSString stringWithFormat:#"%#",[tempDictionary objectForKey:#"vicinity"]];
}
else
{
cell.detailTextLabel.text = [NSString stringWithFormat:#"Address Not Available"];
}
return cell;
}
//Handles tableView row selection and addition and removal of the checkmark
- (void)tableView:(UITableView *)theTableView
didSelectRowAtIndexPath:(NSIndexPath *)newIndexPath {
[theTableView deselectRowAtIndexPath:[theTableView indexPathForSelectedRow] animated:NO];
UITableViewCell *cell = [theTableView cellForRowAtIndexPath:newIndexPath];
if (cell.accessoryType == UITableViewCellAccessoryNone) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
// Reflect selection in data model
} else if (cell.accessoryType == UITableViewCellAccessoryCheckmark) {
cell.accessoryType = UITableViewCellAccessoryNone;
// Reflect deselection in data model
}
}
Method 1:
Add a conditional statement to the end of the checkmark handler to add/remove selections to and from an array. Then create a button action that simply calls the array and displays it in the console. I think this is clunky but could work.
//Handles tableView row selection and addition and removal of the checkmark
- (void)tableView:(UITableView *)theTableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[theTableView deselectRowAtIndexPath:[theTableView indexPathForSelectedRow] animated:NO];
UITableViewCell *cell = [theTableView cellForRowAtIndexPath:indexPath];
if (cell.accessoryType == UITableViewCellAccessoryNone) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
//Reflect selection in data model
} else if (cell.accessoryType == UITableViewCellAccessoryCheckmark) {
cell.accessoryType = UITableViewCellAccessoryNone;
//Reflect deselection in data model
}
if ([[theTableView cellForRowAtIndexPath:indexPath] accessoryType] == UITableViewCellAccessoryCheckmark) {
[_selectedCellIndexes addObject:indexPath];
}
}
- (IBAction)sendResults:(id)sender {
NSLog(#"Add these: %#", _selectedCellIndexes);
}
Method 2:
Get the selected rows AND send to console log only when button is tapped. This seems to be the more logical method, but I can't seem to get it to work either. It doesn't throw any errors, but returns "Selected Items: (null)" in the console. What have I missed?
//Sends checkmarked items to console log
- (IBAction)sendResultsOption1:(id)sender {
NSMutableArray *aList = [[NSMutableArray alloc] init];
for (NSIndexPath *indexPath in _tableView.indexPathsForSelectedRows) {
NSString *r = [NSString stringWithFormat:#"%li",(long)indexPath.row];
[aList addObject:r];
}
NSLog(#"Selected Items: %#", _aList);
}
For what it's worth, I've also followed the instructions here without any luck. Hope you guys can help. Thanks in advance!
In Method 1, your method looks like this:
- (void)tableView:(UITableView *)theTableView
didSelectRowAtIndexPath:(NSIndexPath *)newIndexPath {
But you're referring to indexPath in the body. You don't have an indexPath (the undeclared identifier), but you have a newIndexPath, so at a minimum, this is the start of your problems and should be fixed first.
Giving your variables the right names looks like it should work for this approach...
In Method 2, the problem is none of your table view cells are selected. In you didSelectRowAtIndexPath method, you check the accessory icon to a check mark, then you deselect the row. So there are no objects in _tableView.indexPathsForSelectedRows.
In this approach, you need to change your for loop. Instead you need to iterate through every index path, and check on the accessory icon. If it's a check mark, add it to the array. Now log the array.
As far as which approach would be preferable, it depends on how you intend to use this ultimately. Obviously, the end goal isn't to NSLog the checkmarked rows--this is an iOS app we're talking about.

ios - Expand table view cell like Twitter app

I want to have the same behavior as Twitter app when selecting a tweet that is : expanding the row and adding supplementary content.
So this is not only a basic row resize. I think i need 2 different custom cells, so i did something like that :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([indexPath compare:self.selectedIndexPath] != NSOrderedSame) {
FirstCell *cell = [tableView dequeueReusableCellWithIdentifier:#"FirstCell"];
if (!cell) {
cell = [[FirstCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"FirstCell"];
}
cell.firstnameLabel.text = [[self.items objectAtIndex:indexPath.row] objectForKey:#"firstname"];
return cell;
}
else {
SecondCell *cell = [tableView dequeueReusableCellWithIdentifier:#"SecondCell"];
if (!cell) {
cell = [[SecondCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"SecondCell"];
}
cell.firstnameLabel.text = [[self.items objectAtIndex:indexPath.row] objectForKey:#"firstname"];
cell.lastnameLabel.text = [[self.items objectAtIndex:indexPath.row] objectForKey:#"lastname"];
return cell;
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if ([indexPath compare:self.selectedIndexPath] == NSOrderedSame) {
return 80.0;
} else {
return 44.0;
}
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.selectedIndexPath = indexPath;
[tableView reloadData];
}
My problem here is that i want to keep a fluid animation like Twitter app which is not my case because of [tableView reloadData] (which is mandatory i think since i have 2 different custom cell).
So anyone knows a workaround to my needed or maybe someone knows how Twitter app is handling this animation stuff ?
You want to reload that cell with an animation:
[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];
[tableView beginUpdates];
[tableView endUpdates];
this triggers an update of the cell row sizes for the whole tableView.... referenced from :iPhone - Smooth animation for UITableViewCell height change including content update
Happy Coding!
It is actually rather easy to implement this using two custom cells like you originally intended by just combining your original code with rooster117's solution. Replace [tableView reloadData] in your tableView:didSelectRowAtIndexPath: with:
[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView endUpdates];
and you should have your solution.

iOS UITableViewCellAccessoryCheckmark Visible ob every scroll

I have a list which I have using as a check boxes. I have enable or disable Check mark on row on select. But when I scroll the list its make mark row after every 10 rows.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *oldCell = [tableView cellForRowAtIndexPath:indexPath];
if (oldCell.accessoryType == UITableViewCellAccessoryCheckmark)
{
oldCell.accessoryType = UITableViewCellAccessoryNone;
}
else
{
oldCell.accessoryType = UITableViewCellAccessoryCheckmark;
}
}
UItableView reuses the cell in every scroll so using condition as per accessory type is not a good practice. You can Create an NSMutableArray with selected items and Check as per the Condition below.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
if ([selected containsIndex:indexPath.row]) {
[cell setAccessoryType:UITableViewCellAccessoryCheckmark];
} else {
[cell setAccessoryType:UITableViewCellAccessoryNone];
}
// Do the rest of your code
return cell;
}
in didSelectrowAtindexpath method you can Add and remove the Selected items.
Its because UITableView reuses the cell.
So, in the method cellForRowAtIndexPath, you will have to check for a particular cell (of a particular section and row), if it needs to be checked on, provide the accessory type.
If not needed for that cell, provide accessory type as none.
You need to put your logic to set accessory type for cell in cellForRowAtIndexPath, and to identify the cell to mark with check mark you can mark the object in the list in didSelectRowAtIndexPath: or manage an array of selected/unselected objects of the list here.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath];
if ([selectedCell accessoryType] == UITableViewCellAccessoryNone) {
[selectedCell setAccessoryType:UITableViewCellAccessoryCheckmark];
[NSMutableArray addObject:[AnotherMutableArray objectAtIndex:indexPath.row]];
} else {
[selectedCell setAccessoryType:UITableViewCellAccessoryNone];
[NSMutableArray removeObject:[AnotherMutableArray objectAtIndex:indexPath.row]];
}
[tableView deselectRowAtIndexPath:indexPath animated:NO];
}
Also in your viewDidLoad, instantiate you both mutable arrays-
yourmutableArray1 = [[NSMutableArray alloc]init];
yourmutableArray2 = [[NSMutableArray alloc]init];

Resources