Selecting multiple rows in tableview - iphone

I implemented the UITableView in my iPhone application. Further more I added multiple selection of row in UITableView.But the problem is that when I select particular row and then scrolls table and come back then respective row in unselected(unmarked).Here is my didSelectRowAtIndexPath method.
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
if([tableView cellForRowAtIndexPath:indexPath].accessoryType==UITableViewCellAccessoryNone)
{
[tableView cellForRowAtIndexPath:indexPath].accessoryType=UITableViewCellAccessoryCheckmark;
[selectedIndexes addObject:[NSNumber numberWithInt:indexPath.row]];
NSLog(#"Select now %d",[selectedIndexes count]);
}
else if([tableView cellForRowAtIndexPath:indexPath].accessoryType==UITableViewCellAccessoryCheckmark)
{
[tableView cellForRowAtIndexPath:in dexPath].accessoryType=UITableViewCellAccessoryNone;
[selectedIndexes removeObject:[NSNumber numberWithInt:indexPath.row]];
NSLog(#"DeSelect now %d",[selectedIndexes count]);
}
}
Please provide me solution.

While you are scrolling the table view every time,the delegate method cellForRowAtIndexPath method is called every time..So,it will load the data every time.Means, first you have to store which rows are clicked in didSelectRowAtIndexPath method, and according to these rows indexes in cellForRowAtIndexPath method you have to show that which table view rows are already selected with check marks and for remaining rows with out check mark...

check this. multiple-row-selection-and-editing
You will find really easy & usefull.
Enjoy Programming

Related

Refreshing UITableview Section to One With Different Number of Rows

I've recently been working with a UITableView. It is dynamically populated once, then when a user selects an option, I want the list to change to a new one. I'm working with a grouped table with 3 sections and as you click on the rows the three groups need to be repopulated with a varying number of new rows. While my code works fine when there is the same number of rows in the new section as old, it crashes when that number changes. Interestingly though, it will wait to crash until it attempts to draw one of the cells that was there previously (the tableView still thinks the section has the old number of rows, tries to draw the cell that is no longer in my model object, and so I think it crashes because it's referencing a value in the new array that doesn't exist.
It crashes here:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = nil;
cell = [tableView dequeueReusableCellWithIdentifier:#"MyCell"];
if (cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"MyCell"];
}
if (indexPath.section==2){
//CRASH BELOW
cell.textLabel.text = [NSString stringWithFormat:#"%#", (NSString *)[[[[storyArray objectAtIndex:pageNumber]getChoices] objectAtIndex:(unsigned long)indexPath.row]objectAtIndex:0]] ;
}
return cell;
}
The function I use to reload the table is here:
-(void)changePage:(int)pageChangeNumber{
NSLog(#"The page change! Changing to: %#",[[storyArray objectAtIndex:pageChangeNumber]getTitle]);
pageHeader.text=[[storyArray objectAtIndex:pageChangeNumber] getTitle];
pageBody.text=[[storyArray objectAtIndex:pageChangeNumber] getBody];
[myTableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];
[myTableView reloadSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationFade];
[myTableView reloadSections:[NSIndexSet indexSetWithIndex:2] withRowAnimation:UITableViewRowAnimationFade];
[myTableView reloadData];
pageNumber=pageChangeNumber;
NSLog(#"Page Change Done");
}
I've also changed the numberofRowsInSection to be dynamic...
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
NSLog(#"Recounting Rows");
if (section==2){
return [[[storyArray objectAtIndex:pageNumber]getChoices] count];
} else {
return 0;
}
}
Any ideas on how I can get this working when the number of rows per section changes?
Thanks!
I don't know what crash you're getting, but I ran into crashes if the numberOfSectionsInTableView: or tableView:numberOfRowsInSection: methods returned different numbers of rows while the table was restructuring itself.
For example, UITableView calls those methods many times while it is redrawing (including during animations). In my backend, some of the values were changing during animation.
I had to take special care to synchronize those values before changing the UITableView
Before you update your table view's data and call reloadSections you need to first call [myTableView beginUpdates] and once you're done [myTableView endUpdates]

Clearing TableView contents

Can any one help me by providing code for how to clear all the table view cell contents.
The tableview when a button is pressed gets reloaded but does not get cleared
Thanks
Rakesh
For clearing a tableView, you just remove all objects from the array that is populating your table and you reload the tableView after that.
[myArray removeAllObjects];
[self.tableView reloadData];
Set a bool tableIsEmpty and set the number of rows in the tableview to either 0 (table is empty) or the arrays count according to this BOOl in
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
Of cousre you have to set tableIsEmpty = YES, when the button is pressed and to NO again when theres data loaded.
Then call
[self.tableView reloadData];
Not tested but should work shouldnt it?
You can just add one variable as flag before reloading the table.
makeEmpty = YES;
[tableview reloadData];
makeEmpty = NO;
Then in the tableview delegate method
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if(makeEmpty)
return 0;
}

Smooth reload of uitableview data when rows are deleted

I am trying to reload a uitableview when it scrolls to the bottom of the screen. I delete the first few rows and add more rows to the bottom. Before the news rows are added to the uitableview's data source, i call the [tableview reloaddata] method. This is because i want the tableview to display the row which were previously visible on it.
It reloads the data correctly but there is a sudden jerk in the tableview. It flashes which doesn't give a nice user experience. So my question is
How to update the uitableview
when few rows from the top are
deleted without having the
jerking/flashing effect?
The current visible row should be retained in the refreshed view also.
Any ideas? There are no crashes as i update the data source correctly.
TIA,
Praveen S
delete the data from the data source in tableView delegate.
-(void) tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *key = [keys objectAtIndex:indexPath.section];
NSMutableArray *nameSection =[names objectForKey:key];
int itemID=item.ID;
if ([nameSection count]==1) {
[self.keys removeObjectsInArray:[NSArray arrayWithObject:key]];
[tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationFade];
}
else {
[nameSection removeObjectAtIndex:indexPath.row];
[self.names setValue:nameSection forKey:key];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
}
}
check this too

problem in cell.accessoryType in UITableView

In my app if user press on any cell in UITableView then accessoryType of cell will be set to check mark like following
-(void)Check:(UITableView *)tableView Mark:(NSIndexPath *)indexPath
{
[self.tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryCheckmark;
buttonCount++;
[selectedCellArray addObject:indexPath];
}
and if user press the same cell then uncheck will happen as follows
-(void)UnCheck:(UITableView *)tableView Mark:(NSIndexPath *)indexPath
{
[self.tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryNone;
buttonCount--;
if (buttonCount == 0) {
[selectedCellArray removeAllObjects];
}
}
and i am calling this
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if([tableView cellForRowAtIndexPath:indexPath].accessoryType == UITableViewCellAccessoryCheckmark)
{
[self UnCheck:tableView Mark:indexPath];
}
else
{
[self Check:tableView Mark:indexPath];
}
Problem is when i am pressing on 1st cell it call Check method and mark the cell to but when i am scroll down i find 2-3 more cheked cell ...even i did not select those cell...i dont know why and how it checked automatically ...
i hope some one know where is the problem
thank you very much
Because cells will be reused by the tableview. Also set the checkmark/accessory type in your tableView:cellForRowAtIndexPath: method.
ya.. I see this happen a lot.
the correct pattern to manage the UITableViewCell state is NOT to directly manipulate the TableViewCell to update the UI, and always set, draw and create the correct state from cellForRowAtIndexPath (or tableView:willDisplayCell:forRowAtIndexPath:)
Meaning, if the user clicks on the cell and you need to update the UI of that cell, store the new state of that cell in an array or dictionary (I find that an NSMutableDictionary with the NSIndexPath as the key works very well).
then call the reloadRowsAtIndexPaths:withRowAnimation: or just [tableView reloadData] so that the cellForRowAtIndexPath reads that array or Dictionary and correctly draws the cell.
otherwise, the cellForRowAtIndexPath will constantly overwrite your changes and use recycled cells that hold incorrect state.
one exception to this rule would be if you would like a nice animation between the two states... if that is the case, save off your new state, perform your animation right there on the cell, then when the animation completes, call the same reloadRowsAtIndexPaths:withRowAnimation or reloadData so that the cell is redrawn in its new state.

Updating alternating colored UITableViewCells when rows gets reordered or deleted

I'm having a UITableView with alternating colored UITableViewCells. And the table can be edited: rows can be reordered and deleted. How do I update the cells alternating background color, when the rows get reordered or deleted?
I'm using this to draw the alternating colored cells:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
if ([indexPath row] % 2) {
// even row
cell.backgroundColor = evenColor;
} else {
// odd row
cell.backgroundColor = oddColor;
}
}
But this method is not being called when a row gets reordered or deleted. And I can't call [tableView reloadData] from the following method, because it crashes the app in an infinite loop:
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
// Move the object in the array
id object = [[self.list objectAtIndex:[fromIndexPath row]] retain];
[self.list removeObjectAtIndex:[fromIndexPath row]];
[self.list insertObject:object atIndex:[toIndexPath row]];
[object release];
// Update the table ???
[tableView reloadData]; // Crashes the app in an infinite loop!!
}
Does anybody have a pointer or a best practices solution to deal with the issue of reordering alternating colored cells?
Thanks
Used a delayed call to perform the reload if you can't call from that method:
[tableView performSelector:#selector(reloadData) withObject:nil afterDelay:0.0f];
It waits until after your current method is finished before it calls reload.
No needs to use third-party objects or reload/refresh the whole dataSource. Just use the right tools in your swiss knife:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if(editingStyle == UITableViewCellEditingStyleDelete) {
//1. remove your object
[dataSource removeObjectAtIndex:indexPath.row];
//2. update your UI accordingly
[self.myTableView beginUpdates];
[self.myTableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationRight];
[self.myTableView endUpdates];
//3. obtain the whole cells (ie. the visible ones) influenced by changes
NSArray *cellsNeedsUpdate = [myTableView visibleCells];
NSMutableArray *indexPaths = [[NSMutableArray alloc] init];
for(UITableViewCell *aCell in cellsNeedsUpdate) {
[indexPaths addObject:[myTableView indexPathForCell:aCell]];
}
//4. ask your tableview to reload them (and only them)
[self.myTableView reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
}
}
Reload is too heavyweight; I've written AltTableViewController that just changes background color of cells and it should work faster.
I took all the UITableViewCell subviews from the tableview and sorted that array based on cells frame.origin.y so they were back in the proper order. Then I looped through changing the background color based on the index == 0 || index % 2 == 0 re-coloring them. Seems to work better than reloading the tableView as that was causing the animation to jerk. Worked for me at 1:25 AM
[tableView reloadData] will get your table backgrounds back in the swing of things. Your other option is to swap the background colors of the all visible cells from the indexPath of the lower index in the move on up to the highest in visibleCells.
This works nice. Start the pattern at the last index rather than the first. That way each cell always retains it's background:
if (dataSource.count - indexPath.row) % 2 == 0 {
cell.backgroundColor = UIColor.grayColor()
} else {
cell.backgroundColor = UIColor.whiteColor()
}
I tried the other solutions, but wasn't completely satisfied. This solution is not a hack and doesn't even add another line of code.
If you're using swift and NSFetchedResultsController:
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.endUpdates()
//for the alternate colours
self.tableView.reloadRows(at: tableView.indexPathsForVisibleRows!, with: .fade)
}