I have a UIViewController with a UITableView which has multiple accessory checkmark implemented into it. My problem is, when I click some cells in the tableview it gets checked but there will be some other cell also checked below. I can view it when I scroll down the tableview. I would only want the cell to be checked whichever the user is clicking and not the extra cells. Please let me know how can I do it. Thanks.
Here is the code:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [someData count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"Cell"];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
cell.textLabel.text=[self.someData objectAtIndex:indexPath.row];
cell.textLabel.font=[UIFont fontWithName:#"Times New Roman" size:11];
cell.textLabel.lineBreakMode = NSLineBreakByWordWrapping;
[cell.textLabel setNumberOfLines:2];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath];
if ([selectedCell accessoryType] == UITableViewCellAccessoryNone) {
[selectedCell setAccessoryType:UITableViewCellAccessoryCheckmark];
NSArray *arrayOfIndexPathsTableOne = [self.tableView indexPathsForSelectedRows];
for (int i = 0; i < [arrayOfIndexPathsTableOne count]; i++) {
NSIndexPath *indexPathImInterestedIn = [arrayOfIndexPathsTableOne objectAtIndex:i];
UITableViewCell *currentCell = [self.tableView cellForRowAtIndexPath:indexPathImInterestedIn];
[saveData addObject:[NSString stringWithFormat:#"%#", currentCell.textLabel.text]];
}
} else {
[selectedCell setAccessoryType:UITableViewCellAccessoryNone];
NSArray *arrayOfIndexPathsTableOne = [self.tableView indexPathsForSelectedRows];
for(int i = 0; i < [arrayOfIndexPathsTableOne count]; i++) {
NSIndexPath *indexPathImInterestedIn = [arrayOfIndexPathsTableOne objectAtIndex:i];
UITableViewCell *currentCell = [self.tableView cellForRowAtIndexPath:indexPathImInterestedIn];
[saveData removeObject:[NSString stringWithFormat:#"%#", currentCell.textLabel.text]];
}
}
[tableView deselectRowAtIndexPath:indexPath animated:NO];
}
This is a cell reuse problem.
To fix, ensure that each time you return a cell from cellForRowAtIndexPath: you always set the selection (checkmark) status. That means explicitly setting it true and false as appropriate.
There are only 2 things you need:
Disable multiple selection on your UITableView.
Implement setSelected:(BOOL)selected in your UITableViewCells and make it select/deselect the cell's sub-views (your checkmarks) accordingly.
When you select table you show check box on it but when you scroll and the cell desapeare and it goes to reusable pool after you scroll back the cell is taken from this pool and it doesn't remember the 'state'.
The solution is create NSMutableArray and in didSelectCellAtIndexPath: method add or remove the indexPath for that cell to the array and in cellForRowAtIndexPath: check this array and show/hide checkmark base on that table.
dont write this line in cell for row at index path
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"Cell"];
it must be in loadview or viewdidload
Related
I am new to Objective C. I am having the UITableView in that I have to select the tableview cell from top to bottom also without selecting the first cell the second cell won't be selected and the user won't able to select the in-between cells also.can anyone help me how to solve this?
I added the example image here if the user selected the first cell the second cell want to enable and if the user selects the second cell the third cell want to enable remaining cells want to disable.
- (void)viewDidLoad {
[super viewDidLoad];
self.colors = [[NSArray alloc] initWithObjects: #"Red", #"Yellow", #"Green",
#"Blue", #"Purpole", nil];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.colors count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"cell";
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.textLabel.text = [self.colors objectAtIndex:indexPath.row];
return cell;
}
Your question is not clear, Hope you have an issue with making multiple cell selection:
Allow UITableView to select multiple cells by tableView.allowsMultipleSelection = true;. And then a user can select any number of cells in the table.
Using this way you can able to select the current row and enable next row for selecting. You need to handle another way to disable others row in cellForRowAtIndexPath method.
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.checkMark.selected = YES;
NSString *cellText = cell.textLabel.text;
if(self.colors.count < indexPath.row+1 ){
NSIndexPath *nextIndexPath = [NSIndexPath indexPathForRow:indexPath.row+1 inSection:0];
UITableViewCell *nextCell = [tableView cellForRowAtIndexPath:nextIndexPath];
nextCell.checkMark.userInteractionEnabled = YES;
//OR
nextCell.userInteractionEnabled = YES;
}
}
I have a tableview which can be expanded on selecting the cell and collapses on selecting again. When you select, the cell should expand and display a label and when you select again it collapses and hides the label . The expanding and collapsing works fine, but if i scroll the tableview after expanding a cell it behaves weird. Once it goes out of the view and comes back , the cell will have the expanded cell's height but the label which is supposed to be shown in expanded cell is hidden.If i select the cell again it collapses and displays the label. I use ,
- (CGFloat)tableView:(UITableView *)t heightForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self tableView:t cellForRowAtIndexPath:indexPath];
if([self cellIsSelected:indexPath])
return cell.frame.size.height+35;
return cell.frame.size.height;
}
- (BOOL)cellIsSelected:(NSIndexPath *)indexPath {
// Return whether the cell at the specified index path is selected or not
NSNumber *selectedIndex = [self.selectedIndexes objectForKey:indexPath];
return selectedIndex == nil ? FALSE : [selectedIndex boolValue];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Deselect cell
NSLog(#"Select cell:%#",indexPath);
[self.tableView deselectRowAtIndexPath:indexPath animated:TRUE];
if([self pickTaskForIndexPath:indexPath].productSpecialMessage){
BOOL isSelected = ![self cellIsSelected:indexPath];
NSNumber *selectedIndex = [NSNumber numberWithBool:isSelected];
[self.selectedIndexes setObject:selectedIndex forKey:indexPath];
PickTaskTableviewCell *cell= [self.tableView cellForRowAtIndexPath:indexPath];
cell.message.hidden=false;
cell.messageLabel.text=[self pickTaskForIndexPath:indexPath].productSpecialMessage;
cell.messageLabel.lineBreakMode=NSLineBreakByTruncatingTail;
cell.messageLabel.numberOfLines=3;
if(cell.messageLabel.hidden==true){
cell.messageLabel.hidden = false;
} else {
cell.messageLabel.hidden = true;
}
NSLog(#"message:%#",cell.messageLabel.text);
[cell layoutIfNeeded];
}
self.tableView.rowHeight=UITableViewAutomaticDimension;
[self.tableView beginUpdates];
[self.tableView endUpdates];
}
indexPath is added to the selectedIndexes on didSelectRowAtIndexPath
Please help me
Cells should be configured only within cellForRowAtIndexPath. When a state change occurs that makes a cell need to look different, just reload that cell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
PickTaskTableviewCell *cell = (PickTaskTableviewCell *)[tableView dequeueReusableCellWithIdentifier:#"cell"];
// everything else you do to configure the cell goes here, then ...
// check the logic here, we want one condition that tells us whether to show the labels
if([[self cellIsSelected:indexPath] && self pickTaskForIndexPath:indexPath].productSpecialMessage){
// don't need these here
//NSNumber *selectedIndex = [NSNumber numberWithBool:isSelected];
// [self.selectedIndexes setObject:selectedIndex forKey:indexPath];
// PickTaskTableviewCell *cell= [self.tableView cellForRowAtIndexPath:indexPath];
cell.message.hidden=false;
cell.messageLabel.text=[self pickTaskForIndexPath:indexPath].productSpecialMessage;
cell.messageLabel.lineBreakMode=NSLineBreakByTruncatingTail;
cell.messageLabel.numberOfLines=3;
cell.messageLabel.hidden=NO;
} else {
cell.message.hidden=YES;
cell.messageLabel.hidden=YES;
}
NSLog(#"message:%#",cell.messageLabel.text);
// don't need this here
// [cell layoutIfNeeded];
return cell;
}
Selection (and presumably deselection) cause the need to update the cell, so...
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// don't deselect it here, just reload it
// more on this later...
[self.selectedIndexes setObject:selectedIndex forKey:indexPath];
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
// probably do the same in didDeselectRowAtIndexPath:
One last (optional) point. There's no need to maintain your own list of selected index paths, UITableView does that for you, so you could delete your selectedIndexes property and just use the table view methods, e.g....
- (BOOL)cellIsSelected:(NSIndexPath *)indexPath {
// Return whether the cell at the specified index path is selected or not
return [[self.tableView indexPathsForSelectedRows] containsObject:indexPath];
}
I'm working on a prototype (I'm way too green on Objective C) which has a tableview with two different custom prototype cells, each with their own set of components, layout and height. The expected behavior is that when selected, the row expands and shows cell type b, and the previously selected row returns to the former height and cell type.
Ok, so the issue I'm having is that I can't get the previously selected row to change it's cell type, the height is working ok so I end up displaying a shorter version of the expanded cell but not having the components and layout of the desired cell type, and if I scroll through the tableview and return to the location where the previously selected row is it displays the correct cell type, as if it hasn't refreshed the rest of the rows.
Here's my code for tableView cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier;
UITableViewCell *cell = nil;
if(indexPath.row == self.selectedIndex){
CellIdentifier = #"ExpandedCell";
CollapsedCell *collapsedCell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell = collapsedCell;
}else{
CellIdentifier = #"LocationCell";
LocationCell *locationCell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
locationCell.transparentBorder.layer.borderWidth = 5.0f;
locationCell.transparentBorder.layer.borderColor = [UIColor colorWithRed:255/255 green:255/255 blue:255/255 alpha:0.7].CGColor;
cell = locationCell;
}
BHPlace *currentPlace = self.data[indexPath.row];
NSString *name = currentPlace.name;
LocationCell *locationCell = (LocationCell*)cell;
locationCell.nameLabel.text = name;
cell.backgroundColor = [UIColor clearColor];
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
return cell;
}
and here's the code for my didSelectRowAtIndexPath:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
[self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:self.selectedIndex inSection:0] animated:YES];
[self.tableView beginUpdates];
self.selectedIndex = (int)indexPath.row;
NSArray *paths = #[indexPath];
[self.tableView reloadRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
[self goToMarker:self.data[indexPath.row]];
}
As usual I'm sure it's a simple issue and there's something I'm not getting right but can't find the right approach,
Thank you all for your time
You just need to reload both the previously selected and the newly selected rows -
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
NSMutableArray *paths=[NSMutableArray new];
if (self.selectedIndexPath != nil) {
[paths addObject:self.selectedIndexPath];
[tableView deselectRowAtIndexPath:self.selectedIndexPath animated:YES];
}
[self.tableView beginUpdates];
self.selectedIndexPath = indexPath;
[paths addObject:indexPath];
[self.tableView reloadRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
[self goToMarker:self.data[indexPath.row]];
}
Note that I changed your int property to an NSIndexPath * self.selectedIndexPath
Trying to implement the checkmark for UITableView.
Checkmark for UITableView Cell is not selecting to all row, when scroll tableview
its not not enable.
Below is my code which i Implemented.
IndexButton is UIButton Class which added index init.
-(void)selectAllAction:(IndexedButton *)sender{
for (int rowIndex = 0; rowIndex < [array_MedicineList count]; rowIndex++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:rowIndex inSection:0];
UITableViewCell *cell = [tbl_ProductList cellForRowAtIndexPath:indexPath];
IndexedButton *btn_SelectItem = (IndexedButton *)[cell viewWithTag:TAG_SELECTEDITEM];
[btn_SelectItem setBackgroundImage:[UIImage imageNamed:#"checkMark"] forState:UIControlStateNormal];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *productListTableViewCell = #"ProductListTableViewCell";
ProductListTableViewCell *cell = (ProductListTableViewCell *)[tableView dequeueReusableCellWithIdentifier:productListTableViewCell];
if (cell == nil){
cell = [[ProductListTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:productListTableViewCell];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
IndexedButton *btn_SelectItem = [IndexedButton buttonWithType:UIButtonTypeCustom];
btn_SelectItem.frame = CGRectMake(10,52,32,32);
[btn_SelectItem setBackgroundImage:[UIImage imageNamed:#"uncheckMark"] forState:UIControlStateNormal];
[btn_SelectItem addTarget:self action:#selector(selectItemAction:)forControlEvents:UIControlEventTouchUpInside];
btn_SelectItem.index = (int)indexPath.row;
btn_SelectItem.tag = TAG_SELECTEDITEM;
[cell addSubview:btn_SelectItem];
}
IndexedButton *btn_SelectItem = (IndexedButton *)[cell viewWithTag:TAG_SELECTEDITEM];
btn_SelectItem.index = (int)indexPath.row;
cell.backgroundColor = [UIColor clearColor];
return cell;
}
#All
Need suggestion, how to go forward to implement the check mark for tableview.
I would suggest you to use cell with accessory view with UITableViewCellAccessoryCheckmark type to show all cells selected/ few cells selected/ none of the cells selected.
Also, you must keep the state for each cell index within a section, whether it's selected or not as
// keeps info for selected rows in a section in mutable index set as
NSMutableIndexSet *selctedCellsInSection;
// initialize the above set instance
selctedCellsInSection = [[NSMutableIndexSet alloc] init];
//Inside cell for row at index path
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
if ([selctedCellsInSection containsIndex:indexPath.row])
[cell setAccessoryType:UITableViewCellAccessoryCheckmark];
else
[cell setAccessoryType:UITableViewCellAccessoryNone];
// customize cell as per your requirements
return cell;
}
You need to hold the information about a cell's checkmark whether it needs to be shown or not in selctedCellsInSection set as -
Use [selctedCellsInSection addIndex:rowToSelect]
// to add cell index on which checkmark needs to be shown
Use [selctedCellsInSection removeIndex:rowToUnselect]
// to add cell index on which checkmark should not be shown
After, customizing the data source selctedCellsInSection(which keeps information about selected/ unselected cell) reload the tableview.
Reloading the table will reflect the selected cells with Cell's Accessory Checkmark.
In your case as you need show check mark on all cell, you can do so as-
-(void)showCheckMarkOnAllCells
{
for (int rowIndex = 0; rowIndex < [array_MedicineList count]; rowIndex++)
{
[selctedCellsInSection addIndex: rowIndex];
}
[tableView reloadData];
}
#interface BRNCategoryViewController ()
{
NSMutableArray *arySelectCategory;
NSMutableArray *aryCategory;
}
- (void) viewDidLoad
{
arySelectCategory=[NSMutableArray new];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return aryCategory.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
BRNCategoryCell *cell=[[BRNCategoryCell alloc]initWithOwner:self];
if ([arySelectCategory containsObject:[aryCategory objectAtIndex:indexPath.row]])
{
cell.imgBoxView.image=[UIImage imageNamed:#"checkMark"];
}
else
{
cell.imgBoxView.image=[UIImage imageNamed:#"uncheckMark"];
}
cell.lblTitle.textColor=Rgb2UIColor(127, 127, 127);
cell.lblTitle.font=[ASCustomClass FontWithSize:20.0];
cell.lblTitle.text=aryCategory[indexPath.row];
cell.backgroundColor=[UIColor clearColor];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
if ([arySelectCategory containsObject:[aryCategory objectAtIndex:indexPath.row]])
{
[arySelectCategory removeObject:[aryCategory objectAtIndex:indexPath.row]];
}
else
{
[arySelectCategory addObject:[aryCategory objectAtIndex:indexPath.row]];
}
[tblEventCategory reloadData];
}
I have a list which I have using as a check boxes. I have enable or disable Check mark on row on select. But when I scroll the list its make mark row after every 10 rows.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *oldCell = [tableView cellForRowAtIndexPath:indexPath];
if (oldCell.accessoryType == UITableViewCellAccessoryCheckmark)
{
oldCell.accessoryType = UITableViewCellAccessoryNone;
}
else
{
oldCell.accessoryType = UITableViewCellAccessoryCheckmark;
}
}
UItableView reuses the cell in every scroll so using condition as per accessory type is not a good practice. You can Create an NSMutableArray with selected items and Check as per the Condition below.
- (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;
}
in didSelectrowAtindexpath method you can Add and remove the Selected items.
Its because UITableView reuses the cell.
So, in the method cellForRowAtIndexPath, you will have to check for a particular cell (of a particular section and row), if it needs to be checked on, provide the accessory type.
If not needed for that cell, provide accessory type as none.
You need to put your logic to set accessory type for cell in cellForRowAtIndexPath, and to identify the cell to mark with check mark you can mark the object in the list in didSelectRowAtIndexPath: or manage an array of selected/unselected objects of the list here.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath];
if ([selectedCell accessoryType] == UITableViewCellAccessoryNone) {
[selectedCell setAccessoryType:UITableViewCellAccessoryCheckmark];
[NSMutableArray addObject:[AnotherMutableArray objectAtIndex:indexPath.row]];
} else {
[selectedCell setAccessoryType:UITableViewCellAccessoryNone];
[NSMutableArray removeObject:[AnotherMutableArray objectAtIndex:indexPath.row]];
}
[tableView deselectRowAtIndexPath:indexPath animated:NO];
}
Also in your viewDidLoad, instantiate you both mutable arrays-
yourmutableArray1 = [[NSMutableArray alloc]init];
yourmutableArray2 = [[NSMutableArray alloc]init];