2009-12-22 12 views
13

Qualcuno ha visto un'implementazione di una vista "fisarmonica" (forse chiamata "contorno animato") per l'iPhone? Ho trovato un progetto di esempio per Cocoa, ma prima di provare un porto, speravo che qualcuno avesse già inventato la ruota.Come implementare una vista fisarmonica per un'app iPhone SDK?

Per rendere più chiaro, in un UIView, prendere in considerazione una pila di sezioni, ciascuna contenente un'intestazione, e quindi alcuni contenuti. Quando l'utente tocca l'intestazione (o attraverso qualche messaggio/evento), se la sezione è già aperta => chiudila; se la sezione è chiusa => aprila e chiudi qualsiasi altra sezione aperta. Un esempio in jQuery assomiglia a: http://docs.jquery.com/UI/Accordion

Nel mio caso, vorrei essere in grado di inserire qualsiasi contenuto di UIView in ogni sezione.

Sarei interessato solo a vedere alcune app reali che hanno implementato questo - solo per sapere che è possibile!

+3

Tutto è possibile! –

+0

@Jacob Non tutto è possibile! Vedi http://stackoverflow.com/questions/4962539/creating-generic-method-names-in-generic-class. ;) – jakev

+0

Dai un'occhiata a https://github.com/nacho4d/Accordion Implementa una navigazione in stile fisarmonica per iPad, qui puoi vedere come funziona tutto! – myell0w

risposta

18

Vorrei solo utilizzare un UITableView, rendere l'altezza di ogni cella dipende dal fatto che sia "aperta" o meno e quindi passare da lì. È facile ridimensionare le righe e si potrebbe semplicemente rendere l'altezza totale delle celle combinate l'altezza disponibile in UITableView in modo che assomigli ad una fisarmonica più che a una tabella.

Questo è un trucco veloce che dovrebbe funzionare, ma nel file .h tua UITableViewController di sottoclasse:

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

E nel file .m mettere qualcosa come:

- (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]; 
} 

Questo sarà essenzialmente prendere 4 righe e trasformale in sezioni pieghevoli dove la selezione di una riga la espande a 240 pixel e collassa tutte le altre righe a 40. Puoi cambiare tutti quei numeri e capire le sezioni e fare qualsiasi altra cosa desideri con esso.

Ho provato questo e funziona. Puoi quindi completarlo aggiungendo l'altro contenuto al codice di creazione della cella per aggiungere tutto ciò che desideri in una sezione (incluso eventualmente un UITextView a scorrimento, se lo desideri).

2

Mi sono imbattuto in questo e ho trovato la soluzione di mjdth molto semplice e utile. Tuttavia, si potrebbe desiderare di utilizzare

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

invece del metodo di reloadSections propoesed come le righe di ricarica ti dà una transizione molto più agevole.

1

Ecco la classe CollapsingTableViewDelegate con cui sto attualmente lavorando per farlo. Funziona solo con contenuto statico della tabella.

Fornisci l'implementazione CollapsingTableCellDelegate a questa classe, che deve sapere come calcolare le dimensioni espanse e compresse di ogni riga e come creare un UIView per ogni riga. La vista rimane invariata, sia espansa che espansa, in modo che la porzione superiore della vista di ogni riga funga da intestazione cliccabile di quella riga.

Quindi si rende a questa classe l'origine dati e il delegato per il proprio UITableView.

file di intestazione 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 

e file sorgente 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 

non la perfezione, ma sembra funzionare fondamentalmente per me.

16

Ogni soluzione che ho trovato è stato utilizzando UITableView, che non ha funzionato per me, perché non ho visualizzare i dati tabulari. Questo è il motivo per cui ho creato il controllo AccordionView. L'uso è molto semplice:

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]; 

Nella vita reale assomiglia a questo:

enter image description here

barre nere sono intestazioni e tutto quello che volete (sto usando Three20) è possibile personalizzare.

+0

Ciao Suda, grazie per il controllo. Ho colpito un piccolo intoppo nel usarlo. Ho aperto un problema [qui] (https://github.com/appsome/AccordionView/issues/11) al Github del progetto. Puoi per favore dare un'occhiata? – Isuru

+0

Certo, sto rispondendo in questo momento :) – suda

+0

Ciao suda, grazie mille per il tuo lavoro. Qui ho aperto un piccolo problema. Per favore guarda. https://github.com/appsome/AccordionView/issues/23 –

Problemi correlati