Select one row in each section of UITableView ios?
Scenario:
I made 2 sections in one UITableView and the user needs to select a row in each section as shown in the screenshot below.
Expected result: 1. The user should be able to select a row in each section
Result right now: 1. After I select a row in one section and then when I select a row in the second section, the first selection disappears.
Here is my code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Uncheck the previous checked row
long sec=indexPath.section;
if(sec==0){
if(self->checkedIndexPath)
{
UITableViewCell* uncheckCell = [tableView
cellForRowAtIndexPath:self->checkedIndexPath];
uncheckCell.accessoryType = UITableViewCellAccessoryNone;
}
if([self->checkedIndexPath isEqual:indexPath])
{
self->checkedIndexPath = nil;
}
else
{
UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
self->checkedIndexPath = indexPath;
}}
if(sec==1){
if(self->checkedIndexPath)
{
UITableViewCell* uncheckCell = [tableView
cellForRowAtIndexPath:self->checkedIndexPath];
uncheckCell.accessoryType = UITableViewCellAccessoryNone;
}
if([self->checkedIndexPath isEqual:indexPath])
{
self->checkedIndexPath = nil;
}
else
{
UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
self->checkedIndexPath = indexPath;
}
}
}
Help is appreciated.
This is the easiest way. I finally found a solution. This works for me, hope it works for you. declare these
@interface ViewController ()
{
int selectedsection;
NSMutableArray *selectedindex;
}
Replace didSelectRowAtIndexPath as follows:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Uncheck the previous checked row
NSIndexPath *selectedIndexPath = [tableView indexPathForSelectedRow];
if(self.checkedIndexPath)
{
for (int i=0; i<[selectedindex count]; i++) {
NSIndexPath *temp= [selectedindex objectAtIndex:i];
if (temp.section==selectedIndexPath.section) {
UITableViewCell* uncheckCell = [tableView
cellForRowAtIndexPath:temp];
uncheckCell.accessoryType = UITableViewCellAccessoryNone;
}
}
NSInteger numb= [tableView numberOfRowsInSection:selectedIndexPath.section];
if (selectedsection==selectedIndexPath.section) {
UITableViewCell* uncheckCell = [tableView
cellForRowAtIndexPath:self.checkedIndexPath];
uncheckCell.accessoryType = UITableViewCellAccessoryNone;
}
}
if([self.checkedIndexPath isEqual:indexPath])
{
for (int i=0; i<[selectedindex count]; i++) {
NSIndexPath *temp= [selectedindex objectAtIndex:i];
if (temp.section==selectedIndexPath.section) {
UITableViewCell* uncheckCell = [tableView
cellForRowAtIndexPath:temp];
uncheckCell.accessoryType = UITableViewCellAccessoryNone;
}
}
self.checkedIndexPath = nil;
}
else
{
for (int i=0; i<[selectedindex count]; i++) {
NSIndexPath *temp= [selectedindex objectAtIndex:i];
if (temp.section==selectedIndexPath.section) {
UITableViewCell* uncheckCell = [tableView
cellForRowAtIndexPath:temp];
uncheckCell.accessoryType = UITableViewCellAccessoryNone;
}
}
UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
self.checkedIndexPath = indexPath;
[selectedindex addObject:indexPath];
selectedsection=indexPath.section;
NSLog(@"check");
}
}
source to share
I wrote a sample code where a composite data source stores data source objects for each section. It sounds complicated, but it actually makes it easy to extend the architecture. And keeps your view controller small.
The advantages of this approach:
- Small ViewController
- ViewController configures the view and handles user interaction - as it should in MVC
- Reusable data sources
- using different data sources in a section it is easy to customize cells for each section
basic data source architecture
This makes it easy to expand and easy to reuse.
@import UIKit;
@interface ComoundTableViewDataSource : NSObject
@property (nonatomic,strong, readonly) NSMutableDictionary *internalDictionary;
-(void) setDataSource:(id<UITableViewDataSource>)dataSource forSection:(NSUInteger)section;
-(instancetype)initWithTableView:(UITableView *)tableView;
@end
#import "ComoundTableViewDataSource.h"
@interface ComoundTableViewDataSource () <UITableViewDataSource>
@property (nonatomic,strong, readwrite) NSMutableDictionary *internalDictionary;
@property (nonatomic, weak) UITableView *tableView;
@end
@implementation ComoundTableViewDataSource
-(instancetype)initWithTableView:(UITableView *)tableView
{
self = [super init];
if (self) {
_tableView = tableView;
tableView.dataSource = self;
_internalDictionary = [@{} mutableCopy];
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"Cell"];
}
return self;
}
-(void)setDataSource:(id<UITableViewDataSource>)dataSource forSection:(NSUInteger)section
{
self.internalDictionary[@(section)] = dataSource;
[self.tableView reloadData];
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[self.internalDictionary allKeys] count];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id<UITableViewDataSource> sectionDataSource = self.internalDictionary[@(section)];
return [sectionDataSource tableView:tableView numberOfRowsInSection:section];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
id<UITableViewDataSource> sectionDataSource = self.internalDictionary[@(indexPath.section)];
return [sectionDataSource tableView:tableView cellForRowAtIndexPath:indexPath];
}
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
id<UITableViewDataSource> sectionDataSource = self.internalDictionary[@(section)];
return [sectionDataSource tableView:tableView titleForHeaderInSection:section];
}
@end
@import UIKit;
@interface SingleSectionDataSource : NSObject <UITableViewDataSource>
@property (nonatomic, strong, readonly) NSArray *array;
@property (nonatomic, strong, readonly) UITableView *tableView;
- (instancetype)initWithArray:(NSArray *)array;
@end
#import "SingleSectionDataSource.h"
@interface SingleSectionDataSource ()
@property (nonatomic, strong, readwrite) NSArray *array;
@property (nonatomic, strong, readwrite) UITableView *tableView;
@end
@implementation SingleSectionDataSource
- (instancetype)initWithArray:(NSArray *)array
{
self = [super init];
if (self) {
self.array = array;
}
return self;
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
self.tableView = tableView;
return self.array.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
cell.textLabel.text = self.array[indexPath.row];
return cell;
}
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return [@(section) stringValue];
}
@end
selection data source architecture
We are extending the classes from above to allow one selection per section
#import "ComoundTableViewDataSource.h"
@interface OnSelectionPerSectionComoundTableViewDataSource : ComoundTableViewDataSource
-(void)selectedCellAtIndexPath:(NSIndexPath *)indexPath;
@end
#import "OnSelectionPerSectionComoundTableViewDataSource.h"
#import "SingleSelectionSingleSectionDataSource.h"
@implementation OnSelectionPerSectionComoundTableViewDataSource
-(instancetype)initWithTableView:(UITableView *)tableView
{
self = [super initWithTableView:tableView];
if(self){
[tableView setAllowsMultipleSelection:YES];
}
return self;
}
-(void)selectedCellAtIndexPath:(NSIndexPath *)indexPath
{
SingleSelectionSingleSectionDataSource *sectionDataSource = self.internalDictionary[@(indexPath.section)];
[sectionDataSource selectedCellAtIndexPath:indexPath];
}
@end
View controller implementation
As promised, a very thin controller:
@interface ViewController () <UITableViewDelegate>
@property (weak, nonatomic) IBOutlet UITableView *tableView;
@property (nonatomic, strong) OnSelectionPerSectionComoundTableViewDataSource *tableViewDataSource;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.tableViewDataSource = [[OnSelectionPerSectionComoundTableViewDataSource alloc] initWithTableView:self.tableView];
self.tableView.delegate = self;
[self.tableViewDataSource setDataSource:[[SingleSelectionSingleSectionDataSource alloc] initWithArray:@[@"Hallo", @"Welt"]] forSection:0];
[self.tableViewDataSource setDataSource:[[SingleSelectionSingleSectionDataSource alloc] initWithArray:@[@"Hello", @"World", @"!"]] forSection:1];
[self.tableViewDataSource setDataSource:[[SingleSelectionSingleSectionDataSource alloc] initWithArray:@[@"Hola", @"Mundo", @"!", @"¿Que tal?"]] forSection:2];
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.tableViewDataSource selectedCellAtIndexPath:indexPath];
}
@end
You need to add methods to data sources to get the selected rows.
take an example: https://github.com/vikingosegundo/CompoundDatasourceExample
Note This code has a cell reuse issue. It is pinned to GitHub.
source to share