didSelectRowAtIndexPath selecting multiple tableView cell accessory's - ios

I have a tableView containing a list of user names that are indexed into sections and rows alphabetically. When I tap on one of the rows in a section, the correct user is added to my recipients array and a checkmark is places in the cell besides their name.. but a checkmark is also displayed next to other usernames that have not been selected and are not in the recipients array. I have tried to reassign the selected cell with a new indexPath (see code below) but have not been able to get it to work. It registers the correct path, but won't assign it. I am using a similar method for assigning the users the the rows in each section with no trouble but for some reason the accessory marks are giving me problems. I've seen some other threads on overflow about this same topic but wash;'t able to come to a solution for my case. any clues? cheers!
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
int row = indexPath.row;
int section = indexPath.section;
NSIndexPath *newIndexPath = [NSIndexPath indexPathForRow:row inSection:section];
[tableView deselectRowAtIndexPath:newIndexPath animated:NO];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:newIndexPath];
NSArray *array = [self.sectionsArray objectAtIndex:indexPath.section];
PFUser *user = [array objectAtIndex:indexPath.row];
if (cell.accessoryType == UITableViewCellAccessoryNone) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
[self.recipients addObject:user];
}
else {
cell.accessoryType = UITableViewCellAccessoryNone;
[self.recipients removeObject:user];
}
[self.currentUser saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (error) {
NSLog(#"Error %# %#", error, [error userInfo]);
}
}];
and here's the cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *CellIdentifier = #"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Get the user names from the array associated with the section index in the sections array.
NSArray *userNamesInSection = (self.sectionsArray)[indexPath.section];
// Configure the cell with user name.
UserNameWrapper *userName = userNamesInSection[indexPath.row];
cell.textLabel.text = userName.user;
return cell;
}

As I see you made 2 mistakes in CellForRowAtIndexPath that did not check if cell is null to create one and set accessoryType for cell according to the recipient list.
You should do like below:
NSString *CellIdentifier = #"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
PFUser *user = [self getUserAtIndexPath:indexPath];
cell.textLabel.text = user.name;
if ([self.recipients containObject:user]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;

Related

How to reload data from a specific NSMutableArray into a UITableView

Hi I'm using a web service that returns a JSON document. In that document are several objects, each one containing a value for "STATE" (among others like "ADDRESS", "DISTANCE",etc). This "STATE" value can be either "ARCHIVED" or "ACTIVE". What I want to do is to load the objects with "ARCHIVED" value of "STATE" into the archivedProcessTV and the others to activeProcessTV.
I managed to fill both mutable arrays with the desired data but when the TableViews reload, they reload all the objects into the cells.
Here is what I'm doing on connectionDidFinishLoading:
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
NSLog(#"Entrou no connectionDidFinishLoading");
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
process = [[NSJSONSerialization JSONObjectWithData:data options:0 error:nil] objectForKey:#"GetGesturbeDataJsonResult"];
NSInteger i = 0;
archived = [[NSMutableArray alloc] init];
active = [[NSMutableArray alloc] init];
for (NSObject *array1 in process) {
while (i <= process.count) {
if([[[process objectAtIndex:i] objectForKey:#"STATE" ] isEqualToString:#"ARCHIVED"])
{
[archived addObject:[process objectAtIndex:i]];
i++;
}else
[active addObject:[process objectAtIndex:i]];
i++;
}
}
[activeProcessTV reloadData];
[archivedProcessTV reloadData];
}
Thanks for your help!
EDIT:
-(UITableViewCell *)tableView:(UITableView *)atableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [atableView dequeueReusableCellWithIdentifier:#"a cell"];
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"a cell"];
cell.textLabel.text = [[process objectAtIndex:indexPath.row] objectForKey:#"DISTANCE"];
return cell;
}
You appear to have 2 problems:
tableView:cellForRowAtIndexPath: does not check which table view is asking for data
You don't use your specific array contents to populate the table views, you use [[process objectAtIndex:indexPath.row] objectForKey:#"DISTANCE"]; which contains all data
So, at the start of tableView:cellForRowAtIndexPath: you should have an if statement which checks the atableView parameter and decides which array to use. Then, edit the line which updates the cell text to use that array.
- (UITableViewCell *)tableView:(UITableView *)atableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSArray *sourceArray;
if (atableView == self.activeProcessTV) {
sourceArray = self.active;
} else {
sourceArray = self.archived;
}
UITableViewCell *cell = [atableView dequeueReusableCellWithIdentifier:#"a cell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"a cell"];
}
cell.textLabel.text = [[sourceArray objectAtIndex:indexPath.row] objectForKey:#"DISTANCE"];
return cell;
}
Aside: this code:
UITableViewCell *cell = [atableView dequeueReusableCellWithIdentifier:#"a cell"];
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"a cell"];
needs to be changed to:
UITableViewCell *cell = [atableView dequeueReusableCellWithIdentifier:#"a cell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"a cell"];
}
as currently you're always creating a new cell even if you did get a reusable one back...
In-spite of using sourcearray just pass like that below and check:-
-(UITableViewCell *)tableView:(UITableView *)atableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [atableView dequeueReusableCellWithIdentifier:#"a cell"];
if (cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"a cell"];
}
if ( atableView ==self.activeProcessTV) {
cell.textLabel.text = [[self.activeProcessTV objectAtIndex:indexPath.row] objectForKey:#"DISTANCE"];
}
else{
cell.textLabel.text = [[self.archived objectAtIndex:indexPath.row] objectForKey:#"DISTANCE"];
}
return cell;
}
Note: Pass the correct array count inside numberOfRowsInSection method of UITableView.

How to use to two cell identifiers to sort data?

com documentation, and I am using the PFQueryForTable method with two different tableViewCells to sort my parse objects when they are put into the cells. I am doing this so when the object returns a blank string I can reduce its height to 0. Unfortunately when I do this it does not run like I want to and the blank cells are loaded into the first first cell, and not the one that I want to reduce the height to.
My cellForRowAtIndexPath looks like:
-(UITableViewCell *) tableView: (UITableView *) tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object{
BOOL blank = [[[object objectForKey:#"post"] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] isEqualToString:#""];
static NSString *CellIdentifier = #"Cell";
static NSString *Cell2Identifier = #"Cell2";
if (!blank) {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
UILabel *label;
label = (UILabel *)[cell viewWithTag:1];
label.text = [object objectForKey:#"post"]; //[object objectForKey:#"post"];
label.textAlignment = NSTextAlignmentCenter;
NSLog(#"posted cell");
return cell;
}
else {
UITableViewCell *cell2 = [tableView dequeueReusableCellWithIdentifier:Cell2Identifier forIndexPath:indexPath];
if (cell2 == nil) {
cell2 = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:Cell2Identifier];
}
cell2.textLabel.text = [object objectForKey:#"post"];
NSLog(#"posted cell2");
return cell2;
}
}
Can someone please point me in the right direction?
Thank you.
OK, from your comment you are currently creating a query like this...
- (PFQuery *)theQuery
{
PFQuery *posts = [PFUser query];
[posts whereKey:#"location" nearGeoPoint:self.myLocation withinKilometers:10.0];
[posts includeKey:self.textKey];
return posts;
}
So all you need to do is add another where condition to it...
[posts whereKey:#"theText" notEqualTo:#""];
This will stop blank text posts from being sent down.

UITableView populating Core data one-to-many relationship using checkmark - CoreData: error: Serious application error

I have an app where I'm trying to select Resources (buildings, IT Systems) that a Programme relies on. The relationship between the Entities is Resource.resourceusedonprog<<--->Programme.usesresource. A slice of the data model is here:
I'm trying to use multiple checkmark selection on a UITableViewController of which resources get used by a particular programme. However, when I try to interact with the Resource cells in the table the app crashes with the error
CoreData: error: Serious application error. Exception was caught
during Core Data change processing. This is usually a bug within an
observer of NSManagedObjectContextObjectsDidChangeNotification. The
left hand side for an ALL or ANY operator must be either an NSArray or
an NSSet. with userInfo (null)
My code is as follows:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Resource Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
Resource *resource = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSString *fullname = [NSString stringWithFormat:#"%# %#", resource.hasmanager.firstname, resource.hasmanager.surname];
cell.textLabel.text = resource.name;
cell.detailTextLabel.text = fullname;
if (resource.resourceusedonprog == selectedProgramme){ cell.accessoryType = UITableViewCellAccessoryCheckmark;}
else {cell.accessoryType = UITableViewCellAccessoryNone;}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)path {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:path];
Resource *resource = [self.fetchedResultsController objectAtIndexPath:path];
if (cell.accessoryType == UITableViewCellAccessoryCheckmark) {
cell.accessoryType = UITableViewCellAccessoryNone;
resource.resourceusedonprog = NULL;
} else {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
resource.resourceusedonprog = selectedProgramme;
} }
The error seems to be triggered by the resource.resourceusedonprog = selectedProgramme; and resource.resourceusedonprog = NULL; events. if I comment them out I can check and uncheck the cells (but clearly, not change the state of resource.resourceusedonprog, i.e. what I'm trying to achieve).
I've found the answer, which is based on two issues. Firstly, the relationship key is an NSSet, not an object, for a many-to-many relationship. Secondly, the NSManagedObject Subclass for each entity automatically generates add and remove methods for each relationship. In the case of this one I've implemented
[resource removeResourceusedonprogObject:selectedProgramme]; and
[resource addResourceusedonprogObject:selectedProgramme];
The overall code is here:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Resource Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
Resource *resource = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSString *fullname = [NSString stringWithFormat:#"%# %#", resource.hasmanager.firstname, resource.hasmanager.surname];
cell.textLabel.text = resource.name;
cell.detailTextLabel.text = fullname;
if ([resource.resourceusedonprog containsObject:selectedProgramme]){ cell.accessoryType = UITableViewCellAccessoryCheckmark;}
else {cell.accessoryType = UITableViewCellAccessoryNone;}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)path {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:path];
Resource *resource = [self.fetchedResultsController objectAtIndexPath:path];
NSLog(#"selectedResource is: %#", resource.name);
if (cell.accessoryType == UITableViewCellAccessoryCheckmark) {
cell.accessoryType = UITableViewCellAccessoryNone;
[resource removeResourceusedonprogObject:selectedProgramme];
} else {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
[resource addResourceusedonprogObject:selectedProgramme];
} }

iOS: Tableview multiple selection - AccessoryCheckmark checking reusable cells

I'm using a tableview with sections and multiple selection, but I have an issue with multiple rows being checked when one row is chosen...
I've seen a few other threads about this, but didn't really get a solution...
Here's my code:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *) indexPath
{
[employeeTable deselectRowAtIndexPath:[employeeTable indexPathForSelectedRow] animated:NO];
UITableViewCell *cell = [employeeTable cellForRowAtIndexPath:indexPath];
// get the letter in each section
NSString *alphabet = [charIndex objectAtIndex:indexPath.section];
// get the names beginning with the letter
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF beginswith[c] %#", alphabet];
NSArray *names = [listOfNames filteredArrayUsingPredicate:predicate];
NSString *name = [names objectAtIndex:indexPath.row];
for(int i = 0; i < [employeeConnection.employees count]; i++)
{
Employee *aEmployee = [employeeConnection.employees objectAtIndex:i];
NSString *firstName = aEmployee.firstName;
NSString *lastName = aEmployee.lastName;
NSString *fullName = [NSString stringWithFormat:#"%# %#", firstName, lastName];
if([fullName isEqualToString:name])
{
NSLog(#"Name: %#", name);
if (cell.accessoryType == UITableViewCellAccessoryNone) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
// Reflect selection in data model
[chosenEmployees addObject:aEmployee.employeeID];
[chosenEmployeesNames addObject:name];
} else if (cell.accessoryType == UITableViewCellAccessoryCheckmark) {
cell.accessoryType = UITableViewCellAccessoryNone;
// Reflect deselection in data model
[chosenEmployees removeObject:aEmployee.employeeID];
[chosenEmployeesNames removeObject:name];
}
}
}
}
Update: Added cellForRowAtIndexPath
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
cell.textLabel.textColor = [UIColor whiteColor];
}
// Get the letter in the current section
NSString *alphabet = [charIndex objectAtIndex:[indexPath section]];
// Get the names beginning with the letter
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF beginswith[c] %#", alphabet];
NSArray *names = [listOfNames filteredArrayUsingPredicate:predicate];
if([names count] > 0)
{
// Extract the name
cell.textLabel.text = [names objectAtIndex:indexPath.row];
}
return cell;
}
I would suggest storing an NSMutableSet of either the checked ManagedObject (when using CoreData) or simply the checked IndexPaths. In -cellForRowAtIndexPath: you can then check if the cell is supposed to be checked.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *const identifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier];
cell.textLabel.textColor = UIColor.whiteColor;
}
if ([self.checkedIndexPaths containsObject:indexPath]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *const cell = [tableView cellForRowAtIndexPath:indexPath];
[table deselectRowAtIndexPath:indexPath animated:NO];
if ([self.checkedIndexPaths containsObject:indexPath]) {
[self.checkedIndexPaths removeObject:indexPath];
cell.accessoryType = UITableViewCellAccessoryNone;
}
else {
[self.checkedIndexPaths addObject:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
}
Since cells are being reused, you need to set the accessory mark to on or off for every cell in the table in the cellForRowAtInexPath table datasource method.
So the cell.accessoryType cell property should be soecified in the cellForRowAtInexPath and not the didSelectRow delegate method.
In the didSelectRow, just keep track of the selected rows in an array, and set the cells accessory mark to none or checkmark in the cellForRowAtInexPath dependingon the array value.

UITableViewCell with Checkmark, duplicating checkmarks

So far search on Stack Overflow I havent found a situation that is like mine. Any assistance is greatly appreciated: I keep seeing that if I put a checkmark on Person A, Person H will also have one as well as does a person about 10 away. Basically abut every 10 it repeats a check mark.
Here is my code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{static NSString *CellIdentifier = #"MyCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text =
[NSString stringWithFormat:#"%# %#", [[myArrayOfAddressBooks objectAtIndex:indexPath.row] objectForKey:#"FirstName"],[[myArrayOfAddressBooks objectAtIndex:indexPath.row] objectForKey:#"LastName"]];
cell.detailTextLabel.text =
[NSString stringWithFormat:#"%#", [[myArrayOfAddressBooks objectAtIndex:indexPath.row] objectForKey:#"Address"]];
return cell;
}
In my did select row for index path I have this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell;
cell = [self.tableView cellForRowAtIndexPath: indexPath];
if ([[myArrayOfAddressBooks objectAtIndex:indexPath.row] objectForKey:#"emailSelected"] != #"YES")
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
[[myArrayOfAddressBooks objectAtIndex:indexPath.row] setValue:#"YES" forKey:#"emailSelected"];
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
[[myArrayOfAddressBooks objectAtIndex:indexPath.row] setValue:#"NO" forKey:#"emailSelected"];
}
This is due to how UITableView "recycles" UITableViewCell for efficiency purposes, and how you are marking your cells when they are selected.
You need to refresh/set the accessoryType value for every cell you process/create within tableView:cellForRowAtIndexPath:. You properly update the state in your myArrayOfAddressBooks data structure, and you just need to use this information in tableView:cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"MyCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
NSDictionary *info = [myArrayOfAddressBooks objectAtIndex:indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:#"%# %#", [info objectForKey:#"FirstName"],[info objectForKey:#"LastName"]];
cell.detailTextLabel.text = [NSString stringWithFormat:#"%#", [info objectForKey:#"Address"]];
cell.accessoryType = ([[info objectForKey:#"emailSelected"] isEqualString:#"YES"]) ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;
return cell;
}
Also, unless there is a good reason for saving the state as #"Yes" or #"No" strings, why not save them as [NSNumber numberWithBool:YES] or [NSNumber numberWithBool:NO]? This will simplify your logic when you want to do comparisons versus having to use isEqualToString: all the time.
e.g.
cell.accessoryType = ([[info objectForKey:#"emailSelected"] boolValue]) ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;

Resources