objective-c 如何为 iPhone SDK 应用程序实现手风琴视图?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1944428/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to implement an accordion view for an iPhone SDK app?
提问by raf
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, I was hoping that someone has invented the wheel already.
有没有人见过 iPhone 的“手风琴”(可能称为“动画大纲”)视图的实现?我找到了 Cocoa 的示例项目,但在尝试移植之前,我希望有人已经发明了轮子。
To make it clear, in a UIView, consider a stack of sections, each containing a header, and then some contents. When the user touches the header (or through some message/event), if the section is already open => close it; if the section is closed => open it and close any other open section. An example in jQuery looks like: http://docs.jquery.com/UI/Accordion
为了清楚起见,在 UIView 中,考虑一堆部分,每个部分包含一个标题,然后是一些内容。当用户触摸标题(或通过一些消息/事件)时,如果该部分已经打开 => 关闭它;如果该部分已关闭 => 打开它并关闭任何其他打开的部分。jQuery 中的示例如下所示:http: //docs.jquery.com/UI/Accordion
In my case, I'd want to be able to put any UIView contents in each section.
就我而言,我希望能够在每个部分中放置任何 UIView 内容。
I'd be interested in just seeing some real apps who have implemented this - just to know it's possible!
我只是想看看一些已经实现了这个的真实应用程序 - 只是为了知道这是可能的!
回答by mjdth
I would just use a UITableView, make each cell's height depend on whether or not it's "open" and then go from there. It's easy to resize the rows and you could just make the total height for the combined cells be the available height in the UITableView so that it looks like an accordion more than just a table.
我只会使用 UITableView,使每个单元格的高度取决于它是否“打开”,然后从那里开始。调整行的大小很容易,您可以将组合单元格的总高度设置为 UITableView 中的可用高度,这样它看起来就像一个手风琴而不仅仅是一张桌子。
This is a quick hack that should work, but in your UITableViewController subclass's .h file:
这是一个应该有效的快速技巧,但在您的 UITableViewController 子类的 .h 文件中:
bool sectionopen[4]; ///or some other way of storing the sections expanded/closed state
And in the .m file put something like:
在 .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];
}
This will basically take 4 rows and turn them into collapsable sections where selecting one row will expand it to 240 pixels and collapse all other rows to 40. You can change all of those numbers and figure out the sections and do whatever else you'd like with it.
这将基本上需要 4 行并将它们变成可折叠的部分,其中选择一行会将其扩展为 240 像素并将所有其他行折叠为 40。您可以更改所有这些数字并找出这些部分并做任何其他您想做的事情用它。
I've tried this out and it works. You can then complete it by adding the other content to your cell creation code to add whatever you'd like to a section (including possibly a scrolling UITextView if you'd like).
我已经试过了,它的工作原理。然后,您可以通过将其他内容添加到您的单元格创建代码来完成它,以将您想要的任何内容添加到一个部分(如果您愿意,可能包括滚动 UITextView)。
回答by suda
Every solution that I found was using UITableView, which didn't work for me, because I didn't display tabular data. This is why I created AccordionViewcontrol. Usage is pretty straightforward:
我找到的每个解决方案都使用 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];
In real life it looks like this:
在现实生活中,它看起来像这样:


Black bars are headers and you can customize them all you want (I'm using Three20).
黑条是标题,您可以根据需要自定义它们(我使用的是 Three20)。
回答by Dilip Rajkumar
I found this code: A simple implementation of accordion..
我找到了这个代码:A simple implementation of Accordion ..
https://github.com/kuon/ios-example-accordion
https://github.com/kuon/ios-example-accordion
I hope this will help some one..
我希望这会帮助某人..
回答by wallyB
I just stumbled across this and found mjdth's solution very straight forward and helpful. However, you might want to use
我只是偶然发现了这个,发现 mjdth 的解决方案非常直接和有帮助。但是,您可能想要使用
[self.tableView reloadRowsAtIndexPaths: paths withRowAnimation:UITableViewRowAnimationBottom];
instead of the propoesed reloadSectionsmethod as the reload rows gives you a much smoother transition.
而不是建议的reloadSections方法,因为重新加载行为您提供了更平滑的过渡。
回答by Archie
Here's the CollapsingTableViewDelegateclass that I'm currently working with to do this. This only works with static table content.
这CollapsingTableViewDelegate是我目前正在与之合作的班级。这仅适用于静态表格内容。
You supply the CollapsingTableCellDelegateimplementation to this class, which must know how to compute the collapsed and expanded sizes of each row, and how to create a UIViewfor 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.
您CollapsingTableCellDelegate向此类提供实现,该类必须知道如何计算每行的折叠和展开大小,以及如何UIView为每行创建一个。无论是折叠还是展开,视图都保持不变,因此每行视图的顶部条带用作该行的可点击标题。
You then make this class the datasource and delegate for your UITableView.
然后,您将此类设为数据源并为您的UITableView.
Header file CollapsingTableViewDelegate.h:
头文件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
and source file CollapsingTableViewDelegate.m:
和源文件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
Not perfection, but seems to basically work for me.
不完美,但似乎基本上对我有用。
回答by Jimbalaya
I found this example here if anyone is interested.
如果有人感兴趣,我在这里找到了这个例子。
http://www.cocoanetics.com/2011/03/expandingcollapsing-tableview-sections/
http://www.cocoanetics.com/2011/03/expandingcollapsing-tableview-sections/

