How to implement an accordion view for an iPhone SDK app?

前端 未结 6 2091
一整个雨季
一整个雨季 2020-12-02 13:41

Has anyone seen an implementation of an \"accordion\" (maybe called \"animated outline\") view for the iPhone? I found an example project for Cocoa, but before trying a port

6条回答
  •  南方客
    南方客 (楼主)
    2020-12-02 14:17

    Here's the CollapsingTableViewDelegate class that I'm currently working with to do this. This only works with static table content.

    You supply the CollapsingTableCellDelegate implementation to this class, which must know how to compute the collapsed and expanded sizes of each row, and how to create a UIView for each row. The view stays the same whether collapsed or expanded, so that the top sliver of each row's view serves as that row's clickable header.

    You then make this class the datasource and delegate for your UITableView.

    Header file CollapsingTableViewDelegate.h:

    #import 
    
    @protocol CollapsingTableCellDelegate
    
    @required
    - (CGFloat)collapsingCellHeightForRow:(int)row expanded:(BOOL)expanded;
    - (UIView *)collapsingCellViewForRow:(int)row;
    
    @optional
    - (BOOL)collapsingCellAllowCollapse:(int)row;
    
    @end
    
    struct cell;
    
    @interface CollapsingTableViewDelegate : NSObject  {
        id cellDelegate;
        int numCells;
        int currentSelection;
        struct cell *cells;
    }
    
    @property (nonatomic, retain, readonly) id cellDelegate;
    @property (nonatomic, assign, readonly) int numCells;
    @property (nonatomic, assign) int currentSelection;
    @property (nonatomic, assign, readonly) struct cell *cells;
    
    - (CollapsingTableViewDelegate *)initWithCellDelegate:(id)delegate numCells:(int)numCells;
    - (void)tableView:(UITableView *)tableView touchRow:(int)newSelection;
    
    @end
    

    and source file CollapsingTableViewDelegate.m:

    #import "CollapsingTableViewDelegate.h"
    
    @implementation CollapsingTableViewDelegate
    
    struct cell {
        u_char expanded;
        u_char collapsable;
    };
    
    @synthesize cellDelegate;
    @synthesize currentSelection;
    @synthesize cells;
    @synthesize numCells;
    
    #pragma mark -
    #pragma mark Setup and Teardown
    
    - (CollapsingTableViewDelegate *)initWithCellDelegate:(id)delegate numCells:(int)num {
        if ([super init] == nil)
            return nil;
        if ((cells = calloc(num, sizeof(*cells))) == NULL) {
            [self autorelease];
            return nil;
        }
        cellDelegate = [delegate retain];
        numCells = num;
        for (int row = 0; row < self.numCells; row++) {
            struct cell *const cell = &self.cells[row];
    
            cell->collapsable = ![self.cellDelegate respondsToSelector:@selector(collapsingCellAllowCollapse:)]
              || [self.cellDelegate collapsingCellAllowCollapse:row];
            cell->expanded = !cell->collapsable;
        }
        currentSelection = -1;
        return self;
    }
    
    - (void)dealloc {
        [cellDelegate release];
        free(cells);
        [super dealloc];
    }
    
    - (void)tableView:(UITableView *)tableView reloadRow:(int)row fade:(BOOL)fade {
        [tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:row inSection:0]]
                         withRowAnimation:fade ? UITableViewRowAnimationFade : UITableViewRowAnimationNone];
    }
    
    - (void)tableView:(UITableView *)tableView touchRow:(int)newSelection {
    
        // Sanity check
        if (newSelection < -1 || newSelection >= self.numCells) {
            NSLog(@"CollapsingTableViewDelegate: invalid row %d not in the range [-1..%d)", newSelection, self.numCells);
            return;
        }
    
        // Gather info
        int oldSelection = self.currentSelection;
        BOOL sameCellSelected = newSelection == oldSelection;
        struct cell *const oldCell = oldSelection != -1 ? &self.cells[oldSelection] : NULL;
        struct cell *const newCell = newSelection != -1 ? &self.cells[newSelection] : NULL;
    
        // Mark old cell as collapsed and new cell as expanded
        if (newCell != NULL)
            newCell->expanded = TRUE;
        if (oldCell != NULL)
            oldCell->expanded = FALSE;
        self.currentSelection = sameCellSelected ? -1 : newSelection;
    
        // Update table view
        if (oldSelection >= newSelection) {
            if (oldSelection != -1)
                [self tableView:tableView reloadRow:oldSelection fade:sameCellSelected];
            if (newSelection != -1 && !sameCellSelected)
                [self tableView:tableView reloadRow:newSelection fade:TRUE];
        } else {
            if (newSelection != -1 && !sameCellSelected)
                [self tableView:tableView reloadRow:newSelection fade:TRUE];
            if (oldSelection != -1)
                [self tableView:tableView reloadRow:oldSelection fade:sameCellSelected];
        }
    
        // If expanding a cell, scroll it into view
        if (newSelection != -1 && !sameCellSelected) {
            [tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:newSelection inSection:0]
                             atScrollPosition:UITableViewScrollPositionTop
                                     animated:TRUE];
        }
    }
    
    #pragma mark -
    #pragma mark Table view data source
    
    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
        return 1;
    }
    
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
        return self.numCells;
    }
    
    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
        int row = [indexPath row];
        struct cell *const cell = &self.cells[row];
        return [self.cellDelegate collapsingCellHeightForRow:row expanded:cell->expanded];
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        int row = [indexPath row];
        UIView *cellView = [self.cellDelegate collapsingCellViewForRow:row];
        [cellView removeFromSuperview];
        UITableViewCell *tvcell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil] autorelease];
        [tvcell.contentView addSubview:cellView];
        tvcell.clipsToBounds = TRUE;
        tvcell.selectionStyle = UITableViewCellSelectionStyleNone;
        return tvcell;
    }
    
    #pragma mark -
    #pragma mark Table view delegate
    
    - (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
        int row = [indexPath row];
        struct cell *const cell = &self.cells[row];
        return cell->collapsable ? indexPath : nil;
    }
    
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)newSelection {
        [tableView deselectRowAtIndexPath:newSelection animated:TRUE];
        [self tableView:tableView touchRow:[newSelection row]];
    }
    
    @end
    

    Not perfection, but seems to basically work for me.

提交回复
热议问题