Как реализовать представление аккордеона для iPhone SDK-приложения?
Кто-нибудь видел реализацию "аккордеона" (возможно, называемого "анимированным контуром" ) для iPhone? Я нашел пример проекта для Cocoa, но прежде чем попробовать порт, я надеялся, что кто-то уже изобрел колесо.
Чтобы было ясно, в UIView рассмотрите стек разделов, каждый из которых содержит заголовок, а затем некоторое содержимое. Когда пользователь касается заголовка (или через какое-либо сообщение/событие), если секция уже открыта = > закрыть ее; если секция закрыта = > откройте ее и закройте любой другой открытый раздел. Пример в jQuery выглядит так:
http://docs.jquery.com/UI/Accordion
В моем случае я хотел бы иметь возможность размещать содержимое UIView в каждом разделе.
Мне было бы интересно увидеть некоторые реальные приложения, которые внедрили это - просто, чтобы знать, что это возможно!
Ответы
Ответ 1
Я бы просто использовал 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 open
} else {
return 45;///it 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, если хотите).
Ответ 2
Каждое найденное мной решение использовало 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).
Ответ 3
Я нашел этот код: простая реализация аккордеона.
https://github.com/kuon/ios-example-accordion
Надеюсь, это поможет кому-то...
Ответ 4
Я просто наткнулся на это и нашел решение mjdth очень прямым и полезным. Однако вы можете использовать
[self.tableView reloadRowsAtIndexPaths: paths withRowAnimation:UITableViewRowAnimationBottom];
вместо метода propoesed reloadSections
, поскольку строки перезагрузки дают вам более плавный переход.
Ответ 5
Здесь класс 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
Не совершенство, но, похоже, в основном работает для меня.
Ответ 6
Я нашел этот пример здесь, если кому-то интересно.
http://www.cocoanetics.com/2011/03/expandingcollapsing-tableview-sections/