expandable list view in iOS
Solution 1
This sample may be what you're looking for: http://developer.apple.com/library/ios/#samplecode/TableViewUpdates/Introduction/Intro.html
The sample pulls from static data, but you should be able to update it to use dynamic data from your DB, and also to give the tasks for a date, etc.
Solution 2
JKExpandTableView is an open source library implementing an expandable table view (subclass of UITableView) and should help you: http://jackkwok.github.io/JKExpandTableView/
Check out the sample app.
Solution 3
The thing that I needed is working now, so I'm gonna post the code here:
- (void)viewDidLoad
{
[super viewDidLoad];
BOOL flag = NO;
allDates = [[NSMutableArray alloc]init];
self.date = [[NSMutableArray alloc] init];
// self.date = [[NSMutableArray alloc]init];
self.listOfSections_DataSource = [[NSMutableArray alloc]init];
sqlite3_stmt *statement;
const char *dbpath = [mDatabasePath UTF8String];
if (sqlite3_open(dbpath,&mDiary)== SQLITE_OK) {
NSString *selectSQL = [NSString stringWithFormat:@"SELECT * FROM TODO"];
const char *query_stmt = [selectSQL UTF8String];
if (sqlite3_prepare_v2(mDiary,
query_stmt, -1, &statement, NULL) == SQLITE_OK)
{
while(sqlite3_step(statement) == SQLITE_ROW)
{
tempTaskName = [[NSString alloc]initWithUTF8String:(const char *) sqlite3_column_text(statement, 2)];
[allDates addObject:[[NSString alloc]initWithUTF8String:(const char *) sqlite3_column_text(statement, 0)]];
//[self.date addObject:tempTaskName];
}
}
sqlite3_finalize(statement);
}
NSLog(@"%i",[allDates count]);
sqlite3_close(mDiary);
self.date = [NSMutableArray arrayWithCapacity:[allDates count]];
for(int p = 0; p < [allDates count]; p++)
{
for(int q = p+1; q < [allDates count]; q++)
{
if([allDates[p] isEqualToString:allDates[q]])
{
flag = YES;
break;
}
}
if(flag)
{
//[self.date addObject:allDates[p]];
[allDates removeObjectAtIndex:p];
}
flag = NO;
}
// NSLog(@"%u",[self.date count]);
// [allDates setArray:self.date];
NSLog(@"allDates count%i",[allDates count]);
for(int x = 0;x < [allDates count];x++)
{
if (sqlite3_open(dbpath,&mDiary)== SQLITE_OK) {
NSString *selectSQL = [NSString stringWithFormat:@"SELECT * FROM TODO"];
const char *query_stmt = [selectSQL UTF8String];
if (sqlite3_prepare_v2(mDiary,
query_stmt, -1, &statement, NULL) == SQLITE_OK){
temp = [[NSMutableArray alloc]init];
while(sqlite3_step(statement) == SQLITE_ROW)
{
tempTaskName = [[NSString alloc]initWithUTF8String:(const char *) sqlite3_column_text(statement, 2)];
NSLog(@"%@",tempTaskName);
if([[[NSString alloc]initWithUTF8String:(const char *) sqlite3_column_text(statement, 0)] isEqualToString:allDates[x]])
{
[self.temp addObject:tempTaskName];
}
}
NSLog(@"task name%@",temp);
[listOfSections_DataSource addObject:temp];
} sqlite3_finalize(statement);
}
sqlite3_close(mDiary);
}
//[self.listOfSections_DataSource addObject:allDates];
NSMutableArray *emptyArray = [[NSMutableArray alloc]init];
listOfSections = [[NSMutableArray alloc]init];
for (int i = 0;i<[allDates count]; i++) {
[listOfSections addObject:emptyArray];
}
self.selectedSection = -1;
self.selectedSectionTail = -1;
}
The table view methods:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [listOfSections_DataSource count];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if(selectedSection !=section)
return 1;
else{
NSArray *array = [listOfSections objectAtIndex:section];
return [array count];
}
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
for(int y = 0;y<[allDates count]; y++)
{
if (section == y) {
return allDates[y];
}
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"cell";
if(self.selectedSection == indexPath.section){//this is just to check to see if this section is the one we touched
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// cell.textLabel.text = [[listOfSections_DataSource objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
NSArray *myArray = [listOfSections_DataSource objectAtIndex:indexPath.section];//tasks
NSString* myString = [myArray objectAtIndex:indexPath.row]; // get task for that row, under the date
cell.textLabel.text = myString;
return cell;
}
else{//THIS IS going to happen the first time
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = @"more";
return cell;
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell * cell = [tableView cellForRowAtIndexPath:indexPath];
//check to see if the cell is for exapnding or for selection
if([cell.textLabel.text isEqualToString:@"more"]){ // this means we need to expand
listOfSections = [[NSMutableArray alloc]init];
//setting up the list of section with empty arrays
NSMutableArray *emptyArray = [[NSMutableArray alloc]init];
for (int i = 0;i<[allDates count]; i++) {
[listOfSections addObject:emptyArray];
}
//Add array of tasks here
[listOfSections replaceObjectAtIndex:indexPath.section withObject:[listOfSections_DataSource objectAtIndex:indexPath.section]];
int tapedRow = [indexPath section];
self.selectedSection = tapedRow;
NSMutableIndexSet *myIndexSet = [[NSMutableIndexSet alloc]init];
[myIndexSet addIndex:selectedSection];
[myIndexSet addIndex:selectedSectionTail];
// Updating section in the tableview
if(selectedSectionTail!=-1)
[taskTable reloadSections:(NSIndexSet*)myIndexSet withRowAnimation:UITableViewRowAnimationFade];
else {
[taskTable reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:
UITableViewRowAnimationFade];
}
//[taskTable reloadData];
}
else{
}
selectedSectionTail = selectedSection;
}
I know there must be much better ways of doing this, but this solution worked for me, so I decided to post it.
Related videos on Youtube
Comments
-
Crazed'n'Dazed almost 2 years
I need to create expandable list view in my iOS app, ie. if you tap on a cell, it should expand to give more cells. I'm following the example given on this link: Toggle showing/hiding child table cells iOS The code for the implementation file is given here too: http://pastie.org/7756763
However, in the example above the arrays are fixed. I have a sqlite database where I store tasks and their start dates, among other things. I need to create an expandable view where the different rows are the different start dates and when I tap on a date, it should give the tasks for that date. Here's the code:
- (void)viewDidLoad { [super viewDidLoad]; allDates = [[NSMutableArray alloc]init]; self.date = [[NSMutableArray alloc] initWithObjects: nil]; // self.date = [[NSMutableArray alloc]init]; self.listOfSections_DataSource = [[NSMutableArray alloc]initWithObjects:nil]; sqlite3_stmt *statement; const char *dbpath = [mDatabasePath UTF8String]; if (sqlite3_open(dbpath,&mDiary)== SQLITE_OK) { NSString *selectSQL = [NSString stringWithFormat:@"SELECT * FROM TODO"]; const char *query_stmt = [selectSQL UTF8String]; if (sqlite3_prepare_v2(mDiary, query_stmt, -1, &statement, NULL) == SQLITE_OK) { while(sqlite3_step(statement) == SQLITE_ROW) { tempTaskName = [[NSString alloc]initWithUTF8String:(const char *) sqlite3_column_text(statement, 2)]; [allDates addObject:[[NSString alloc]initWithUTF8String:(const char *) sqlite3_column_text(statement, 0)]]; [self.date addObject:tempTaskName]; } } sqlite3_finalize(statement); } sqlite3_close(mDiary); NSLog(@"%i",[allDates count]); for(int x = 0;x < [allDates count];x++) { if (sqlite3_open(dbpath,&mDiary)== SQLITE_OK) { NSString *selectSQL = [NSString stringWithFormat:@"SELECT * FROM TODO"]; const char *query_stmt = [selectSQL UTF8String]; if (sqlite3_prepare_v2(mDiary, query_stmt, -1, &statement, NULL) == SQLITE_OK){ temp = [[NSMutableArray alloc]init]; while(sqlite3_step(statement) == SQLITE_ROW) { tempTaskName = [[NSString alloc]initWithUTF8String:(const char *) sqlite3_column_text(statement, 2)]; NSLog(@"%@",tempTaskName); // [allDates addObject:[[NSString alloc]initWithUTF8String:(const char *) sqlite3_column_text(statement, 0)]]; if([[[NSString alloc]initWithUTF8String:(const char *) sqlite3_column_text(statement, 0)] isEqualToString:allDates[x]]) { [self.temp addObject:tempTaskName]; } } NSLog(@"task name%@",temp); [listOfSections_DataSource addObject:temp]; } sqlite3_finalize(statement); } sqlite3_close(mDiary); } //[self.listOfSections_DataSource addObject:allDates]; NSMutableArray *emptyArray = [[NSMutableArray alloc]init]; listOfSections = [[NSMutableArray alloc]init]; for (int i = 0;i<[allDates count]; i++) { [listOfSections addObject:emptyArray]; } self.selectedSection = -1; self.selectedSectionTail = -1; }
the table view methods are:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return [listOfSections_DataSource count]; } -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { if(selectedSection !=section) return 1; else{ NSArray *array = [listOfSections objectAtIndex:section]; return [array count]; } } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"cell"; if(self.selectedSection == indexPath.section){//this is just to check to see if this section is the one we touched UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; } NSArray *myArray = [listOfSections_DataSource objectAtIndex:indexPath.section];//this now contains your cities NSString* myString = [myArray objectAtIndex:indexPath.row]; // get city for that row, under the state cell.textLabel.text = myString; return cell; } else{//THIS IS going to happen the first time UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; } cell.textLabel.text = @"more"; return cell; } } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell * cell = [tableView cellForRowAtIndexPath:indexPath]; //check to see if the cell is for exapnding or for selection if([cell.textLabel.text isEqualToString:@"more"]){ // this means we need to expand listOfSections = [[NSMutableArray alloc]init]; //setting up the list of section with empty arrays NSMutableArray *emptyArray = [[NSMutableArray alloc]init]; for (int i = 0;i<[allDates count]; i++) { [listOfSections addObject:emptyArray]; } //Add array of tasks here [listOfSections replaceObjectAtIndex:indexPath.section withObject:[listOfSections_DataSource objectAtIndex:indexPath.section]]; int tapedRow = [indexPath section]; self.selectedSection = tapedRow; NSMutableIndexSet *myIndexSet = [[NSMutableIndexSet alloc]init]; [myIndexSet addIndex:selectedSection]; [myIndexSet addIndex:selectedSectionTail]; // Updating section in the tableview if(selectedSectionTail!=-1) [taskTable reloadSections:(NSIndexSet*)myIndexSet withRowAnimation:UITableViewRowAnimationFade]; else { [taskTable reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation: UITableViewRowAnimationFade]; } //[taskTable reloadData]; } else{ } selectedSectionTail = selectedSection; }
The problem here is that: 1. No. of sections that shows on screen is correct but when the first cell is tapped, it disappears. 2. the list doesn't get expanded when any of the cells is tapped.
Please can anyone help me out? Thanks..
-
Crazed'n'Dazed about 11 yearsthanks.. but I've already gone through this.. it lifts data from a plist, not from an array
-
art-divin about 11 years@Crazed'n'Dazed to use .plist you have to load it with NSDictionary, and I suppose NSDictionary has some kind of NSArray inside in the provided example. You should check it again
-
canhazbits about 11 yearsAlso you can modify the sample to read from an Array rather than the plist.