expandable list view in iOS

11,653

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.

screenshot

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.

Share:
11,653

Related videos on Youtube

Crazed'n'Dazed
Author by

Crazed'n'Dazed

Asking questions since 2013. iOS and Web.

Updated on August 21, 2022

Comments

  • Crazed'n'Dazed
    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
    Crazed'n'Dazed about 11 years
    thanks.. but I've already gone through this.. it lifts data from a plist, not from an array
  • art-divin
    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
    canhazbits about 11 years
    Also you can modify the sample to read from an Array rather than the plist.