How to tell which rows toggle switch was changed - iphone

I have a tableview with the accessoryview of a toggle switch. I specify the section and the row and am having a difficult time determining which row was toggled. I used the toggleSwitch.tag to grab the indexRow but as my indexRow is part of an indexPath.section I am not sure how to tell which row I toggled.
Here is the code:
- (UITableViewCell *)tableAlert:(SBTableAlert *)tableAlert cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell;
Category *cat = [allCategories objectAtIndex:indexPath.section];
Subject *sub = [cat.subjects objectAtIndex:indexPath.row];
cell = [[[SBTableAlertCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil] autorelease];
UISwitch *toggleSwitch = [[UISwitch alloc] init];
cell.accessoryView = [[UIView alloc] initWithFrame:toggleSwitch.frame];
[cell.accessoryView addSubview:toggleSwitch];
cell.textLabel.text =sub.title;
cell.detailTextLabel.text = sub.category_title;
if (sub.active==1){
[toggleSwitch setOn:YES];
} else {
[toggleSwitch setOn:NO];
}
toggleSwitch.tag = indexPath.row;
[toggleSwitch addTarget:self action:#selector(viewButtonPushed:) forControlEvents:UIControlEventValueChanged];
[toggleSwitch release];
return cell;
}
- (void)viewButtonPushed:(id)sender {
UIButton *button = (UIButton *)sender;
UITableViewCell *cell = button.superview; // adjust according to your UITableViewCell-subclass' view hierarchy
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
Category *cat = [allCategories objectAtIndex:indexPath.section];
Subject *sub = [cat.subjects objectAtIndex:indexPath.row];
selectedSubject = sub;
UISwitch* switchControl = sender;
NSLog( #"The switch is %#", switchControl.on ? #"ON" : #"OFF" );
if(switchControl.on){
[sub setActive:1];
NSLog(#"%# is being set to ACTIVE", selectedSubject.title);
}else{
[sub setActive:0];
NSLog(#"%# is being set to INACTIVE", selectedSubject.title);
}
[sub setIsDirty:YES];
[cat.subjects replaceObjectAtIndex:indexPath.row withObject:sub];
[sub autorelease];
[cat autorelease];
}
Here is my didSelectRowAtIndexPath. Do I need to have any reference to the toggleSwitch here?
- (void)tableAlert:(SBTableAlert *)tableAlert didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
Category *cat = [allCategories objectAtIndex:indexPath.section];
Subject *sub = [cat.subjects objectAtIndex:indexPath.row];
selectedSubject = sub;
NSLog(#"selectedSubject = %#", selectedSubject.title);
if (tableAlert.type == SBTableAlertTypeMultipleSelct) {
UITableViewCell *cell = [tableAlert.tableView cellForRowAtIndexPath:indexPath];
if (cell.accessoryType == UITableViewCellAccessoryNone)
[cell setAccessoryType:UITableViewCellAccessoryCheckmark];
else
[cell setAccessoryType:UITableViewCellAccessoryNone];
[tableAlert.tableView deselectRowAtIndexPath:indexPath animated:YES];
}
}

I have found that you need to go to the superview of the superview of the item in the cell (assuming that the button or control is right off the root of the cell) in order to get the pointer to the cell.
Try this instead:
UITableViewCell *cell = button.superview.superview;
and see if the results are any better. Check out my blog post on this for more information:
Two superviews are better than one

Related

cell.textLabel not getting resized

I'm trying to create a Settings for our app. I'm not sure what is happening here. I have a UITableViewSyleGrouped and in each section of the table, there is 1 row. For my particular row, it shows the person's name. If you click on it, then it pushes to a new tableView that has the list of people to choose from, then when you pop back, the label gets updated, but the label is truncated when I go from a smaller name to a bigger name. I'm trying to create a Settings for our app. Some of the fields look like:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSUInteger row = [indexPath row];
if (tableView == _settingsTableView) {
UITableViewCell *cell = nil;
NSNumber *aSection = [_tableArray objectAtIndex:indexPath.section];
if ([aSection integerValue] == SOUNDS)
{
cell = [tableView dequeueReusableCellWithIdentifier:#"SwitchCell"];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"SwitchCell"] autorelease];
}
cell.textLabel.text = #"Sounds";
cell.selectionStyle = UITableViewCellSelectionStyleNone;
UISwitch *switchView = [[UISwitch alloc] initWithFrame:CGRectZero];
cell.accessoryView = switchView;
[switchView setOn:[[Settings sharedInstance] playSounds] animated:NO]; // initialize value from Settings
[switchView addTarget:self action:#selector(switchChanged:) forControlEvents:UIControlEventValueChanged];
[switchView release];
}
else if ([aSection integerValue] == PERSON) {
cell = [tableView dequeueReusableCellWithIdentifier:#"PersonCell"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:#"PersonCell"] autorelease];
}
Person *p = [_personArray objectAtIndex:row];
cell.textLabel.text = [NSString stringWithFormat:#"%# %#", p.firstName, p.lastName];
NSLog(#"cL: %#", NSStringFromCGRect(cell.textLabel.frame));
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
return cell;
}
My PERSON section gives the user the ability to change People. That code in didSelectRowAtIndexPath is
else {
Person *p = [_personArray objectAtIndex:row];
NSUInteger oldRow = [_lastIndexPath row];
if (oldRow != row) {
dmgr.currentPerson = p;
// Put checkmark on newly selected cell
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.accessoryType = UITableViewCellAccessoryCheckmark;
// Remove checkmark on old cell
UITableViewCell *oldCell = [tableView cellForRowAtIndexPath:_lastIndexPath];
oldCell.accessoryType = UITableViewCellAccessoryNone;
[_settingsTableView deselectRowAtIndexPath:_lastIndexPath animated:YES];
self.LastIndexPath = indexPath;
// Update the cell
NSIndexPath *path = [NSIndexPath indexPathForRow:0 PERSON];
UITableViewCell *theCell = [_settingsTableView path];
theCell.textLabel.text = [NSString stringWithFormat:#"%# %#", p.firstName, p.lastName];
[theCell setNeedsDisplay];
NSLog(#"ceLL: %#", NSStringFromCGRect(theCell.textLabel.frame));
}
}
What happens is the label is truncated until I click on the label. (e.g. John D... instead of John Doe). Why does the label not get updated?
I tried looking at the frames, and I'm not sure if that has something to do with it or not. My output is:
cL: {{0, 0}, {0, 0}}
ceLL: {{10, 11}, {76, 21}}
The textLabel field of a UITableViewCell is a regular UILabel. You can set this property to cause it to scale down the text to fit:
theCell.textLabel.adjustsFontSizeToFitWidth = YES;
You can also set a minimum font size
theCell.textLabel.minimumFontSize = whatever
Take a look at the Documentation on UILabel it will help you a lot.

Multiple Check Box in UITableview iPhone

I am new in iOS
I am creating dynamically buttons in my tableview
i had set a image on button for check and uncheck
What i have to do is when i tap(or check) on button the data on indexpath row will add in my array.
And if i deselect it , data removes from my array. Please Help me
-(void)btnForCheckBoxClicked:(id)sender
{
UIButton *tappedButton = (UIButton*)sender;
indexForCheckBox= [sender tag];
if([tappedButton.currentImage isEqual:[UIImage imageNamed:#"checkbox_unchecked.png"]])
{
[sender setImage:[UIImage imageNamed: #"checkbox_checked.png"] forState:UIControlStateNormal];
strinForCheckBox= [ApplicationDelegate.ArrayForSearchResults objectAtIndex:indexForCheckBox];
[arrForCheckBox addObject:strinForCheckBox];
NSLog(#"Sender Tag When Add %d", indexForCheckBox);
NSLog(#"Array Count Check Box %d",[arrForCheckBox count]);
}
else
{
[sender setImage:[UIImage imageNamed:#"checkbox_unchecked.png"]forState:UIControlStateNormal];
[arrForCheckBox removeObjectAtIndex:indexForCheckBox];
NSLog(#"Sender Tag After Remove %d", indexForCheckBox);
NSLog(#"Array Count Uncheck Box %d",[arrForCheckBox count]);
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
if ([selectedRowsArray containsObject:[contentArray objectAtIndex:indexPath.row]) {
cell.imageView.image = [UIImage imageNamed:#"checked.png"];
}
else {
cell.imageView.image = [UIImage imageNamed:#"unchecked.png"];
}
UITapGestureRecogniser *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleChecking:)
[cell.imageView addGestureRecognizer:tap];
[tap release];
cell.textLabel.text = [contentArray objectAtIndex];
return cell;
}
- (void) handleChecking:(UITapGestureRecognizer *)tapRecognizer {
CGPoint tapLocation = [tapRecognizer locationInView:self.tableView];
NSIndexPath *tappedIndexPath = [self.tableView indexPathForRowAtPoint:tapLocation];
if (selectedRowsArray containsObject:[contentArray objectAtIndex:tappedIndexPath.row]) {
[selectedRowsArray removeObject:[contentArray objectAtIndex:tappedIndexPath.row]];
}
else {
[selectedRowsArray addObject:[contentArray objectAtIndex:tappedIndexPath.row]];
}
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:tappedIndexPath] withRowAnimation: UITableViewRowAnimationFade];
}
I think this question has been answered some time ago! Check it out first and tell us if this is what you are looking for ?
How to add checkboxes to UITableViewCell??
image http://img208.yfrog.com/img208/6119/screenshotkmr.png
You can add this on your button click method.
First set Bool value which set yes on one click and No In second click and when you first click add that index value in array using array addobject property and other click removeobject property.
-(void)list
{
if(isCheck==YES)
{
//add array object here
isCheck=NO;
}
else if(isCheck==NO)
{
//remove array object here
isCheck=YES;
}
}
I have found another way. Code extracted from #The Saad And, i had just modified with key/value pairs as useful one like below code -
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
if ([[selectedRowsArray objectAtIndex:indexPath.row] containsObject:#"YES"])
cell.imageView.image = [UIImage imageNamed:#"checked.png"];
else
cell.imageView.image = [UIImage imageNamed:#"unchecked.png"];
UITapGestureRecogniser *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleChecking:)
[cell.imageView addGestureRecognizer:tap];
[tap release];
cell.textLabel.text = [contentArray objectAtIndex];
return cell;
}
- (void) handleChecking:(UITapGestureRecognizer *)tapRecognizer {
CGPoint tapLocation = [tapRecognizer locationInView:self.tableView];
NSIndexPath *tappedIndexPath = [self.tableView indexPathForRowAtPoint:tapLocation];
if ([[selectedRowsArray objectAtIndex:tappedIndexPath.row] containsObject:#"YES"])
[selectedRowsArray replaceObjectAtIndex:tappedIndexPath.row withObject:[NSSet setWithObject:#"NO"]];
else
[selectedRowsArray replaceObjectAtIndex:tappedIndexPath.row withObject:[NSSet setWithObject:#"YES"]];
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:tappedIndexPath] withRowAnimation: UITableViewRowAnimationFade];
}
Add, the objects for selectedRowsArray as below with the count of your self.tableView's row count. So, you need to add the bool values as below
for ( int i = 0; i < tableViewRowCount; i++ ) { // tableViewRowCount would be your tableView's row count
....
....
....
[selectedRowsArray addObject:[NSSet setWithObject:#"NO"]];
....
....
}
Hope this helps! Cheers!
Update
With the use of updated code you don't need to keep key/value pair of NSMutableDictionary for overkilling that. NSSet provides you better way with this.

Objective-C: How to retain inputs and also tag names when UItextFields scrolled off?

My App is placing questions and according to the question, placing either UITextField or UISwitch.
When a user input texts it automatically detects which textField and placing the texts accordingly.
It works well but when the items are scrolled off, it removes the user inputs and tag names as well, and when displayes the area placing a new items on top of that.
So when a user input texts it stores it into the old textField.
I would like to know how to prevent it from this issue.
Is there any suggestion? Thanks in advance.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
NSLog(#"-------------cellForRowAtIndexPath---------------");
cell_id = [qid objectAtIndex:[indexPath row] ];
static NSString *CellIdentifier = #"Cell";
label = nil;
cell = (UITableViewCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
[self configureLabel];
[[cell contentView] addSubview:label];
}
dict = [qtext objectAtIndex:[indexPath row]];
celltext = [NSString stringWithFormat:#"%#\n\n\n",[[dict allKeys] objectAtIndex:0]];
dict = [qtype objectAtIndex:[indexPath row]];
type = [[dict allKeys] objectAtIndex:0];
//place the question
cell.textLabel.text = celltext;
NSLog(#"celltext=%#",celltext);
if([type isEqualToString:#"devider"]){
[self configureDevider];
}else{
[self configureCell];
}
if([cell_id intValue] == ([qid count])){
tabledone = #"Yes";
}
tableView.backgroundColor=[UIColor clearColor];
tableView.opaque=NO;
tableView.backgroundView=nil;
NSString *a = [arrAllheight objectAtIndex:[indexPath row]];
allheight +=thisheight;
thisheight =[a intValue];
if([type isEqualToString:#"YN"]){
DCRoundSwitch *ynSwitch = [[DCRoundSwitch alloc] initWithFrame:CGRectMake(220,thisheight-40,80,27)] ;
ynSwitch.onText=#"Yes";
ynSwitch.offText=#"No";
[answers addObject:ynSwitch];
[cell addSubview:ynSwitch];
[ynSwitch setTag:[cell_id intValue]];
[ynSwitch addTarget:self action:#selector(setAnswersForRoundSwitches:) forControlEvents:UIControlEventValueChanged];
i++;
}else if([type isEqualToString:#"freetext"]){
//When the done button was clicked, remove the keybords from the screen
[self makeTextField];
[rtxtfield addTarget:self action:#selector(setAnswersfortextFields:) forControlEvents:UIControlEventEditingDidEndOnExit];
// [rtxtfield value];
}else if([type isEqualToString:#"dropdown"]){
picc = [picker_array objectForKey:[[NSString alloc] initWithFormat:#"%d",cell_id]];
//Choose an array for this textField
// [self getPickerArray];
[self makeTextField];
//[rtxtfield addTarget:self action:#selector(setAnswersfortextFields:) forControlEvents:UIControlEventEditingDidEndOnExit];
//When the done button was clicked, remove the keybords from the screen
[rtxtfield addTarget:self action:#selector(textFieldReturn:) forControlEvents:UIControlEventEditingDidEndOnExit];
//Get the tag for picker
[rtxtfield addTarget:self action:#selector(getTag:) forControlEvents:UIControlEventTouchDown];
//Display picker
[rtxtfield addTarget:self action:#selector(acsheet:) forControlEvents:UIControlEventTouchDown];
//set Tag for the textField
[rtxtfield setTag:[cell_id intValue]];
NSLog(#"rtxtfield tag=%d",rtxtfield.tag);
}
if([type isEqualToString:#"devider"]){
[self caliculateHeightofCell];
}else{
[self caliculateHeightofCell];
}
return cell;
}
Save the state of your controls in your data model as soon as they change. So, maybe your model is an array of questions, and each question has an instance variable that can hold the answer. Your view controller is probably both the table data source and table delegate, and you should make it the target of any controls in the cells, too. That is, when you set up a new cell in your -tableView:cellForRowAtIndexPath:, make the view controller the target of the UITextField or UISwitch in the cell. When the user changes either of those controls, then, the change will trigger an action in the view controller, and the view controller can retrieve the new value of the control and store it in the corresponding question in the data model.
If you take this approach, you never have to worry about questions scrolling out of view. As soon as the question scrolls back into view, -tableView:cellForRowAtIndexPath: will be called for that row again, and you'll have all the information you need to reconstitute that cell.
Save the text to the dataSource. UITableViewCells must not contain any state information.
Implement something similar to this:
- (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];
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(2, 2, 200, 40)];
textField.tag = 999;
textField.delegate = self;
textField.placeholder = #"Enter text here";
[cell.contentView addSubview:textField];
}
UITextField *textField = (UITextField *)[cell.contentView viewWithTag:999];
textField.text = [self.dataSource objectAtIndex:indexPath.row];
/* configure cell */
return cell;
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
UIView *contentView = [textField superview];
UITableViewCell *cell = (UITableViewCell *)[contentView superview];
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
[self.dataSource replaceObjectAtIndex:indexPath.row withObject:textField.text];
}
Do not add views outside of if (cell == nil)!
If you have different type of cells use a different CellIdentifier! Like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *SwitchCellIdentifier = #"SwitchCell";
static NSString *TextFieldCellIdentifier = #"TextFieldCell";
UITableViewCell *cell = nil;
if (/* current cell is a text field cell */) {
cell = [tableView dequeueReusableCellWithIdentifier:TextFieldCellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TextFieldCellIdentifier];
// add textField
}
// configure cell...
}
else if (/* current cell is a switch cell */) {
cell = [tableView dequeueReusableCellWithIdentifier:SwitchCellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:SwitchCellIdentifier];
// add switch
}
// configure cell...
}
return cell;
}
I took a day to fix the problem, but I've finally got it right:
Sorry it's a long code cause it's using textField, DCRoundSwitch, and pickerView.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"-----------------cellForRowAtIndexPath---------------");
Questions *q = [qtext objectAtIndex:indexPath.row];
NSLog(#"question=%#",q.question);
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
static NSString *SwitchCellIdentifier = #"SwitchCell";
static NSString *TextFieldCellIdentifier = #"TextFieldCell";
static NSString *DropDownFieldCellIdentifier = #"DropDownCell";
cell = nil;
cell = [tableView dequeueReusableCellWithIdentifier:TextFieldCellIdentifier];
if ([q.question_type isEqualToString:#"freetext"]) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TextFieldCellIdentifier];
if (cell == nil) {
[self makeTextField];
[cell.contentView addSubview:rtxtfield];
cell.textLabel.text = q.question;
}
[self makeTextField];
[self configureCell];
[self configureLabel];
cell.textLabel.text = q.question;
[cell.contentView addSubview:rtxtfield];
rtxtfield.text = [appDelegate.dataSource objectAtIndex:indexPath.row];
rtxtfield.tag=[indexPath row];
}else if([q.question_type isEqualToString:#"YN"]){
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:SwitchCellIdentifier];
if (cell == nil) {
[self makeDCRoundSwitch];
[ynSwitch setTag:[indexPath row]];
cell.textLabel.text = q.question;
[self configureLabel];
[self configureCell];
}
[self makeDCRoundSwitch];
[ynSwitch setTag:[indexPath row]];
cell.textLabel.text = q.question;
[self configureLabel];
[self configureCell];
[ynSwitch addTarget:self action:#selector(ynSwitchDidEndEditing:) forControlEvents:UIControlEventValueChanged];
if([[appDelegate.dataSource objectAtIndex:indexPath.row] isEqualToString:#"Yes"]){
[ynSwitch setOn:YES animated:YES];
} else{
[ynSwitch setOn:NO animated:YES];
}
rtxtfield.tag=[indexPath row];
} else if([q.question_type isEqualToString:#"dropdown"]){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:DropDownFieldCellIdentifier];
if (cell == nil) {
[self makeTextField];
[cell.contentView addSubview:rtxtfield];
cell.textLabel.text = q.question;
}
[self makeTextField];
[self configureCell];
[self configureLabel];
cell.textLabel.text = q.question;
[cell.contentView addSubview:rtxtfield];
picc=q.question_array;
//When the done button was clicked, remove the keybords from the screen
[rtxtfield addTarget:self action:#selector(textFieldReturn:) forControlEvents:UIControlEventEditingDidEndOnExit];
//Get the tag for picker
[rtxtfield addTarget:self action:#selector(getTag:) forControlEvents:UIControlEventTouchDown];
//Display picker
[rtxtfield addTarget:self action:#selector(acsheet:) forControlEvents:UIControlEventTouchDown];
//set Tag for the textField
[rtxtfield setTag:[indexPath row]];
// AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
rtxtfield.text = [appDelegate.dataSource objectAtIndex:indexPath.row];
rtxtfield.tag=[indexPath row];
}
[self makeTextField];
rtxtfield.tag=[indexPath row];
tableView.backgroundColor=[UIColor clearColor];
tableView.opaque=NO;
tableView.backgroundView=nil;
return cell;
}
try this
label = nil;
//cell = (UITableViewCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
//if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
[self configureLabel];
[[cell contentView] addSubview:label];
//}
comment your code like that and tell me what happens

Iphone: Checkmarks in UITableview get mixed up when scrolling

I have a bit of a problem where my check marks that i apply to my rows in my UITableView get all mixed up when i scroll. Im pretty sure this has to do with how the iphone reuses the cells and when i scroll away from on that has a check mark it probably puts it back in when i gets a chance.
Could someone please give me some tips on how I might avoid this or possibly take a look at my methods and see if anything looks off?
I was thinking that maybe I could save each row selection that the user made and then check to see which rows were being displayed to make sure the correct ones got the checkmark but I could'nt see a way to do so.
Thanks so much.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
CustomCell *cell = (CustomCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[CustomCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
[cell setAccessoryView:nil];
}
NSMutableArray *temp = [[NSMutableArray alloc]init];
for (int j = 0; j < [listOfRowersAtPractice count]; j++) {
if ([[differentTeams objectAtIndex:indexPath.section] isEqualToString:[[rowersAndInfo objectForKey:[listOfRowersAtPractice objectAtIndex:j]]objectForKey:#"Team"]]) {
[temp addObject:[listOfRowersAtPractice objectAtIndex:j]];
}
}
[cell.cellText setText:[temp objectAtIndex:indexPath.row]];
[temp removeAllObjects];
[temp release];
// Set up the cell...
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (cell.accessoryType != UITableViewCellAccessoryCheckmark) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
}
Yes save the state of the row which is selected and in cellforrowatindexpath after you get the cell reset it to default state and check the state of the row and change the state.
EDIT:
You can create a NSMutabaleArray with number of items equal to the number of items in your datasource which is the name temp in your code.
On select you can actually change the value at that index to some text like #"selected" in the above created array.
In your cellforrowatindexpath you can check this text if its selected or unselected and then change the property of the cell. Its like maintaining a bitmap state for selected and unselected states.
Give this a go :
static NSString *CellIdentifier = [NSString stringWithFormat:#"Cell %d",indexPath.row];
I had the same problem on one of my app's.
As for the check marks, are you using a core data store at all?
If you are use the following....
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
NSManagedObject *item = [[self fetchedResultsController] objectAtIndexPath:indexPath];
if ([[item valueForKey:#"checks"] boolValue]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
[cell.textLabel setTextColor:[UIColor redColor]];
[cell.detailTextLabel setTextColor:[UIColor redColor]];
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
[cell.textLabel setTextColor:[UIColor blackColor]];
[cell.detailTextLabel setTextColor:[UIColor blackColor]];
}
}
And......
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSManagedObject *selectedObject = [self.fetchedResultsController objectAtIndexPath:indexPath];
if ([[selectedObject valueForKey:#"checks"] boolValue]) {
[selectedObject setValue:[NSNumber numberWithBool:NO] forKey:#"checks"];
} else {
[selectedObject setValue:[NSNumber numberWithBool:YES] forKey:#"checks"];
}
[managedObjectContext save:nil];
}
You need to reset/clear all settings in the cell whenever you reuse the cell.
So here, right after you get the cell,
you need to do something like
CustomCell *cell = (CustomCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[CustomCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
[cell setAccessoryView:nil];
}
cell.accessoryType = UITableViewCellAccessoryNone // This and other such calls to clean up the cell
You need refresh the accessoryType of cell, because the cell is reused then it inherited the accessoryType from a reused Cell, this is the solution:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"cellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
//Refresh acessory for cell when tableview have many cells and reuse identifier
if([self.tableView.indexPathsForSelectedRows containsObject:indexPath]){
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}else{
cell.accessoryType = UITableViewCellAccessoryNone;
}
cell.textLabel.text = #"Your text cell";
return cell;
}
it worked for me..
in cell for row at index path i had created a checkbox button..
after everytym tableview is scrolled cellForRowAtIndexPath Method gets called
hence i had to add condition in cellForRowAtIndexPath to check whether a cell has a checked or unchecked button
static NSString *simpleTableIdentifier = #"SimpleTableCell";
SimpleTableCell *cell = (SimpleTableCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"SimpleTableCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
cell.nameLabel.text = [tableData objectAtIndex:indexPath.row];
cell.thumbnailImageView.image = [UIImage imageNamed:[thumbnails objectAtIndex:indexPath.row]];
cell.prepTimeLabel.text = [prepTime objectAtIndex:indexPath.row];
checkbox = [[UIButton alloc]initWithFrame:CGRectMake(290, 5, 20, 20)];
[checkbox setBackgroundImage:[UIImage imageNamed:#"checkbox_empty.png"]
forState:UIControlStateNormal];
[checkbox addTarget:self action:#selector(checkUncheck:) forControlEvents:UIControlEventTouchUpInside];
[cell addSubview:checkbox];
if(selectedRows.count !=0)
{
if([[selectedRows objectAtIndex:indexPath.row]integerValue]==1)
{
[checkbox setImage:[UIImage imageNamed: #"checkbox_full.png"] forState:UIControlStateNormal];
}
else
{
[checkbox setImage:[UIImage imageNamed: #"checkbox_empty.png"] forState:UIControlStateNormal];
}
}
return cell;
}
method to define selection of checkbox is as
- (IBAction)checkUncheck:(id)sender {
UIButton *tappedButton = (UIButton*)sender;
NSLog(#"%d",tappedButton.tag);
if ([[sender superview] isKindOfClass:[UITableViewCell class]]) {
UITableViewCell *containerCell = (UITableViewCell *)[sender superview];
NSIndexPath *cellIndexPath = [self.tableView indexPathForCell:containerCell];
int cellIndex = cellIndexPath.row;
NSLog(#"cell index%d",cellIndex);
[selectedRows insertObject:[NSNumber numberWithInt:1] atIndex:cellIndex];
}
NSLog(#"%#",selectedRows);
if([tappedButton.currentImage isEqual:[UIImage imageNamed:#"checkbox_empty.png"]])
{
[sender setImage:[UIImage imageNamed: #"checkbox_full.png"] forState:UIControlStateNormal];
}
else
{
[sender setImage:[UIImage imageNamed: #"checkbox_empty.png"] forState:UIControlStateNormal];
}
}
do not forget to initialize selectedRows array..
happy coding...!!!

Only one UITableViewCellAccessoryCheckmark allowed at a time

I have a tableview with a section that contains a list of sounds the user can "pick." Only the currently selected sound should show a UITableViewCellAccessoryCheckmark. Basically I got it working. However, when e.g. the bottom cell is checked, and I scroll up the table view (making the checked cell go out of view), and I check another cell, the check for the bottom (out of view) cell is not removed. Does anyone know why this is?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
static NSString *TheOtherOne = #"OtherCell";
static NSString *MiddleCell = #"MiddleCell";
// Configure the cell...
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSString *theSound = [prefs objectForKey:#"pickedFailSound"];
NSUInteger index = [failSounds indexOfObject:theSound];
if (indexPath.section == 0){
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
failAtLaunch = [[[UISwitch alloc] initWithFrame:CGRectMake(200, 7, 100, 30)] autorelease];
[cell addSubview:failAtLaunch];
cell.accessoryView = failAtLaunch;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
if(![prefs boolForKey:#"failAtLaunch"]) {
[failAtLaunch setOn:NO animated:NO];
} else {
[failAtLaunch setOn:YES animated:NO];
}
[(UISwitch *)cell.accessoryView addTarget:self action:#selector(setIt) forControlEvents:UIControlEventValueChanged];
cell.textLabel.text = #"Instafail";
cell.detailTextLabel.text = #"Fail at Laucnh";
return cell;
} else if (indexPath.section == 1) {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MiddleCell];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MiddleCell] autorelease];
}
NSString *cellTitle = [failSounds objectAtIndex:indexPath.row];
cell.textLabel.text = cellTitle;
if (indexPath.row == index) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
if ([cellTitle isEqualToString:#"Your Fail Sound"]){
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
if(![prefs boolForKey:#"customSoundAvailable"]) {
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.userInteractionEnabled = NO;
cell.textLabel.textColor = [UIColor grayColor];
cell.detailTextLabel.text = #"Record a Custom Failsound First";
}
}
return cell;
} else {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:TheOtherOne];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:TheOtherOne] autorelease];
}
cell.textLabel.text = #"Hold to record fail sound";
UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
btn.frame = CGRectMake(230.0f, 4.0f, 60.0f, 36.0f);
[btn setTitle:#"OK" forState:UIControlStateNormal];
[btn setTitle:#"OK" forState:UIControlStateSelected];
[btn addTarget:self action:#selector(recordAudio) forControlEvents:UIControlEventTouchDown];
[btn addTarget:self action:#selector(stop) forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:btn];
return cell;
}
}
And:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSString *theSound = [prefs objectForKey:#"pickedFailSound"];
NSUInteger index = [failSounds indexOfObject:theSound];
//NSLog(#"The index is: %#", index);
if (indexPath.section == 1){
NSIndexPath *oldIndexPath = [NSIndexPath indexPathForRow:index inSection:1];
NSLog(#"Oldindexpath is: %#", oldIndexPath);
UITableViewCell *newCell = [tableView cellForRowAtIndexPath:indexPath];
if (newCell.accessoryType == UITableViewCellAccessoryNone) {
newCell.accessoryType = UITableViewCellAccessoryCheckmark;
}
UITableViewCell *oldCell = [tableView cellForRowAtIndexPath:oldIndexPath];
if (oldCell != newCell){
if (oldCell.accessoryType == UITableViewCellAccessoryCheckmark) {
oldCell.accessoryType = UITableViewCellAccessoryNone;
}
}
NSString *pickedSound = [failSounds objectAtIndex:indexPath.row];
NSLog(#"Picked sound is %#", pickedSound);
[prefs setObject:pickedSound forKey:#"pickedFailSound"];
[prefs synchronize];
[self performSelector:#selector(loadSound) withObject:nil];
[failSound play];
}
}
You aren't remembering to reset the accessory type for dequeued cells.
This should fix it for you;
cell.accessoryType = UITableViewCellAccessoryNone;
if (indexPath.row == index) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
Handy tip : You may find it useful to use switch(indexPath.section) rather than lots of if ... elseif ... else constructs. Although you're OK now with just 2 sections, I find that using switch makes it easier to scale up the number of sections and also makes it mentally easier to read throught the code since you often have if .. else constructs used for other things - having the different syntax helps make it clearer what the tests are for.
In - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPathafter each declaration of cell try reseting the cells accessoryType by adding following line: cell.accessoryType = UITableViewCellAccessoryNone;