Expandable and Collapsible TableView Sections

Discussion in 'iOS Programming' started by RagingGoat, Jan 9, 2013.

  1. RagingGoat macrumors 6502

    Joined:
    Jun 21, 2010
    #1
    I have a sectioned table view that will show events for each month. Each section is a month and I'm wanting to make each section expandable and collapsible to minimize the amount of scrolling the user has to do to get to the info they want. Can someone help point me in the right direction? Here is what I have so far.

    Code:
    //
    //  Events.m
    //  KFBNewsroom
    //
    //  Created by Adam Rayborn on 12/12/12.
    //  Copyright (c) 2012 com.kfb. All rights reserved.
    //
    
    
    #import "Events.h"
    #import "CustomCellBackground.h"
    
    @interface Events ()
    
    @end
    
    @implementation Events
    {
        NSMutableArray *jan;
        NSMutableArray *feb;
        NSMutableArray *mar;
        NSMutableArray *apr;
        NSMutableArray *may;
        NSMutableArray *june;
        NSMutableArray *july;
        NSMutableArray *aug;
        NSMutableArray *sept;
        NSMutableArray *oct;
        NSMutableArray *nov;
        NSMutableArray *dec;
    }
    
    - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
    {
        self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
        if (self) {
            // Custom initialization
            self.title = NSLocalizedString(@"Events", @"Events");
        }
        return self;
    }
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        // Do any additional setup after loading the view from its nib.
        jan = [NSMutableArray arrayWithObjects:@"3\n\n113th Congress Convenes\nWashington, D.C.", @"6-8\n\nAmerican Forage & Grassland Council Annual Conference\nMarriott River Center\nCovington, KY", @"7-8\n\nJoint Meeting: KY State Horticulture Society, KY Vegetable Growers Association, KY Vineyard Society, KY Farmers Market Association\nEmbassy Suites\nLexington, KY", @"8\n\nKY General Assembly Part I Convenes\nFrankfort, KY", @"10-12\n\nKentucky Cattlemen's Association & Ag Industry Convention & Trade Show\nConvention Center\nLexington, KY", @"11\n\nKentucky Horse Council Annual Meeting\nConvention Center\nLexington, KY", @"13-16\n\nAFBF 94th Annual Meeting\nNashville, TN", @"16\n\nAFBF Board & Affiliated Meetings\nNashville, TN", @"18\n\nKentucky Commodity Conference\nHoliday Inn\nBowling Green, KY", @"18-19\n\nKentucky Pork Producers Association Annual Meeting\nHoliday Inn\nBowling Green, KY", @"21-22\n\nLEAD Meeting\nLexington, KY", @"22\n\nOrientation for New Directors\nState Office", @"23\n\nBoard of Directors Meetings: KFBF, KFB Investment Corporation, KFB Education Foundation, KFB Management Corporation\nStateOffice", @"23-25\n\nMid States Horticulture Expo\nKYInternational Convention Center\nLouisville, KY", @"24\n\nKFB Mutual Insurance Company Board of Directors Meeting\nState Office", @"24-25\n\nKentucky Landscape Industries Conference\nKYInternational Convention Center\nLouisville, KY", @"25-26\n\nYoung Farmer Leadership Conference\nMarriott Downtown\nLouisville, KY", @"30\n\nAgency Support & Marketing Tip Off Meeting\nRupp Arena\nLexington, KY", @"30\n\nAg Expo\nHines Center\nOwensboro, KY", nil];
    }
    
    - (void)didReceiveMemoryWarning
    {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    
    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
    {
        // Return the number of sections.
        return 12;
    }
    
    - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
    {
        if (section == 0)
        {
            return @"January";
        }
        else if (section == 1)
        {
            return @"February";
        }
        else if (section == 2)
        {
            return @"March";
        }
        else if (section == 3)
        {
            return @"April";
        }
        else if (section == 4)
        {
            return @"May";
        }
        else if (section == 5)
        {
            return @"June";
        }
        else if (section == 6)
        {
            return @"July";
        }
        else if (section == 7)
        {
            return @"August";
        }
        else if (section == 8)
        {
            return @"September";
        }
        else if (section == 9)
        {
            return @"October";
        }
        else if (section == 10)
        {
            return @"November";
        }
        else if (section == 11)
        {
            return @"December";
        }
        return 0;
    }
    
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        if (section == 0)
        {
            return [jan count];
        }
        else if (section == 1)
        {
            return [feb count];
        }
        else if (section == 2)
        {
            return [mar count];
        }
        else if (section == 3)
        {
            return [apr count];
        }
        else if (section == 4)
        {
            return [may count];
        }
        else if (section == 5)
        {
            return [june count];
        }
        else if (section == 6)
        {
            return [july count];
        }
        else if (section == 7)
        {
            return [aug count];
        }
        else if (section == 8)
        {
            return [sept count];
        }
        else if (section == 9)
        {
            return [oct count];
        }
        else if (section == 10)
        {
            return [nov count];
        }
        else if (section == 11)
        {
            return [dec count];
        }
        return 0;
    }
    
    -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        return 175;
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *mbTableIdentifier = @"SimpleTableItem";
        
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:mbTableIdentifier];
        
        if (cell == nil)
        {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:mbTableIdentifier];
            cell.textLabel.font=[UIFont systemFontOfSize:16.0];
        }
        
        cell.backgroundView = [[CustomCellBackground alloc] init];
        cell.selectedBackgroundView = [[CustomCellBackground alloc] init];
        cell.textLabel.backgroundColor = [UIColor clearColor];
        cell.textLabel.highlightedTextColor = [UIColor darkGrayColor];
        cell.textLabel.lineBreakMode = NSLineBreakByWordWrapping;
        cell.textLabel.numberOfLines = 0;
    
        
        cell.textLabel.text = [jan objectAtIndex:indexPath.row];
        return cell;
    }
    
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        
    }
    
    
    @end
    
     
  2. TheWatchfulOne macrumors 6502

    TheWatchfulOne

    Joined:
    Jun 19, 2009
    #2
    I'm interested in implementing this myself in the future but I haven't actually done it yet. But I have an idea what I would try.

    Based on what I've seen while quickly skimming over what you have so far, I would do this:

    1. Declare an array of boolean values. Each index in the array corresponds to one of your sections.

    2. In your
    Code:
    tableView:numberOfRowsInSection
    method, return either the actual number of rows or return 0 depending on the result of testing the coresponding BOOL value.

    3. To change the values in the boolean array, I image you would add a button of some type to each the section header. When the button is tapped you need to get the index for the section you are in and toggle the boolean value that coorelates to that section.

    4. Then, refresh the tableView. I expect that it will automatically animate the expanding and collapsing of the section. I expect this because I've gotten similar results from a tableView implementation I have that dynamically removes a tableViewCell when there is no data to be displayed in it. That is, when I add a record and then refresh the tableView, the addition of the cell is animated as if it were expanding to make room for the cell. I know animation comes free in several UIView controls and I did not write any animation code.

    5. Come back and let us know if it works.

    I want to try this in my own app but I have more important things to do to it before I can start on this functionality.

    Also you could improve your
    Code:
    titleForHeaderInSection
    method by storing the months in an array and when the method gets called, make it return the value at the corresponding index.

    For the
    Code:
    numberOfRowsInSection 
    method, I would store each of your arrays that represent a month into another array and then return either the count of the array at the corresponding index (or 0 dending on the section's expanded/collapsed state).

    I'm not at my Mac right now, so I can't post any sample code until later.

    And if somebody more knowlegeable than me has any ideas, I'd be gald to hear them!:cool:
     
  3. KoolStar macrumors demi-god

    KoolStar

    Joined:
    Oct 16, 2006
    Location:
    Kentucky
    #4
    Just fyi you may want to switch your massive if statement group to a simple switch statement it may make it a bit cleaner and easier to read. As for the coconetics post thats a great tutorial on how to do expanding and contracting tableviews.

    In the simplest term. You could have the custom headerview be a button and when pressed you change the number of rows for the section that they header belongs to to zero. Thus collapsing the section. If they click it again you can return the proper number of rows, thus expanding the section.
     
  4. RagingGoat thread starter macrumors 6502

    Joined:
    Jun 21, 2010
    #5
    Yes, pretty much just like that. I'm wondering though if it's possible to make the headers I currently have be expandable instead of putting them in what appears to be a normal table view cell. I'll look at that code in more detail later though.

    ----------

    Thanks for the advise. I'm going to go though that tutorial a little later today.
     
  5. KoolStar macrumors demi-god

    KoolStar

    Joined:
    Oct 16, 2006
    Location:
    Kentucky
    #6
    If you did want to use the header as the actually button, you would need to implement the method for the tableview:

    Code:
    - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
    
    This will allow you to make a custom view that looks similar to the standard tableview header. On this view you can add a custom button that is the same frame as the header view. You can set the buttons title label to the delegate method for the tableview. You can then use the tableview datasource that you already have for the titles to get the text for the button.

    Code:
    [self tableView:tableView titleForHeaderInSection:section];
    
    Then you can add a target/action to the button in the header view that will fire the logic to change the number of rows returned for that section.
     

Share This Page