didSelectRowAtIndexPath selecting more than one row at a time - objective-c

I have a UITableView populated with 27 rows. I am trying to change the accessoryType of the selected cell. I do that in the didSelectRowAtIndexPath: delegate method.
The problem I am facing is that, when selecting a row and changing the accessoryType of the cell, the eleventh row from that row also gets modified.
I have tried printing the [indexPath row] value, but it's showing only the row that was selected and not another one.
I am really puzzled by such stuff; please help me out.
ADDED THE CODE cellForRowAtIndexPath method
UITableViewCell *cell;
if ([indexPath row] == 0) {
cell = [tableView dequeueReusableCellWithIdentifier:#"acell"];
}
else {
cell = [tableView dequeueReusableCellWithIdentifier:#"bcell"];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
if (cell == nil && [indexPath row] != 0) {
cell = [[[UITableViewCell alloc] initWithStyle:
UITableViewCellStyleSubtitle reuseIdentifier:#"bcell"] autorelease];
}
else if(cell == nil && [indexPath row] == 0){
cell = [[[UITableViewCell alloc] initWithStyle:
UITableViewCellStyleSubtitle reuseIdentifier:#"acell"] autorelease];
}
if ([cell.contentView subviews]){
for (UIView *subview in [cell.contentView subviews]) {
[subview removeFromSuperview];
}
}
if ([indexPath row] == 0) {
cell.textLabel.text = #"Select All";
cell.textLabel.font = [UIFont boldSystemFontOfSize:13.0f];
}
else {
cell.textLabel.text = #"Some Text Here"
cell.detailTextLabel.text = #"Another piece of text here"
}
return cell;
I am doing %10 because the behaviour is repeating after 11th row, hence trying to create new object for every 11th row.
My didSelectRowAtIndexPath methos code is
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (cell.accessoryType == UITableViewCellAccessoryCheckmark) {
if ([indexPath row] != 0) {
NSIndexPath *tempIndex = [NSIndexPath indexPathForRow:0 inSection:0];
UITableViewCell *tempCell = [tableView cellForRowAtIndexPath:tempIndex];
tempCell.accessoryType = UITableViewCellAccessoryNone;
}
cell.accessoryType = UITableViewCellAccessoryNone;
}
else{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
if ([indexPath row] == 0) {
for (int i = 0; i < [dataSource count]; i++) {
NSIndexPath *tempIndex = [NSIndexPath indexPathForRow:i+1 inSection:0];
UITableViewCell *tempCell = [tableView cellForRowAtIndexPath:tempIndex];
if (cell.accessoryType == UITableViewCellAccessoryCheckmark) {
tempCell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else{
tempCell.accessoryType = UITableViewCellAccessoryNone;
}
}
}
Please help me in multiple selection or anyother way to solve the problem of multiple selection.
Thanks in advance!!

Here's one way to do it:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
[cell.textLabel setText:[NSString stringWithFormat:#"Row %d", indexPath.row]];
NSIndexPath* selection = [tableView indexPathForSelectedRow];
if (selection && selection.row == indexPath.row) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
// Configure the cell.
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryCheckmark;
}
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryNone;
}
Remember every cell in the table view is actually the same object being re-used. If you don't set the accessory type every time cellForRowAtIndexPath is called, when new cells scroll onto the screen they're going to all have the same accessory.
Multiple Select
For multiple selection it's a bit more complicated.
Your first option: Undocumented API
Note that this only works when the table's in editing mode. Set each cell's editing style to the undocumented UITableViewCellEditingStyleMultiSelect. Once you do that, you can get the table view's selection via an undocumented member of UITableView: indexPathsForSelectedRows. That should return an array of the selected cells.
You can expose this bit of functionality by putting this in a header:
enum {
UITableViewCellEditingStyleMultiSelect = 3,
};
#interface UITableView (undocumented)
- (NSArray *)indexPathsForSelectedRows;
#end
Then set the editing style for each cell like so:
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewCellEditingStyleMultiSelect;
}
When the table is in editing mode you'll see the multi-select controls on your cells.
To look through other undocumented API, you can use the nm command line utility like this:
nm /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/System/Library/Frameworks/UIKit.framework/UIKit
Your second option: Manage the selection yourself
Have your UITableView subclass contain an array that indicates which cells are selected. Then in cellForRowAtIndexPath, configure the cell's appearance using that array. Your didSelectRowAtIndexPath method should then look something like this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([tableView indexPathIsSelected:indexPath]) {
[tableView removeIndexPathFromSelection:indexPath];
} else {
[tableView addIndexPathToSelection:indexPath];
}
// Update the cell's appearance somewhere here
[tableView deselectRowAtIndexPath:indexPath animated:NO];
}
This assumes you create indexPathIsSelected, removeIndexPathFromSelection, and addIndexPathToSelection methods in your UITableView subclass. These methods should do exactly what their names imply: Add, remove, and check for index paths in an array. You wouldn't need a didDeselectRowAtIndexPath implementation if you go with this option.

Remember every cell in the table view is actually the same object being re-used. If you don't set the accessory type every time cellForRowAtIndexPath is called, when new cells scroll onto the screen they're going to all have the same accessory." - daxnitro
This is where I got caught. I had mine set up so that in my "cellForRowAtIndexPath" function, I would only change the accessory for those specified in my array of checked cells, when what I should have done was update the accessory for all cells in the table.
In other words:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//normal set up
//retrieve key
NSUserDefaults *settings = [NSUserDefaults standardUserDefaults];
id obj = [settings objectForKey:#yourKey];
//if the array is not populated, keep standard format for all cells
if (obj == nil){
selectedStyles = [[NSMutableArray alloc] initWithObjects:nil];
[cell setAccessoryType:UITableViewCellAccessoryNone]; //no check mark
[cell textLabel].textColor = [[UIColor alloc] initWithRed:0.0/255 green:0.0/255 blue:0.0/255 alpha:1.0]; //keep black color
}
//else retrieve information from the array and update the cell's accessory
else{
//if the cell is in your array, add a checkbox
[cell setAccessoryType:UITableViewCellAccessoryCheckmark]; //add check box
[cell textLabel].textColor = [[UIColor alloc] initWithRed:50.0/255 green:79.0/255 blue:133.0/255 alpha:1.0]; //change color of text label
//if the cell is not in your array, then keep standard format
[cell setAccessoryType:UITableViewCellAccessoryNone]; //no check mark
[cell textLabel].textColor = [[UIColor alloc] initWithRed:0.0/255 green:0.0/255 blue:0.0/255 alpha:1.0]; //keep black color
Hope this helps!

Related

Expand and Collapse specific row after selected in UITableViewCell

I am trying to implement the expand/collapse table view cell. It's working good for many rows; they can be expanded and collapsed after clicked.
But what happen is when my table view has only one row; when I click it and then all the rest of the rows are expended too even though they are empty.
Here my code:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(selectedIndex == indexPath.row){
return 100;
}else{
return 44;
}
}
// Display in cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellIdentifier = #"CommentTableCell";
//-- try to get a reusable cell --
CommentTableCell *cell = (CommentTableCell *) [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
//-- create new cell if no reusable cell is available --
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:cellIdentifier owner:self options:nil];
cell = [nib objectAtIndex:0];
}
VocabularyController *vc;
// Display word from database else display vocabulary when searching
if (tableView != self.strongSearchDisplayController.searchResultsTableView) {
vc = [self.myArray objectAtIndex:indexPath.row];
}else{
vc = [self.filteredArray objectAtIndex:indexPath.row];
}
cell.nameLabel.text = vc.korean;
return cell;
}
// Selected Cell
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if(selectedIndex == indexPath.row){
selectedIndex = -1;
NSIndexPath* rowToReload = [NSIndexPath indexPathForRow:indexPath.row inSection:0];
NSArray* rowsToReload = [NSArray arrayWithObjects:rowToReload, nil];
[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:rowsToReload withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];
return;
}
if(selectedIndex >= 0){
NSIndexPath *previousPath = [NSIndexPath indexPathForRow:selectedIndex inSection:selectedSection];
selectedIndex = indexPath.row;
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:previousPath] withRowAnimation:UITableViewRowAnimationFade];
}
// Finally set the selected index to the new selection and reload it to expand
selectedIndex = indexPath.row;
[tableView beginUpdates];
[tableView endUpdates];
}
// I would like show my screen shot.
How to expand only selected row when there is only one result after search? How to keep the other empty stay the same !
Thank for reading.
I don't think you can change that behavior for a one row table -- the table shows the cell dividers based on the height of that one row. If you don't want this look, then I think you have to set the separatorStyle to UITableViewCellSeparatorStyleNone. You could make a custom cell that has a line at the bottom to mimic a cell separator if want to see a line only at the bottom of filled cells.

Default CellAccessory

How can I set first row of tableview checkmarked when the app started? I mean when I start app now, there are few rows in tableview and when I click on some of them its accessory change to checkmark. When click another one, the another one comes checkmarked and previous checkmark dissapears. So now I want first row of tableview to be checkmarked when app is started and then when I click another row it 'uncheckmark' and 'checkmark' new row etc...
Try the options suggested in these posts how to apply check mark on table view in iphone using objective c? or iPhone :UITableView CellAccessory Checkmark
Basically you need to keep track of the checkmarks using say a dictionary or so. And In viewDidLoad or init method make the first cell as checked in the dictionary. While drawing cells, always check if the corresponding entry in the dictionary is checked or not and display check mark accordingly. When user taps on a cell, modify the value in dictionary to checked/unchecked.
Update:
Here is a sample code.
In .h file declare a property as
#property(nonatomic) NSInteger selectedRow;
Then use the below code,
- (void)viewDidLoad {
//some code...
self.selectedRow = 0; //if nothing should be selected for the first time, then make it as -1
//create table and other things
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// create cell and other things here
if (indexPath.row == self.selectedRow)
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// some other code..
if (indexPath.row != self.selectedRow) {
self.selectedRow = indexPath.row;
}
[tableView reloadData];
}
You could set an integer to a variable similar to "checkedCell", have that value default to cell 0, and in the cellForRowAtIndexPath check to see if the cell is already checked, if not change the accessory to make it checked and update your variable.
-(void)viewWillAppear{
currentCheckedCell = 0;
}
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//
// Create cells -- if indexpath.row is equal to current checked cell reload the table with the correct acessories.
//
static NSString *cellIdentifier = #"RootMenuItemCell";
MyCell *cell = (MyCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[MyCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
NSArray *nibContents = [[NSBundle mainBundle] loadNibNamed:#"MyCell" owner:self options:nil];
for (UIView *view in nibContents) {
if ([view isMemberOfClass:[MyCell class]]) {
cell = (MyCell *)view;
break;
}
}
//OTHER CELL CREATION STUFF HERE
// cell accessory
UIImage *accessory = [UIImage imageNamed:#"menu-cell-accessory-default.png"];
UIImage *highlighted = [UIImage imageNamed:#"menu-cell-accessory-selected.png"];
if(indexPath.row == currentCheckedCell){
//DEFAULT ACCESSORY CHECK
// cell.accessoryType = UITableViewCellAccessoryCheckmark;
UIImageView *accessoryView = [[UIImageView alloc] initWithImage:accessory highlightedImage:highlighted];
}else{
//DEFAULT ACCESSORY NONE
// cell.accessoryType = UITableViewCellAccessoryNone;
UIImageView *accessoryView = [[UIImageView alloc] initWithImage:accessory highlightedImage:accessory];
}
[cell setAccessoryView:accessoryView];
}
return cell;
}
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//UPDATE SELECTED CELL & RELOAD TABLE
currentCheckedCell = indexPath.row;
[self.tableview reloadData];
}
Also worth noting that my examples uses custom images for accessories.
If you check out the link #ACB provided you'll find a very similar concept.
You can use,
if (firstCellSelected)
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
Set the firstCellSelected flag in viewDidLoad method.

UITableView cellForRowAtIndexPath checkmark accessory setting acting weird

Setup: I have a property called "_itemListArray(ivar)", that is set to a list of "Item(NSString itemName, NSString itemPrice)". I populate a UITableView with these items and the user can select multiple rows, displaying a checkmark on that row. The indexPath of the checked cell is stored to an IVAR(_selectedItemRows). If the user selects the row again, the checkmark accessory is set to none and the indexPath is removed from the IVAR(_selectedItemRows). In "cellForRowAtIndexPath" I check the currently queued indexPath against all indexPaths in _selectedItemRows(array of indexPaths of checked cells). If the index path is in array, I check the dequeued cell, if not, I uncheck it.
Problem: The checkmark accessory is set correctly(didSelectRowAtIndexPath), but when I scroll, it acts funky. For example, if I check the first cell then scroll down, then scroll back up to the first cell, nslogs have verified that my program knows to check the cell, but it doesn't seem to.
Also, if I check 2 or more cells, scroll down, then scroll back up, usually the last cell is the only checked.
Code:
#implementation
#synthesize itemListArray = _itemListArray;
#synthesize selectedItemRows = _selectedItemRows;
-(void)setItemListArray:(NSArray *)itemListArray
{
_itemListArray = itemListArray;
[_propTableView reloadData];
}
- (void)viewDidLoad
{
[super viewDidLoad];
_selectedItemRows = [[NSMutableArray alloc] init];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [_itemListArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Item Selected Reuse"; //Identifier of prototype cell
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (nil == cell) { //If somethong goes wrong, all hell breaks loose.
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
NSLog(#"%s", __PRETTY_FUNCTION__);
}
// Configure the cell...
Item *curItem = [_itemListArray objectAtIndex:indexPath.row]; //Get the model information at row location.
cell.textLabel.text = curItem.itemName; //Set the name of the item in title field
cell.detailTextLabel.text = curItem.itemPrice; //Set the price of the item in the detail field.
for(NSIndexPath * elem in _selectedItemRows)
{ //Enumerate through checked cells
//NSIndexPath *ip = [_selectedItemRows objectAtIndex:x];
if ([indexPath compare:elem] == NSOrderedSame) { //If the current cell index path ='s any index path in the array of checked cells, check this cell.
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
}
return cell;
}
//pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; //Get cell clicked on.
if(cell.accessoryType == UITableViewCellAccessoryNone){ //When selected, if the cell is checked, uncheck it.
cell.accessoryType = UITableViewCellAccessoryCheckmark;
[_selectedItemRows addObject:indexPath]; //Add the index path of checked cell into array to use later for comparisons
} else {
if(cell.accessoryType == UITableViewCellAccessoryCheckmark){ //If the cell is checked, uncheck it when clicked on
cell.accessoryType = UITableViewCellAccessoryNone;
[_selectedItemRows removeObject:indexPath]; //Remove that index path of unchecked cell from index array
}
}
[tableView deselectRowAtIndexPath:indexPath animated:YES];//Deselect row after done.
}
#end
//Other code left out for brevity sake
You have a logic error in your code. Think about what happens in this bit of code:
for(NSIndexPath * elem in _selectedItemRows)
{ //Enumerate through checked cells
//NSIndexPath *ip = [_selectedItemRows objectAtIndex:x];
if ([indexPath compare:elem] == NSOrderedSame) { //If the current cell index path ='s any index path in the array of checked cells, check this cell.
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
}
Unless the index path for the current row happens to be the last one in _selectedItemRows the cell will have the checkmark cleared. It will set the check mark when it finds it in _selectedItemRows and then unset it as it continues searching. Instead you want to replace this with something like the following:
cell.accessoryType = UITableViewCellAccessoryNone;
for(NSIndexPath * elem in _selectedItemRows)
{ //Enumerate through checked cells
//NSIndexPath *ip = [_selectedItemRows objectAtIndex:x];
if ([indexPath compare:elem] == NSOrderedSame) { //If the current cell index path ='s any index path in the array of checked cells, check this cell.
cell.accessoryType = UITableViewCellAccessoryCheckmark;
break;
}
}

iOS - NSRangeException only when breakpoints are disabled

Recently started developing apps, so excuse my ignorance. I have a tableView, and when a cell in the table view is clicked, I want to insert a new row directly below it. This currently works in my code. However, I also want the row that has been inserted to be removed once the cell has been clicked again. This is giving me the NSRangeException saying I am out of bounds in my array.
I figured this probably has to do with my tableView delegate/data methods, so I set up break points at each of them. With the break points enabled, the cell is removed perfectly. However, when I disable the breakpoints, and let the application run on its own, it crashes. How could break points possibly be affecting this?
Here is the relevant code:
- (NSInteger) numberOfSectionsInTableView:(UITableView *)songTableView{
return 1;
}
- (NSInteger)tableView:(UITableView *)songTableView
numberOfRowsInSection:(NSInteger)section{
bool debug = false;
if (debug) NSLog(#"--TableView: rankings");
if (expandedRow == -1)
return [self.songs count];
else //one row is expanded, so there is +1
return ([self.songs count]+1);
}
- (UITableViewCell *)tableView:(UITableView *)songTableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath{
bool debug = false;
if (debug) NSLog(#"--tableView: tableView");
NSUInteger row = [indexPath row];
if (row == expandedRow){ //the expanded row, return the custom cell
UITableViewCell *temp = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"test"];
return temp;
}
UITableViewCell *cell = [tableViewCells objectAtIndex:row];
return cell;
}
}
- (NSString *)tableView:(UITableView *)songTableView
titleForHeaderInSection:(NSInteger)section{
//todo: call refresh title
return #"The Fresh List";
}
- (CGFloat)tableView:(UITableView *)songTableView
heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return 44.0; //same as SongCell.xib
}
- (void)tableView: (UITableView *)songTableView
didSelectRowAtIndexPath: (NSIndexPath *)indexPath {
bool debug = true;
//todo: if the user selects expanded cell, doesn't do anything
SongCell *cell = (SongCell *)[songTableView cellForRowAtIndexPath:indexPath];
if (cell->expanded == NO){
//change cell image
cell.bgImage.image = [UIImage imageNamed:#"tablecellbg_click.png"];
cell->expanded = YES;
//add new cell below
NSInteger atRow = [indexPath row] + 1;
NSIndexPath *insertAt = [NSIndexPath indexPathForRow:atRow inSection:0];
NSArray *rowArray = [[NSArray alloc] initWithObjects:insertAt, nil];
if (debug) NSLog(#"Expanded row: %d", atRow);
expandedRow = atRow;
[tableView insertRowsAtIndexPaths:rowArray withRowAnimation:UITableViewRowAnimationTop];
}else { //cell is already open, so close it
//change cell image
cell.bgImage.image = [UIImage imageNamed:#"tablecellbg.png"];
cell->expanded = NO;
NSIndexPath *removeAt = [NSIndexPath indexPathForRow:expandedRow inSection:0];
NSArray *rowArray = [[NSArray alloc] initWithObjects:removeAt, nil];
if(debug) NSLog(#"--about to delete row: %d", expandedRow);
expandedRow = -1;
[tableView deleteRowsAtIndexPaths:rowArray withRowAnimation:UITableViewRowAnimationTop];
//remove expaned cell below
}
}
This is just a guess, but it's a good idea to wrap code that changes the table structure in calls to
[tableView beginUpdates];
[tableView endUpdates];
I bet this returns null: [NSIndexPath indexPathForRow:expandedRow inSection:0]; and if it does it blows ...
hth
I hate to answer my own questions but I figured it out.
I was loading up my tableView from an array of objects. When I added the cell, that array still only held 30 objects, whereas my table held 31. I was correctly returning the numberOfRowsInSection method.
Here is the modification I made. Notice the extra else if:
NSUInteger row = [indexPath row];
if (row == expandedRow){ //the expanded row, return the custom cell
if(debug) NSLog(#"row == expandedRow");
UITableViewCell *temp = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"test"];
return temp;
}
else if (expandedRow != -1 && row > expandedRow)
row--;
My array of objects and the UITableViewCells were suppose to match up 1-1. After the expanded row, indexPath's row because off by 1. Here is my quick fix to this problem, although I'm sure there is a better way to solve this.

Objective C: Cell text disappears after scrolling out of screen (and back)

I am creating a controller with a UITableview which contains 2 sections. In the second section, I have a list of users which I want to add a checkmark to the user in the list when I click on the particular row. I have an issue whereby after I click on a user and scroll the cell out of the page, the cell returns with the 'name' field BLANK (see screenshot below). I know this has to do with the way the cells are reused but am not able to get my head around the exact issue. My code is posted below. Any advise?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString* ShareOnCellIdentifier = #"ShareOnholderCell";
static NSString* WhoShouldAnswerCellIdentifier = #"WhoShouldAnswerCell";
int row = [indexPath row];
User *user = [self.friendsToShareWithArray objectAtIndex:row];
if (indexPath.section == 0)
{
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:ShareOnCellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ShareOnCellIdentifier];
}
cell.textLabel.text = [self.shareOnArray objectAtIndex:indexPath.row];
return cell;
}
else
{
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:WhoShouldAnswerCellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:WhoShouldAnswerCellIdentifier];
}
cell.textLabel.text = user.nickname;
if (user.isSelected)
{
cell.selectionStyle = UITableViewCellAccessoryCheckmark;
}
else
{
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
return cell;
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
User *user = [self.friendsToShareWithArray objectAtIndex:indexPath.row];
if (indexPath.section == 1)
{
UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath];
if (cell.accessoryType == UITableViewCellAccessoryCheckmark)
{
cell.accessoryType = UITableViewCellAccessoryNone;
user.isSelected = NO;
}
else
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
user.isSelected = YES;
}
}
}
EDIT: Example of how I create my user object
User *user1 = [[User alloc]init];
user1.nickname = #"Dionis";
user1.uid = #"1";
user1.isSelected = NO;
The only problem I see is in the way you've implemented didSelectRowAtIndexPath: method.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 1)
{
User *user = [self.friendsToShareWithArray objectAtIndex:indexPath.row];
user.isSelected = !user.isSelected;
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationNone];
}
}
I've had this kind of issue in the past caused by not realizing that basically every time a cell comes into view in a scrollable UITableView, it'll be re-requested via cellForRowAtIndexPath. I can't see an error being made here (you're always setting the label's text), but at this point I would suggest checking the text of the label after you set it.:
if (!cell.textLabel.text || [[cell.textLabel.text stringByReplacingOccurrencesOfString:#" " withString:#""] isEqualToString:#""])
NSAssert(NO, #"Bad text!");
Running in debug, this will "crash" at the NSAssert if the text is blank or nil. Then you might be able to inspect the surrounding variables (could user be nil, or something like that?) and find out more.