如何实现iPhone SDK应用程序的手风琴视图?

时间:2009-12-22 06:02:26

标签: iphone objective-c iphone-sdk-3.0 uiview

有没有人看到iPhone的“手风琴”(也许称为“动画大纲”)视图的实现?我找到了一个Cocoa的示例项目,但在尝试端口之前,我希望有人已经发明了这个轮子。

为了清楚起见,在UIView中,考虑一堆部分,每个部分包含一个标题,然后是一些内容。当用户触摸标题(或通过某些消息/事件)时,如果该部分已经打开=>关闭它;如果该部分已关闭=>打开它并关闭任何其他开放部分。 jQuery中的一个例子如下: http://docs.jquery.com/UI/Accordion

在我的情况下,我希望能够在每个部分中放置任何UIView内容。

我有兴趣看到一些真正的应用程序实现了这一点 - 只是为了知道它是可能的!

6 个答案:

答案 0 :(得分:18)

我只想使用UITableView,让每个单元格的高度取决于它是否“打开”然后从那里开始。调整行的大小很容易,你可以让组合单元格的总高度为UITableView中的可用高度,这样它就像手风琴而不仅仅是一张桌子。

这是一个应该可行的快速入侵,但是在你的UITableViewController子类的.h文件中:

bool sectionopen[4]; ///or some other way of storing the sections expanded/closed state

在.m文件中输入类似的内容:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 4;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath  {
    if (sectionopen[indexPath.row]) {
        return 240;///it's open
    } else {
        return 45;///it's closed
    }

}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *mycell = [[[UITableViewCell alloc] init] autorelease];
    mycell.textLabel.text= @"Section Name";
    return mycell;
}


- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    ///turn them all off
    sectionopen[0]=NO;
    sectionopen[1]=NO;
    sectionopen[2]=NO;
    sectionopen[3]=NO;

    ///open this one
    sectionopen[indexPath.row]=YES;

    ///animate the opening and expand the row
    [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];
}

这将基本上需要4行并将它们转换为可折叠部分,其中选择一行将其扩展为240像素并将所有其他行折叠为40.您可以更改所有这些数字并找出这些部分并执行其他任何操作我喜欢它。

我已经尝试过了,但它确实有效。然后,您可以通过将其他内容添加到单元格创建代码中来完成它,以便将您想要的任何内容添加到某个部分(如果您愿意,还可以添加滚动的UITextView)。

答案 1 :(得分:16)

我发现的每个解决方案都使用UITableView,这对我不起作用,因为我没有显示表格数据。这就是我创建AccordionView控件的原因。用法非常简单:

AccordionView *accordion = [[AccordionView alloc] initWithFrame:CGRectMake(0, 0, 320, 420)];
[self addSubview:accordion];

// Only height is taken into account, so other parameters are just dummy
UIButton *header1 = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 0, 30)];
[header1.titleLabel setText:@"First row"];

UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 200)];
// ... add subviews to view1

[accordion addHeader:header1 withView:view1];

// ... add more panels

[accordion setSelectedIndex:0];

在现实生活中它看起来像这样:

enter image description here

黑条是标题,您可以随意自定义它们(我使用的是Three20)。

答案 2 :(得分:4)

我找到了这个代码:手风琴的一个简单实现..

https://github.com/kuon/ios-example-accordion

我希望这会对某人有所帮助..

答案 3 :(得分:2)

我偶然发现了这一点,发现mjdth的解决方案非常直接且有用。但是,您可能想要使用

[self.tableView reloadRowsAtIndexPaths: paths withRowAnimation:UITableViewRowAnimationBottom];

而不是propoesed reloadSections方法,因为重新加载行为您提供了更平滑的过渡。

答案 4 :(得分:1)

这是我正在与之合作的CollapsingTableViewDelegate课程。这仅适用于静态表格内容。

您向此类提供CollapsingTableCellDelegate实现,该类必须知道如何计算每行的折叠和扩展大小,以及如何为每行创建UIView。无论是折叠还是展开,视图都保持不变,因此每行视图的顶部条子用作该行的可点击标题。

然后,您将此类作为UITableView的数据源和委托。

标题文件CollapsingTableViewDelegate.h

#import <UIKit/UIKit.h>

@protocol CollapsingTableCellDelegate<NSObject>

@required
- (CGFloat)collapsingCellHeightForRow:(int)row expanded:(BOOL)expanded;
- (UIView *)collapsingCellViewForRow:(int)row;

@optional
- (BOOL)collapsingCellAllowCollapse:(int)row;

@end

struct cell;

@interface CollapsingTableViewDelegate : NSObject <UITableViewDelegate, UITableViewDataSource> {
    id<CollapsingTableCellDelegate> cellDelegate;
    int numCells;
    int currentSelection;
    struct cell *cells;
}

@property (nonatomic, retain, readonly) id<CollapsingTableCellDelegate> cellDelegate;
@property (nonatomic, assign, readonly) int numCells;
@property (nonatomic, assign) int currentSelection;
@property (nonatomic, assign, readonly) struct cell *cells;

- (CollapsingTableViewDelegate *)initWithCellDelegate:(id<CollapsingTableCellDelegate>)delegate numCells:(int)numCells;
- (void)tableView:(UITableView *)tableView touchRow:(int)newSelection;

@end

和源文件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<CollapsingTableCellDelegate>)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

不完美,但似乎基本上适合我。

答案 5 :(得分:1)

如果有人有兴趣,我在这里找到了这个例子。

http://www.cocoanetics.com/2011/03/expandingcollapsing-tableview-sections/