Как реализовать вид аккордеона для приложения iPhone SDK? - PullRequest
13 голосов
/ 22 декабря 2009

Кто-нибудь видел реализацию представления "аккордеон" (возможно, называемого "анимированный контур") для iPhone? Я нашел пример проекта для Какао, но перед тем, как попробовать порт, я надеялся, что кто-то уже изобрел колесо.

Чтобы было ясно, в UIView рассмотрим стек разделов, каждый из которых содержит заголовок, а затем некоторое содержимое. Когда пользователь касается заголовка (или через какое-то сообщение / событие), если раздел уже открыт => закрыть его; если раздел закрыт => откройте его и закройте любой другой открытый раздел. Пример в jQuery выглядит так: http://docs.jquery.com/UI/Accordion

В моем случае я хотел бы иметь возможность помещать любое содержимое UIView в каждый раздел.

Мне было бы интересно посмотреть на некоторые реальные приложения, которые реализовали это - просто чтобы знать, что это возможно!

Ответы [ 6 ]

18 голосов
/ 22 декабря 2009

Я бы просто использовал UITableView, чтобы высота каждой ячейки зависела от того, открыта она или нет, а затем пошла бы оттуда. Изменить размер строк легко, и вы можете просто сделать общую высоту для объединенных ячеек доступной высотой в UITableView, чтобы она выглядела как аккордеон, а не просто таблица.

Это быстрый хак, который должен работать, но в файле .h вашего подкласса UITableViewController:

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).

16 голосов
/ 20 декабря 2011

В каждом решении, которое я нашел, использовался 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).

4 голосов
/ 04 июня 2012

Я нашел этот код: простая реализация аккордеона ..

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

Надеюсь, это кому-нибудь поможет.

2 голосов
/ 15 марта 2010

Я просто наткнулся на это и нашел решение MJDTH очень простым и полезным. Тем не менее, вы можете использовать

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

вместо предложенного метода reloadSections, поскольку строки перезагрузки обеспечивают более плавный переход.

1 голос
/ 29 ноября 2011

Я нашел этот пример здесь, если кому-то интересно.

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

1 голос
/ 22 июня 2010

Вот класс 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

Не совершенство, но, кажется, в основном работает для меня.

...