Strange behavior - Row won't reselect until three taps - ios

I'm currently facing a strange issue with a UITableView in iOS whereby deselected rows are no longer selectable immediately, you must tap three times before you can select again. I'm implementing something akin to a checklist, with a "clear" button to go through and deselect all the cells. This effect only occurs when the cell had been previously selected, but the "clear" button is pressed. If a cell had been untouched before and is touched after the clear button, it will be presented and toggle fine.
Here is what I have
I should mention that the VC this Table is in is stored inside a container, hence the reference to it.
The deselect
- (IBAction)btnClearTapped:(id)sender {
UITableView *locationList = self.locationsVc.tableView;
for (NSUInteger i = 0; i < [locationList numberOfSections]; i++) {
for (NSUInteger j = 0; j < [locationList numberOfRowsInSection:i]; j++) {
[locationList deselectRowAtIndexPath:[NSIndexPath indexPathForRow:j inSection:i] animated:YES];
}
}
}
The didSelectRowAtIndexPath method
- (void) tableView: (UITableView *) tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
BOOL isChecked = !((NSNumber *) cellToggleDict[indexPath]).boolValue;
cellToggleDict[indexPath] = #(isChecked);
UIView *selectionColorView = [[UIView alloc] init];
if (isChecked) {
selectionColorView.backgroundColor = selectedColor;
selectedLocations[indexPath] = [[Search sharedManager] locationAtIndexPath:indexPath];//PFObject
}
else {
selectionColorView.backgroundColor = unselectedColor;
[selectedLocations removeObjectForKey:indexPath];
}
cell.selectedBackgroundView = selectionColorView;
}
And finally the dequeue and redraw method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Reuse" forIndexPath:indexPath];
PFObject *object = [[Search sharedManager] locationAtIndexPath:indexPath];
cell.textLabel.text = object[#"Name"];
BOOL isChecked = ((NSNumber *) cellToggleDict[indexPath]).boolValue;
UIView *selectionColorView = [[UIView alloc] init];
if (isChecked) {
selectionColorView.backgroundColor = selectedColor;
}
else {
selectionColorView.backgroundColor = unselectedColor;
}
cell.selectedBackgroundView = selectionColorView;
return cell;
}

You don't clear the cellToggleDict entries in the btnClearTapped: method. You should have.
- (IBAction)btnClearTapped:(id)sender {
UITableView *locationList = self.locationsVc.tableView;
[cellToggleDict removeAllObjects]; // Clear all objects from the selected store
for (NSUInteger i = 0; i < [locationList numberOfSections]; i++) {
for (NSUInteger j = 0; j < [locationList numberOfRowsInSection:i]; j++) {
[locationList deselectRowAtIndexPath:[NSIndexPath indexPathForRow:j inSection:i] animated:YES];
}
}
}

Related

Store Cells with switch on from a Tableview

What I want is to generate a sub-contact using properties form an existing CNContact object.
I got the CNContact object from CNContactPickerViewController and display all properties in a tableview.
The tableviewcell is embedded with switch. I have a preview button on the navigation item, when the button is pressed, if the switch in a property is on, this property should be stored in a new CNMutableContact.
My issue is: If the Contact has too many properties, I cannot get the off-screen properties stored. Is there a way to solve this problem.
part of the code to get sub-contact:
+(CNMutableContact*)newContactWithSelectedFieldInTableView:(UITableView*)tableView FromContact:(CNContact*)contact
{
CNMutableContact* aContact = [[CNMutableContact alloc]init];
//get all indexPath from tableview
NSMutableArray* indexPathArr = [[NSMutableArray alloc]init];
NSInteger nSections = [tableView numberOfSections];
for (int j=0; j<nSections; j++) {
NSInteger nRows = [tableView numberOfRowsInSection:j];
for (int i=0; i<nRows; i++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:j];
[indexPathArr addObject:indexPath];
}
}
//selected phone numbers
//go through indexPath
for (NSIndexPath* path in indexPathArr)
{
UITableViewCell* nameCell = [tableView cellForRowAtIndexPath:path];
UISwitch *mySwitch = (UISwitch *)nameCell.accessoryView;
switch (path.section)
{
case basicInfoSection://basic info section (name,company,department,title)
{
int row = 0;
if(path.row==row)
{
if(mySwitch.on)
{
aContact.givenName = contact.givenName;
aContact.middleName = contact.middleName;
aContact.familyName = contact.familyName;
}
}
if(![contact.organizationName isEqualToString:#""])
{row += 1;
if(path.row==row)//company row
{
//store company
if(mySwitch.on)
aContact.organizationName = contact.organizationName;
}
}
if(![contact.departmentName isEqualToString:#""])
{row += 1;
if(path.row==row)//department row
{
//store department
if(mySwitch.on)
aContact.departmentName = contact.departmentName;
}
}
if(![contact.jobTitle isEqualToString:#""])
{row += 1;
if(path.row==row)//jobTitle row
{
//store job Title
if(mySwitch.on)
aContact.jobTitle = contact.jobTitle;
}
}
}
break;
case phoneSection:
{
if(mySwitch.on)
{
aContact.phoneNumbers = [aContact.phoneNumbers arrayByAddingObject:contact.phoneNumbers[path.row]];
}
}
break;
I come up with a solution and it work fine for now, some code are listed below:
1# create a subclass of UISwitch
#import <UIKit/UIKit.h>
#interface SwitchWithIndex : UISwitch
#property (strong ,nonatomic) NSIndexPath* indexPath;
#end
2# create a dictionary that record indexPath with switch state and in view did load, loop datasource(the CNContact) for all possible indexPath according to the demand tableview layout.
for(NSInteger section=0; section<7; section++)
{
switch (section) {
{
//section 1 basic info(name, company, department, job title)
case basicInfoSection:
{int row =0;
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:row inSection:section];
[_switchStateAtIndex setObject:boolNumber forKey:indexPath];
if(![_contact.organizationName isEqualToString:#""])
{row += 1;
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:row inSection:section];
[_switchStateAtIndex setObject:boolNumber forKey:indexPath];
}
if(![_contact.departmentName isEqualToString:#""])
{row += 1;
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:row inSection:section];
[_switchStateAtIndex setObject:boolNumber forKey:indexPath];
}
if(![_contact.jobTitle isEqualToString:#""])
{row += 1;
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:row inSection:section];
[_switchStateAtIndex setObject:boolNumber forKey:indexPath];
}
}
break;
//section 2 phones
case phoneSection:
{
for(NSInteger row=0; row<[_contact.phoneNumbers count];row++)
{
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:row inSection:section];
[_switchStateAtIndex setObject:boolNumber forKey:indexPath];
}
}
break;
//more code ..
3# In Table View Datasource Delegate load the state of switch at indexpath and
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
SwitchWithIndex* mySwitch = [[SwitchWithIndex alloc] init];
[mySwitch addTarget:self action:#selector(switchChanged:) forControlEvents:UIControlEventValueChanged];
NSNumber* switchStateNumber =[_switchStateAtIndex objectForKey:indexPath];
BOOL switchState = [switchStateNumber boolValue];
mySwitch.on = switchState;
mySwitch.indexPath = [[NSIndexPath alloc]init];
mySwitch.indexPath = indexPath;
cell.accessoryView = mySwitch;
//more code
and the following code to detect change on switch state.
- (void) switchChanged:(id)sender {
SwitchWithIndex* mySwitch = sender;
NSIndexPath* indexPath = mySwitch.indexPath;
NSLog(#"%#",indexPath);
NSNumber* switchStateBool = [NSNumber numberWithBool:mySwitch.on ? YES : NO];
[_switchStateAtIndex setObject:switchStateBool forKey:indexPath];
NSLog( #"The switch is %#", mySwitch.on ? #"ON" : #"OFF" );
}
4# And finally save selected field based on switch state
+(CNMutableContact*)newContactFrom:(CNContact*)contact withSwitchState:(NSMutableDictionary*)switchState
{
CNMutableContact* aContact = [[CNMutableContact alloc]init];
for (NSIndexPath* indexPath in switchState.keyEnumerator)
{
NSNumber* boolNumber = [switchState objectForKey:indexPath];
BOOL switchOn = [boolNumber boolValue];
switch (indexPath.section) {
case basicInfoSection://basic info section (name,company,department,title)
{
int row = 0;
if(indexPath.row==row)
{
if(switchOn)
{
aContact.givenName = contact.givenName;
aContact.middleName = contact.middleName;
aContact.familyName = contact.familyName;
}
}
if(![contact.organizationName isEqualToString:#""])
{row += 1;
if(indexPath.row==row)//company row
{
//store company
if(switchOn)
aContact.organizationName = contact.organizationName;
}
}
//more code
There may be other solution to solve this issue, above is what I can do for now.
Rather than enumerating all of the cells in a table view, you need to store the state of the switches somewhere other than in the table view's cells. I would imagine that this is already being done, otherwise the state of the switches would not be reliable/consistent just from scrolling the table view.
For instance, in your cellForRowAtIndexPath: method, you must tell the cell whether the switch should be displayed as on or off. You would do this by holding the state of your switches in an instance-level array. Here is one way to do that:
#interface YourClass ()
{
// Create an instance-level array to store switch values.
NSMutableArray *switchValues;
}
...
- (void)viewDidLoad {
switchValues = [NSMutableArray new];
for(int i = 0; i < YOUR_TABLE_SIZE; i++)
[switchValues insertObject:[NSNumber numberWithBool:DEFAULT_SWITCH_STATE]];
// Alternatively, populate your array with another data object if all the
// switches do not have the same default starting state (on/off).
}
...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
YourCustomCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CellIdentifier" forIndexPath:indexPath];
...
// Add a target to the switch so we know when it toggles.
[cell.toggleSwitch addTarget:self action:#selector(switchSwitched:) forControlEvents:UIControlEventValueChanged];
cell.toggleSwitch.tag = indexPath.row;
// Set the state to the appropriate value.
cell.toggleSwitch.on = [[switchValues objectAtIndex:indexPath.row] boolValue];
return cell;
}
- (void)switchSwitched:(UISwitch *)switcher
{
// Toggle the switch's value in the instance array
[switchValues replaceObjectAtIndex:switcher.tag withObject:[NSNumber numberWithBool:switcher.on]];
}

Blur view in TableViewCell

I have created blur view in view controller and added sub view of custom table view cell. The problem is if I select 1st cell, it works perfectly. But if I select 2nd cell, blur view is shown in 3rd cell. If I select 3rd cell, blur view is shown in 5th cell.
- (void)viewDidLoad {
[super viewDidLoad];
[self.tableView registerNib:[UINib nibWithNibName:#"TableViewCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:#"TableViewCell"];
cell = [TableViewCell new];
[self resetBoolArray];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
self.effectView = [[UIVisualEffectView alloc] initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]];
cell = (TableViewCell *) [self.tableView dequeueReusableCellWithIdentifier:#"TableViewCell"];
self.effectView.frame = cell.frame;
[self.effectView setHidden:[[self.boolAry objectAtIndex:indexPath.row] boolValue]];
[cell addSubview:self.effectView];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self resetBoolArray:indexPath.row];
[self.tableView reloadData];
NSLog(#"selected indexpath %ld",indexPath.row);
}
- (void)resetBoolArray {
self.boolAry = [NSMutableArray new];
for (int i = 0; i < 6; i++) {
[self.boolAry addObject:[NSNumber numberWithBool:YES]];
}
}
- (void)resetBoolArray: (NSInteger)indexpath {
self.boolAry = [NSMutableArray new];
for (int i = 0; i < 6; i++) {
if(i == indexpath) {
[self.boolAry addObject:[NSNumber numberWithBool:NO]];}
else{
[self.boolAry addObject:[NSNumber numberWithBool:YES]];}
}
}

Make Change in TableViewCell

I'm working on the TableView, and I have used the label on the TableViewCell and on the button click I want to hide the label from all cells in my table,
In the label I have set the tag:
label.tag = indexPath.row+1;
And on the button click I am using the code like this:
for (int i = 0; i < Array.count; i++)
{
[[self.view viewWithTag:i+1] setHidden:YES];
}
But from my code the label is hiding only from the last cell not all the others.
You can simply do this with another way.
First you need to declare a BOOL in your class
#property(assign,nonatomic) BOOL hideLabels;
Next in your button action handler method, set this YES
In your cellForRowAtIndexPath, check whether hideLabels is YES, if yes, the hide labels using code.
cell.yourLabel.hidden = hideLabels;
Now reload the table after setting hideLabels as YES
[self.tableView reloadData];
for(id object in tableView.subviews)
{
if([object isKindOfClass:[UILabel class]])
{
UILabel *label = (UILabel *) object;
[[label setHidden:YES];
}
}
In your ViewController.h
BOOL isLabelHidden;
in ViewController.m
- (void)viewDidLoad
{
isLabelHidden = FALSE;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"TableCell";
UILabel *lbl;
UITableViewCell *cell = (UITableViewCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell = nil;
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
if(isLabelHidden)
[lbl setHidden:YES];
else
[lbl setHidden:NO];
}
in you button clicked method
- (void)buttonClicked
{
isLabelHidden = TRUE;
[tableView reloadData];
}
You should first get the reference to the UITableViewCell and then you can remove the labels in them.
First of all get the reference to all the cells in your tableview as follows:
NSMutableArray *cells = [[NSMutableArray alloc] init];
for (NSInteger j = 0; j < [tableView numberOfSections]; ++j)
{
for (NSInteger i = 0; i < [tableView numberOfRowsInSection:j]; ++i)
{
[cells addObject:[tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:j]]];
}
}
And now iterate through those cells in cells Array to hide the label view,
for (int i = 0; i < cells.count; i++)
{
[[[cells objectAtIndex:i] viewWithTag:i+1] setHidden:YES];
}
Source:How can I loop through UITableView's cells?

UITableView with open & closed sections

I have a UITableView with several sections.
I would like click on a section to "close/open" its content, in order to show/hide the rows under it. So that, I can keep some sections open (with its rows visible) and others close, with the next section immediately below the previous section header.
How can I implement it? Do I need to subclass the UITableView and add a gesture recognizer and somehow add an animation to the rows? But I'm not sure this is easy...
thanks
Use reloadSections:withRowAnimation to trigger the change;
Provide the updated number of rows in UITableViewDataSource delegate
I can't comment for the question as I don't have enough reputation.
As per my understanding, you want to achieve accordion functionality for your table view. For this, please check this:- effect or animation in UItableVIew and How to implement an accordion view for an iPhone SDK app?
Just subclass sectionHeaderView and define delegate methods like this.
#protocol SectionHeaderViewDelegate <NSObject>
#optional
-(void)sectionHeaderView:(SectionHeaderView*)sectionHeaderView sectionOpened:(NSInteger)section;
-(void)sectionHeaderView:(SectionHeaderView*)sectionHeaderView sectionClosed:(NSInteger)section;
#end
And then in tableViewCOntroller.h
#interface TableViewController : UITableViewController <SectionHeaderViewDelegate>
And in tableViewCOntroller.m
-(void)sectionHeaderView:(SectionHeaderView*)sectionHeaderView sectionOpened:(NSInteger)sectionOpened
{
SectionInfo *sectionInfo = [self.sectionInfoArray objectAtIndex:sectionOpened];
sectionInfo.open = YES;
/*
Create an array containing the index paths of the rows to insert: These correspond to the rows for each quotation in the current section.
*/
NSInteger countOfRowsToInsert = [sectionInfo.play.quotations count];
NSMutableArray *indexPathsToInsert = [[NSMutableArray alloc] init];
for (NSInteger i = 0; i < countOfRowsToInsert; i++) {
[indexPathsToInsert addObject:[NSIndexPath indexPathForRow:i inSection:sectionOpened]];
}
/*
Create an array containing the index paths of the rows to delete: These correspond to the rows for each quotation in the previously-open section, if there was one.
*/
NSMutableArray *indexPathsToDelete = [[NSMutableArray alloc] init];
NSInteger previousOpenSectionIndex = self.openSectionIndex;
if (previousOpenSectionIndex != NSNotFound) {
SectionInfo *previousOpenSection = [self.sectionInfoArray objectAtIndex:previousOpenSectionIndex];
previousOpenSection.open = NO;
[previousOpenSection.headerView toggleOpenWithUserAction:NO];
NSInteger countOfRowsToDelete = [previousOpenSection.play.quotations count];
for (NSInteger i = 0; i < countOfRowsToDelete; i++) {
[indexPathsToDelete addObject:[NSIndexPath indexPathForRow:i inSection:previousOpenSectionIndex]];
}
}
// Style the animation so that there's a smooth flow in either direction.
UITableViewRowAnimation insertAnimation;
UITableViewRowAnimation deleteAnimation;
if (previousOpenSectionIndex == NSNotFound || sectionOpened < previousOpenSectionIndex) {
insertAnimation = UITableViewRowAnimationTop;
deleteAnimation = UITableViewRowAnimationBottom;
}
else {
insertAnimation = UITableViewRowAnimationBottom;
deleteAnimation = UITableViewRowAnimationTop;
}
// Apply the updates.
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:indexPathsToInsert withRowAnimation:insertAnimation];
[self.tableView deleteRowsAtIndexPaths:indexPathsToDelete withRowAnimation:deleteAnimation];
[self.tableView endUpdates];
self.openSectionIndex = sectionOpened;
}
-(void)sectionHeaderView:(SectionHeaderView*)sectionHeaderView sectionClosed:(NSInteger)sectionClosed
{
/*
Create an array of the index paths of the rows in the section that was closed, then delete those rows from the table view.
*/
SectionInfo *sectionInfo = [self.sectionInfoArray objectAtIndex:sectionClosed];
sectionInfo.open = NO;
NSInteger countOfRowsToDelete = [self.tableView numberOfRowsInSection:sectionClosed];
if (countOfRowsToDelete > 0)
{
NSMutableArray *indexPathsToDelete = [[NSMutableArray alloc] init];
for (NSInteger i = 0; i < countOfRowsToDelete; i++) {
[indexPathsToDelete addObject:[NSIndexPath indexPathForRow:i inSection:sectionClosed]];
}
[self.tableView deleteRowsAtIndexPaths:indexPathsToDelete withRowAnimation:UITableViewRowAnimationTop];
}
self.openSectionIndex = NSNotFound;
if ([selectedIndexes count]>0)
{
for(NSIndexPath *indexPath in selectedIndexes)
{
if(sectionClosed == indexPath.section)
{
[sectionInfo.headerView changeOnHighlighted:YES];
break;
}
else
{
[sectionInfo.headerView changeOnHighlighted:NO];
}
}
}
else
{
[sectionInfo.headerView changeOnHighlighted:NO];
}
}
And for further details you can refer this sample project from iOS dev lib..
Here is a simple solution by which you can even create a custom expanded/ collapse view.
here are simple step
1) create a custom view add button over it.
///
join all outlet and create on BOOL variable in view class
#property (weak, nonatomic) IBOutlet UIButton *BtnAction;
#property(assign, nonatomic)BOOL isOpen;
// Create a header where tableview is added and you want it.
here is a simple logic to added as much as you need . I have added which are in headertitle array I wanted it to be dynamic .
NSMutableArray * headerTitle = [NSMutableArray arrayWithObjects:#"Your Order", #"Delivery Address", #"Pay By", nil];
for (NSUInteger index = 0; index<headerTitle.count; index++) {
VGOrderHeader* HeaderView = [[[NSBundle mainBundle] loadNibNamed:#"VGOrderHeader" owner:self options:nil] lastObject];
HeaderView.frame = CGRectMake(0, 0, 32, 40);
HeaderView.BtnAction.tag = index;
if (index == 0) {
HeaderView.isOpen = YES;
HeaderView.lblPlus.text = [NSString stringWithFormat:#"open"];
}
[HeaderView.BtnAction addTarget:self action:#selector(selectSectionToOpen:) forControlEvents:UIControlEventTouchUpInside];
[headerArray addObject:HeaderView];
}
/// Here is header click action.
-(void)selectSectionToOpen:(UIButton *)sender{
for (NSUInteger Increment=0; Increment<headerArray.count; Increment++) {
if (sender.tag == Increment) {
DCOrderHeader* HeaderView= headerArray[Increment];
HeaderView.isOpen = !HeaderView.isOpen;
}
}
// little animation
dispatch_async(dispatch_get_main_queue(), ^{
[UIView transitionWithView:self.tableView
duration:0.55f
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^(void) {
[self.tableView reloadData];
} completion:NULL];
});
}
/// Finally Assign view in header method of table view and provide a height
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 40;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
return [headerArray objectAtIndex:section];
}
// Final Touch
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return headerArray.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
DCOrderHeader* HeaderView = headerArray[section];
if (HeaderView.isOpen == YES) {
return self.someArray.count;
}else{
return 0;
}
}

How do you select uitableview rows programmatically

I am trying to achive a email like select all functionality in uitableview where on same button tap user can checkmark or remove all checkmark and additionally user can also select/deselect rows(on didSelectRowAtIndexPath). I tried to do but its not working properly, here is my code.
- (IBAction)selectAll:(id)sender
{
if(myBoolean)
{
for (NSInteger s = 0; s < self.iTable.numberOfSections; s++)
{
for (NSInteger r = 0; r < [self.iTable numberOfRowsInSection:s]; r++)
{
[[self.iTable cellForRowAtIndexPath:[NSIndexPath indexPathForRow:r inSection:s]] setAccessoryType:UITableViewCellAccessoryNone];
}
}
myBoolean = NO;
[_selectUnselectButton setTitle:#"Select all Friends" forState:UIControlStateNormal];
}
else
{
for (NSInteger s = 0; s < self.iTable.numberOfSections; s++)
{
for (NSInteger r = 0; r < [self.iTable numberOfRowsInSection:s]; r++)
{
[[self.iTable cellForRowAtIndexPath:[NSIndexPath indexPathForRow:r inSection:s]] setAccessoryType:UITableViewCellAccessoryCheckmark];
NSLog(#"%d-%d",s,r);
}
}
myBoolean = YES;
[_selectUnselectButton setTitle:#"Unselect all Friends" forState:UIControlStateNormal];
}
}
-(void)tableView:(UITableView *)tableView_ didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView_ cellForRowAtIndexPath:indexPath];
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
if (cell.accessoryType == UITableViewCellAccessoryCheckmark)
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
else
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
}
I wrote a sample code that I adapted to your needs.
Basically it is
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *unifiedID = #"aCellID";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:unifiedID];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:unifiedID];
}
cell.textLabel.text = [self.states objectAtIndex:indexPath.row];
//if the indexPath was found among the selected ones, set the checkmark on the cell
cell.accessoryType = ([self isRowSelectedOnTableView:tableView atIndexPath:indexPath]) ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *state = [self.states objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if([self isRowSelectedOnTableView:tableView atIndexPath:indexPath]){
[self.selectedCells removeObject:indexPath];
[self.selecedStates removeObject:state];
cell.accessoryType = UITableViewCellAccessoryNone;
} else {
[self.selectedCells addObject:indexPath];
[self.selecedStates addObject:state];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
NSLog(#"%#", self.selecedStates);
}
-(BOOL)isRowSelectedOnTableView:(UITableView *)tableView atIndexPath:(NSIndexPath *)indexPath
{
return ([self.selectedCells containsObject:indexPath]) ? YES : NO;
}
- (IBAction)selectAll:(id)sender {
[self.selecedStates removeAllObjects];
[self.selectedCells removeAllObjects];
NSUInteger numberOfSections = [self.tableView numberOfSections];
for (NSUInteger s = 0; s < numberOfSections; ++s) {
NSUInteger numberOfRowsInSection = [self.tableView numberOfRowsInSection:s];
for (NSUInteger r = 0; r < numberOfRowsInSection; ++r) {
NSIndexPath *idxPath = [NSIndexPath indexPathForRow:r inSection:s];
[self.selectedCells addObject:idxPath];
[self.selecedStates addObject:self.states[idxPath.row]];
}
}
[self.tableView reloadData];
}
- (IBAction)deselectAll:(id)sender {
[self.selecedStates removeAllObjects];
[self.selectedCells removeAllObjects];
[self.tableView reloadData];
}
- (IBAction)toggleAll:(id)sender {
if ([self.states count] == [self.selecedStates count]) {
[sender setTitle:#"select all"];
[self deselectAll:sender];
} else {
[sender setTitle:#"deselect all"];
[self selectAll:sender];
}
}
in action:
You are calling
[[self.iTable cellForRowAtIndexPath:[NSIndexPath indexPathForRow:r inSection:s]] setAccessoryType:UITableViewCellAccessoryNone];
for every row in every section within the tableView. if you have many row, this is ver inefficient, as it will deal with rows not on the screen. But this is not needed. just put every selected index path into an array and tell the tableView to reload. This will reload the visible cells and due to the implementation of -tableView:cellForRowAtIndexPath: cells wfor new rows will be correctly re-conigured.
Setting the accessory view needs to happen inside the tableView:cellForRowAtIndexPath: method. When you want to change the accessories from outside, the outside method needs to change the model first to indicate that check marks must be placed in certain cells, and then call reloadData on the UITableView.
One way to store what cells are checked is an array of NSIndexSet objects - one index set per section. In the example below I show code for a single section, but you should get an idea of how to make multiple sections work.
// This variable needs to be declared in a place where your data source can get it
NSMutableIndexSet *selected;
// You need to initialize it in the designated initializer, like this:
selected = [[NSMutableIndexSet alloc] init];
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
if ([selected containsIndex:indexPath.row]) {
[cell setAccessoryType:UITableViewCellAccessoryCheckmark];
} else {
[cell setAccessoryType:UITableViewCellAccessoryNone];
}
// Do the rest of your code
return cell;
}
Now in the code where you want to set rows selected or unselected you just need to call [selected addIndex:rowToSelect] or [selected removeIndex:rowToUnselect], and call your table's reloadData.
Use selectRowAtIndexPath:animated:scrollPosition: to select a row
and deselectRowAtIndexPath:animated: to deselect a row.
For more read the UITableView docs
Try this code instead of your old one
- (IBAction)selectAll:(id)sender
{
if(myBoolean)
{
for (NSInteger s = 0; s < self.iTable.numberOfSections; s++)
{
for (NSInteger r = 0; r < [self.iTable numberOfRowsInSection:s]; r++)
{
[self.iTable selectRowAtIndexPath:[NSIndexPath indexPathForRow:r inSection:s]] animated:YES scrollPosition:UITableViewScrollPositionNone];
}
}
myBoolean = NO;
[_selectUnselectButton setTitle:#"Select all Friends" forState:UIControlStateNormal];
}
else
{
for (NSInteger s = 0; s < self.iTable.numberOfSections; s++)
{
for (NSInteger r = 0; r < [self.iTable numberOfRowsInSection:s]; r++)
{
[self.iTable selectRowAtIndexPath:[NSIndexPath indexPathForRow:r inSection:s]] animated:YES scrollPosition:UITableViewScrollPositionNone];
NSLog(#"%d-%d",s,r);
}
}
myBoolean = YES;
[_selectUnselectButton setTitle:#"Unselect all Friends" forState:UIControlStateNormal];
}
}
-(void)tableView:(UITableView *)tableView_ didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView_ cellForRowAtIndexPath:indexPath];
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
if (cell.accessoryType == UITableViewCellAccessoryCheckmark)
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
else
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
}
You can follow these steps to implement this,
1) You should have a mutable array to store indexpaths
2) What you can do is, when you tap Check All or Uncheck All, do add or remove all indexpaths to/from array (which you've), reload table for update changes
3) In - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath datasource method, check into array using if([array containsObject:indexPath]), if exist mark it checked or unchecked
4) In - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath delegate method, you should check for already existence of indexpath tapped into array, as you've did in 3rd step, and add or remove indexpaths as per the condition, reload table for update changes
Take another NSMutableArray as SelectedArray
in didSelectRowAtIndexPath row You can Add remove objects from SelectedArray.
You can select a UITableViewCell calling UITableViews selectRowAtIndexPath method:
[yourtable selectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]
animated:NO
scrollPosition:UITableViewScrollPositionTop];
Put for loop for selectedArray to putcheckmark only in selected cells or All cells.
check my accepted answer here : Need to create a select all button for UITableView and add selections to an array in iOS
Xcode 8x
Swift 3x
Select Row
let indexPath = IndexPath(row: 0, section: 0)
mytableView.selectRow(at: indexPath, animated: true, scrollPosition: .bottom)
myTableView.delegate?.tableView!(myTableView, didSelectRowAt: indexPath)
DeSelect Row
let deselectIndexPath = IndexPath(row: 7, section: 0)
myTableView.deselectRow(at: deselectIndexPath, animated: true)
myTableView.delegate?.tableView!(tblView, didDeselectRowAt: indexPath)
you can do something like this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self doSomethingWithRowAtIndexPath:indexPath];
}

Resources