Fast-enumeration logic error checking for compare of UITableViewCell - objective-c

I have a mutable array (editedServiceParts) with two objects in it; I have a table view with about 30 cells in it. I want to check to see if any of table view cells (cell.textLabel.text) appear in the mutable array; if they do, I want to set the cell.accessory to checkmark.
UPDATED This is my code from -cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// get the timeFormat
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *preferencesDict = [[userDefaults dictionaryForKey:#"preferencesDictionary"] mutableCopy];
int iTimeFormat = [[preferencesDict objectForKey:#"timeFormat"] intValue]; // set timeFormat
NSMutableArray *selectedServicesParts = [NSMutableArray new];
NSMutableArray *editedServiceParts = [NSMutableArray new];
NSString *service;
if(tableView.tag == kServicesTableView) { // services array
static NSString *CellIdentifier = #"apptServicesCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell
SingletonServicesArray *sharedServicesArray = [SingletonServicesArray sharedServicesArray]; // list of available services
[cell.textLabel setText:[sharedServicesArray.globalServicesArray objectAtIndex:indexPath.row]]; // move selected row to cell
// separate soServices.text into individual elements
NSString *cleanService;
if(soServices.text.length > 0) {
selectedServicesParts = [[soServices.text componentsSeparatedByString:#","] mutableCopy];
for(int x = 0; x < selectedServicesParts.count; x++) {
cleanService = [selectedServicesParts[x] stringByReplacingOccurrencesOfString:#"," withString:#""];
[editedServiceParts insertObject: cleanService atIndex: x];
}
}
// now, take the editedServicesParts (w/o commas) and mark cell if appropriate
for (int i = 0; i < editedServiceParts.count; i++) { if ([editedServiceParts containsObject:cell.textLabel.text]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
// Remove accessory mark of recycled cells
cell.accessoryType = UITableViewCellAccessoryNone;
}
}
// for (int i = 0; i < editedServiceParts.count; i++) { // total number of rows in list of all available services
//
// for(service in editedServiceParts) {
// if([cell.textLabel.text isEqualToString: service]) {
// cell.accessoryType = UITableViewCellAccessoryCheckmark;
// break;
}
// }
// }
return cell;
}
What's happening is only the first comparison is being found, although there is another that should have been found. I fear I have a logic problem here, and for the life of me, I don't see it. Help would be greatly appreciated!
SD

You can use containsObject: method to avoid looping altogether:
if ([editedServiceParts containsObject:cell.textLabel.text]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
// Remove accessory mark of recycled cells
cell.accessoryType = UITableViewCellAccessoryNone;
}

Related

Unable to add UITextViewCell's accessoryType

I need to get addressability to the UITableView cell so I can set the checkmark if the user has previously selected that row. Initially, the user checks the tableViewCell, I move that to a textField and store it in Core Data. Now if the user wants to change something in the UITableView, I want to be able to show what is already checked or unchecked.
So, I have a UITableView that I present in a UIPopover (outside of -cellForRowAtIndexPath); if a particular cell has a text value equal to an element in a NSArray, I want to set the cell's accessoryType to checked. This is my UPDATED code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// get the timeFormat
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *preferencesDict = [[userDefaults dictionaryForKey:#"preferencesDictionary"] mutableCopy];
int iTimeFormat = [[preferencesDict objectForKey:#"timeFormat"] intValue]; // set timeFormat
if(tableView.tag == kServicesTableView) { // services array
static NSString *CellIdentifier = #"servicesCell"; // servicesCell is just an identifier; not used that I can tell
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:(UITableViewCellStyleDefault) reuseIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell
SingletonServicesArray *sharedServicesArray = [SingletonServicesArray sharedServicesArray]; // initialize
[cell.textLabel setText:[sharedServicesArray.globalServicesArray objectAtIndex:indexPath.row]];
if( ![soServices hasText] ) { // no text in text box
for (int i = 0; i < sharedServicesArray.globalServicesArray.count; i++) {
NSIndexPath *path = [NSIndexPath indexPathForRow:i inSection:0];
// if row has been checked, remove the check
UITableViewCell *cell = [servicesTableView cellForRowAtIndexPath:path];
if (cell.accessoryType == UITableViewCellAccessoryCheckmark) {
cell.accessoryType = UITableViewCellAccessoryNone;
break;
}
}
}
else { // not empty
for (int i = 0; i < sharedServicesArray.globalServicesArray.count; i++) {
NSIndexPath *path = [NSIndexPath indexPathForRow:i inSection:0];
UITableViewCell *cell = [servicesTableView cellForRowAtIndexPath:path];
// if row is in soServices textfield, add a checkmark
if ([soServices.text containsString: sharedServicesArray.globalServicesArray[i]]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
break;
}
}
}
return cell;
}
It's not working; I know for sure at least two of the services are in the array, so I should have two cells with checkmarks; I'm getting zilch!. How do I get this to work correctly?
If I get it correctly, this cellForRowAtIndexPath is updating the cells of a DIFFERENT tableView. It would be more logical to add the above code to the cellForRowAtIndexPath of the tableView that needs updating.
In case you are trying to update the cells of that tableView, then the following code would not work:
for (int i = 0; i < sharedServicesArray.globalServicesArray.count; i++) {
NSIndexPath *path = [NSIndexPath indexPathForRow:i inSection:0];
UITableViewCell *cell = [servicesTableView cellForRowAtIndexPath:path];
// if row is in soServices textfield, add a checkmark
if ([soServices.text containsString: sharedServicesArray.globalServicesArray[i]]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
break;
}
}
Instead, you would write:
for (int i = 0; i < sharedServicesArray.globalServicesArray.count; i++) {
// if row is in soServices textfield, add a checkmark
if ([soServices.text containsString: sharedServicesArray.globalServicesArray[i]]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
break;
}
}

checking multiple cells in UITableView

I am using two NSMutable Arrays for storing multiple checkmarks..repeatDaysToStore and data1.
This is the code in viewDidLoad method...
for (int i=1; i<=[repeatDaysToStore count]; i++)
{
BOOL isTheObjectThere = [repeatArray containsObject:[repeatDaysToStore objectAtIndex:(i-1)]];
if (isTheObjectThere)
{
NSString *into=[repeatArray objectAtIndex:[repeatArray indexOfObject:[repeatDaysToStore objectAtIndex:(i-1)]]];
[data1 addObject:into];
NSLog(#"check did load");
}
}
and after this I am using data1 array for checking the cells...
- (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];
}
cell.textLabel.text=[repeatArray objectAtIndex:indexPath.row];
tableView.allowsMultipleSelection=YES;
for (int j=1; j<=[data1 count]; j++) {
NSLog(#"%d",[[data1 objectAtIndex:(j-1)]intValue]);
NSLog(#"%d",indexPath.row);
if([data1 indexOfObject:[data1 objectAtIndex:(j-1)]]==indexPath.row)
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
}
and for handling row selections I am using this code in didSelectRowAtIndexPath
{
NSString *myobj=[repeatArray objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (cell.accessoryType == UITableViewCellAccessoryCheckmark) {
cell.accessoryType = UITableViewCellAccessoryNone;
[repeatDaysToStore removeObject:myobj];
[data1 removeObject:myobj];
} else {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
[repeatDaysToStore addObject:myobj];
[data1 addObject:myobj];
}
[tableView reloadData];
}
but its not working.Someone please tell me what is wrong?
I think the issue is with cellForRowAtIndexPath: method's following code:
for (int j=1; j<=[data1 count]; j++)
{
NSLog(#"%d",[[data1 objectAtIndex:(j-1)]intValue]);
NSLog(#"%d",indexPath.row);
if([data1 indexOfObject:[data1 objectAtIndex:(j-1)]]==indexPath.row)
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
}
Remove the code and write the condition like:
if([data1 containsObject:[repeatArray objectAtIndex:indexPath.row])
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
Also remove the [tableView reloadData]; from didSelectRowAtIndexPath:
I think you need a less-complicated data structure to make this work in a way that's easy to understand.
The data for your table should be an array of objects. Each object should contain at least two properties: one is the information you're going to show in cell.textLabel.text and the other is a BOOL property that says whether or not the object is selected.
When a row is selected, set the BOOL to the opposite of what it currently is and reload your table. In cellForRowAtIndexPath:, just use the current setting of the object for the requested row without going through a secondary array.

table removing index path row values from array

I'm trying to create an array from selected table rows so I can push it to a new view. My issue is deleting an unselected row from the array it throws an error of index beyond bounds, although I have other items selected.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//listOfItems is a Pre Populated Array for the table
NSString *cellValue = [listOfItems objectAtIndex:indexPath.row];
//the array i want my selected items to be added to
NSArray *array = names;
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (cell.accessoryType == UITableViewCellAccessoryNone) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
[names addObject:cellValue];
NSLog(#"ARRAY: %#", array);
}
else {
cell.accessoryType = UITableViewCellAccessoryNone;
[names removeObjectAtIndex:indexPath.row];
NSLog(#"ARRAY: %#", array);
}
[tableView deselectRowAtIndexPath:indexPath animated:NO];
}
}
How do I find the index number in the array that is being created so it can delete the proper value? Any help would be appreciated :-) Thanks!
-----SOLUTION-----
Here's an alternative way too just in case others were wondering.
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
NSString *selected = [listOfItems objectAtIndex:indexPath.row];
if (cell.accessoryType == UITableViewCellAccessoryNone) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
[names addObject:selected];
NSLog(#"ARRAY ADDED %#", names);
}
else {
cell.accessoryType = UITableViewCellAccessoryNone;
[names removeObject:selected];
NSLog(#"ARRAY DELETED %#", names);
}
If your intention is to pass an array of checked cell values to a view, why adding and removing objects at each cell selection? You could easily achieve this just before you're about to present the new view controller. Something like this:
// In whatever method you have to present the new view controller
// ...
NSMutableArray *names = [[NSMutableArray alloc] initWithCapacity:0];
for (int i = 0; i < listOfItems.count; i++)
{
if ([self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]].accessoryType == UITableViewCellAccessoryCheckmark) //Change section number if not 0
{
[names addObject:[listOfItems objectAtIndex:i]];
}
}
// Pass the array now to the destination controller
Ps. You still have to manage checking/unchecking of cells (just as you're already doing in the code above).

Search Bar for UITableView: I would like it to search through both textLabels and detailTextLabels (iOS 5.0, xcode 4.2)

I have set up a search bar for my table view, and I would like to have it search through both the textLabels (main text), and detailTextLabels (underlying text), while maintaining the relationship between the main labels and detail labels when narrowing down the search. Right now, I can search through the main labels only, but the detail labels will not stay with the related main labels. I have two arrays that carry the strings for the main labels and the detail labels. Here is some code.
The initialization of the arrays carrying the main labels (groceryStores) and the detail labels (groceryStoreAddresses), as well as the search array
- (void)viewDidLoad
{
[super viewDidLoad];
self.groceryStores = [NSArray arrayWithObjects:#"Safeway",#"Grocery Store 1",#"Grocery Store 2",#"Grocery Store 3",#"Grocery Store 4",#"Grocery Store 5",#"Grocery Store 6",#"Grocery Store 7",nil];
self.groceryStoreAddresses = [NSArray arrayWithObjects:#"1444 Shattuck Place, Berkeley, CA 94709",#"Address 1",#"Address 2",#"Address 3",#"Address 4",#"Address 5",#"Address 6",#"Address 7",nil];
self.searchGroceryStores = [NSMutableArray arrayWithCapacity:[self.groceryStores count]];
}
The search method
- (BOOL) searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
[self.searchGroceryStores removeAllObjects] ;
for (NSString *groceryStore in self.groceryStores) {
NSRange result = [groceryStore rangeOfString:searchString options:
NSCaseInsensitiveSearch];
if (result.location != NSNotFound)
[self.searchGroceryStores addObject:groceryStore];
}
return YES;
}
Rows in section tweaked for search functionality
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
return [self.searchGroceryStores count];
}
else {
return [self.groceryStores count];
}
}
cellForRowAtIndexPath tweaked for search functionality
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"UITableViewCellAccessoryDisclosureIndicator"];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
if (tableView == self.searchDisplayController.searchResultsTableView) {
cell.textLabel.text = [self.searchGroceryStores objectAtIndex:indexPath.row];
cell.detailTextLabel.text = [self.groceryStoreAddresses objectAtIndex:indexPath.row];
}
else {
cell.textLabel.text = [self.groceryStores objectAtIndex:indexPath.row];
cell.detailTextLabel.text = [self.groceryStoreAddresses objectAtIndex:indexPath.row];
}
// Configure the cell...
return cell;
}
Please help if you can. Thank you so much for your time, I appreciate it.
You need to keep a searchGroceryStoreAddresses array in parallel with your searchGroceryStores array. Example:
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
[self.searchGroceryStores removeAllObjects];
[self.searchGroceryStoreAddresses removeAllObjects];
for (int i = 0, c = self.groceryStores.count; i < c; ++i) {
NSString *groceryStore = [self.groceryStores objectAtIndex:i];
NSString *address = [self.groceryStoreAddresses objectAtIndex:i];
NSRange result = [groceryStore rangeOfString:searchString options:
NSCaseInsensitiveSearch];
if (result.location != NSNotFound) {
[self.searchGroceryStores addObject:groceryStore];
[self.searchGroceryStoreAddresses addObject:address];
}
}
return YES;
}

Custom TableViewCell changing values randomly?

I'm having a problem. I have a Custom UITableViewCel, the cell contains a slider which changes a value on a label on the cell. When clicking on the cell and then moving the table, the value replicates itself on another cell and then changes the value to a different cell, resetting it's value to 0.
For demonstration purposes:
First setting the value
Clicking on a random cell then returns:
A totally different cell with the same data that was not put there.
And then when returning back to the cell where the value was first set:
The value is back to 0
Can anyone help me here:
My Slider value changed code;
labelSliderVal.text = [NSString stringWithFormat:#"%1.0f", sliderSlider.value];
if(sliderSlider.value < 30)
{
self.backgroundColor = [UIColor redColor];
}
else if(sliderSlider.value > 60)
{
self.backgroundColor = [UIColor greenColor];
} else {
self.backgroundColor = [UIColor blueColor];
}
And my UITableViews didSelectRowAtIndexPath
Commented out
/*
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Navigation logic may go here. Create and push another view controller.
/*
<#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:#"<#Nib name#>" bundle:nil];
// ...
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
}*/
CellForRowAtIndexPath:
- (CustomCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"customCell";
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *nibObjs = [[NSBundle mainBundle] loadNibNamed:#"CustomCellView" owner:nil options:nil];
//cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
for(id currentObj in nibObjs)
{
if([currentObj isKindOfClass:[CustomCell class]])
{
cell = (CustomCell *)currentObj;
}
}
}
GradeToolAppDelegate * appDelegate = [UIApplication sharedApplication].delegate;
Module *aModule = [appDelegate.modules4 objectAtIndex:indexPath.section];
AssessmentDetail *anAssess = [aModule.assessmentDetails4 objectAtIndex:indexPath.row];
cell.sliderSlider.tag = indexPath.row;
cell.labelAssessment.text = [NSString stringWithFormat:#"%#", anAssess.assessmentName4];
cell.labelAssessmentType.text = [NSString stringWithFormat:#"%#", anAssess.assessmentType4];
cell.labelWeighting.text = [NSString stringWithFormat:#"%#", anAssess.assessmentWeighting4];
cell.labelDueDate.text = [NSString stringWithFormat:#"%#", anAssess.assessmentDueDate4];
return cell;
}
Initialization
NSMutableArray *results = [[NSMutableArray alloc] init];
NSInteger numberOfSections = [myTableView numberOfSections];
for ( int section = 0; section < numberOfSections ; section++ ) {
NSInteger numberOfRows = [myTableView numberOfRowsInSection:section];
NSMutableArray *sectionResults = [NSMutableArray array];
[results addObject:sectionResults];
for ( int row = 0; row < numberOfRows; row++ ) {
[sectionResults addObject:[NSNumber numberWithFloat:0.0]];
}
}
In tableView:cellForRowAtIndexPath:,
...
NSArray *sectionResults = [results objectAtIndex:indexPath.section];
NSNumber *number = [sectionResults objectAtIndex:indexPath.row];
slider.value = [number floatValue];
...
In sliderValueChanged:,
- (void)sliderValueChanged:(UISlider *)slider {
CustomCell *cell = (CustomCell *)slider.superview;
NSIndexPath *indexPath = [myTableView indexPathForCell:cell];
NSArray *sectionResults = [results objectAtIndex:indexPath.section];
...
NSNumber *number = [NSNumber numberWithFloat:slider.value];
cell.sliderValueLabel.text = [NSString stringWithFormat:#"%f", slider.value];
[sectionResults replaceObjectAtIndex:indexPath.row withObject:number];
...
}
Code for totalForSection:,
- (float) totalForSection:(NSInteger)sectionIndex {
NSArray *sectionResults = [results objectAtIndex:sectionIndex];
float result = 0.0;
for (NSNumber *number in sectionResults) {
result += [number floatValue];
}
return result;
}
- (float)sumTotal {
float result = 0.0;
NSinteger numberOfSections = [myTableView numberOfSections];
for ( int i = 0; i < numberOfSections; i++ ) {
result += [self totalForSection:i];
}
return result;
}
Initial Answer
Well this is happening because of reusable cells. You will need to store the state of the contents and update the cell accordingly in tableView:cellForRowAtIndexPath:. On slider value change,
- (void)sliderValueChanged:(UISlider *)slider {
CustomCell *cell = (CustomCell *)slider.superview;
NSIndexPath *indexPath = [myTableView indexPathForCell:cell];
AssessmentDetail *anAssessment = [module.assessmentDetails4 objectAtIndex:indexPath.row];
anAssessment.property = slider.value;
cell.propertyLabel.text = [NSString stringWithFormat:#"%f", slider.value];
}
Now both the model and the label have been changed. So next time cell is reused, the label should get the right value.