Подтвердить что ты не робот

Как реализовать представление аккордеона для iPhone SDK-приложения?

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

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

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

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

4b9b3361

Ответ 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).

Ответ 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

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