2011-01-08 2 views
50

consente di dire che abbiamo un personalizzato UITableViewCellUITableViewCell ampliare al clic

Così ogni volta che clicco pulsante personalizzato sul cellulare .. si dovrebbe estendere al certa misura (si può dire 40 altezza di più ...) e quando clicco di nuovo allo stesso pulsante personalizzato dovrebbe collassare all'altezza precedente.

Developer per favore mi guida .. come posso realizzare questo compito

+0

NOTA: Se si desidera che l'area estesa per contenere nuove cellule cliccabili (righe) (e non solo ha ampliato lo spazio per la cella cliccato), vedere [la risposta di gcamp] (http://stackoverflow.com/a/ 7253288/199364). Inizia con un insieme di * sezioni * che sono chiuse; fai clic sull'intestazione di una sezione per aprire quella sezione, mostrandone le celle. – ToolmakerSteve

risposta

65

Implementare heightForRowAtIndexPath per calcolare l'altezza corretta. Poi nel codice per il pulsante, forzare il tavolo a rivalutare l'altezza di ogni cella con beginUpdates più endUpdates:

[self.tableView beginUpdates]; 
[self.tableView endUpdates]; 

Modifiche altezze delle cellule Tableview verrà automaticamente calcolato con heightForRowAtIndexPath e sarà animato anche le modifiche.

Infatti, invece di un pulsante sulla cella che esegue questa operazione, potresti anche solo selezionare la cella per farlo in didSelectRowAtIndexPath.

8

Ho fatto una componente riutilizzabile che farà esattamente quello che stai parlando. È abbastanza facile da usare, e c'è un progetto demo.

GCRetractableSectionController su GitHub.

+0

Questo è esattamente ciò di cui avevo bisogno. Un UITableview all'interno di UITableviewCell. Perfezionare. Grazie. :) – ScarletWitch

+0

Anche per me, questa era la soluzione perfetta - mi chiedevo come avrei potuto avere l'area espansa contenente ulteriori file di celle cliccabili, piuttosto che essere una singola cella. – ToolmakerSteve

2

Ho usato il codice sorgente di Gcamp e ho creato la mia versione.

1) In un metodo loadView inizializzare un array mutabile in cui si salveranno gli stati espansi o non espansi delle sezioni. È fondamentale per salvare gli stati espansi in un array separato, che non viene distrutto mentre gli scroll della vista tabella (ad esempio se lo si archivia in un headerView verrà ridisegnato e si dimenticherà che il tempo è stato espanso o meno). Nel mio caso è l'array _sectionStatuses.

- (void)loadView 
{ 
    // At the beginning all sections are expanded 
    _sectionStates = [NSMutableArray arrayWithCapacity:self.tableView.numberOfSections]; 
    for (int i = 0; i < self.tableView.numberOfSections; i++) { 
     _sectionStates[i] = [NSNumber numberWithBool:YES]; 
    } 
} 

2) Creare un'intestazione personalizzata per una sezione con un pulsante per l'espansione. Delega un'azione da un pulsante nell'intestazione Visualizza sul TableViewController utilizzando lo schema di delega. Puoi trovare immagini adatte nel codice sorgente di Gcamp.

3) Creare un'azione per rimuovere o aggiungere righe. Qui _foldersArray è la mia struttura, che contiene tutti i dati. L'header della mia vistaView - MCExpandableAccountHeaderView conosce il proprio numero di sezione - lo trasferisco lì quando creo le viste di intestazione per ogni sezione. È fondamentale trasferirlo su questo metodo, poiché è necessario sapere quale sezione è ora espansa o estesa.

- (void)expandClicked:(MCAccountHeaderView *)sender 
{ 
MCExpandableAccountHeaderView *expandableAccountHeaderView = (MCExpandableAccountHeaderView*)sender; 

// Finding a section, where a button was tapped 
NSInteger section = expandableAccountHeaderView.section; 

// Number of rows, that must be in a section when it is expanded 
NSUInteger contentCount = [_foldersArray[section - 1][@"folders"] count]; 

// Change a saved status of a section 
BOOL expanded = [_sectionStates[section] boolValue]; 
expanded = ! expanded; 
expandableAccountHeaderView.expanded = expanded; 
_sectionStates[section] = [NSNumber numberWithBool:expanded]; 

// Animation in a table 
[self.tableView beginUpdates]; 

NSMutableArray* modifiedIndexPaths = [[NSMutableArray alloc] init]; 
for (NSUInteger i = 0; i < contentCount; i++) { 
    NSIndexPath* indexPath = [NSIndexPath indexPathForRow:i inSection:section]; 
    [modifiedIndexPaths addObject:indexPath]; 
} 

if (expandableAccountHeaderView.expanded) [self.tableView insertRowsAtIndexPaths:modifiedIndexPaths withRowAnimation:UITableViewRowAnimationFade]; 
else [self.tableView deleteRowsAtIndexPaths:modifiedIndexPaths withRowAnimation:UITableViewRowAnimationFade]; 

[self.tableView endUpdates]; 

// Scroll to the top of current expanded section 
if (expandableAccountHeaderView.expanded) [self.tableView scrollToRowAtIndexPath:INDEX_PATH(0, section) atScrollPosition:UITableViewScrollPositionTop animated:YES]; 
} 

4) E 'inoltre importante ritornare numero o righe corretto in una sezione seconda wheather viene espanso o meno.

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
{ 
    BOOL expanded = [_sectionStates[section] boolValue]; 

    return expanded ? [_foldersArray[section - 1][@"folders"] count] : 0; 
} 
92

Non ho intenzione di dire nulla qui per contraddire la risposta accettata considerando che è perfettamente corretto. Tuttavia, ho intenzione di andare più nel dettaglio su come realizzare questo. Se non vuoi leggere tutto questo e sei più interessato a giocare con il codice sorgente in un progetto di lavoro, ho caricato uno example project to GitHub.

L'idea di base è quella di avere una condizione all'interno del metodo -tableView: heightForRowAtIndexPath: che determina se la cella corrente debba essere espansa o meno. Questo verrà attivato chiamando gli aggiornamenti di inizio/fine sulla tabella dall'interno di -tableView: didSelectRowAtIndexPath: In questo esempio, mostrerò come creare una vista tabella che consenta l'espansione di una cella alla volta.

La prima cosa che devi fare è dichiarare un riferimento a un oggetto NSIndexPath. È possibile farlo come vuoi, ma raccomando di usare una dichiarazione di proprietà come questa:

@property (strong, nonatomic) NSIndexPath *expandedIndexPath; 

NOTA: Non è necessario creare questo percorso indice all'interno viewDidLoad, o qualsiasi altro metodo simile. Il fatto che l'indice sia inizialmente nullo significherà solo che inizialmente la tabella non avrà una riga espansa. Se si preferisce la tabella di iniziare con una fila di vostra scelta espanso, si potrebbe aggiungere qualcosa di simile per il metodo di viewDidLoad:

NSInteger row = 1; 
NSInteger section = 2; 
self.expandedIndexPath = [NSIndexPath indexPathForRow:row inSection:section]; 

Il passo successivo è quello di testa su oltre al UITableViewDelegate metodo -tableView: didSelectRowAtIndexPath: per aggiungere la logica per modificare l'indice di cella espansa in base alla selezione degli utenti. L'idea qui è di controllare il percorso dell'indice che è stato appena selezionato rispetto al percorso dell'indice memorizzato all'interno della variabile expandedIndexPath. Se i due sono una corrispondenza, allora sappiamo che l'utente sta cercando di deselezionare la cella espansa, nel qual caso, impostiamo la variabile su zero. Altrimenti, impostiamo la variabile expandedIndexPath all'indice appena selezionato. Tutto ciò avviene tra le chiamate a beginUpdates/endUpdates, per consentire alla vista tabella di gestire automaticamente l'animazione di transizione.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    [tableView beginUpdates]; // tell the table you're about to start making changes 

    // If the index path of the currently expanded cell is the same as the index that 
    // has just been tapped set the expanded index to nil so that there aren't any 
    // expanded cells, otherwise, set the expanded index to the index that has just 
    // been selected. 
    if ([indexPath compare:self.expandedIndexPath] == NSOrderedSame) { 
     self.expandedIndexPath = nil; 
    } else { 
     self.expandedIndexPath = indexPath; 
    } 

    [tableView endUpdates]; // tell the table you're done making your changes 
} 

Poi il passo finale è in un altro metodo UITableViewDelegate-tableView: heightForRowAtIndexPath:. Questo metodo verrà chiamato dopo aver attivato beginUpdates una volta per ogni percorso dell'indice che la tabella determina l'esigenza di aggiornamento. Qui è dove si confronta il expandedIndexPath con il percorso dell'indice che viene attualmente rivalutato.

Se i due percorsi di indice sono uguali, questa è la cella che si desidera espandere, altrimenti l'altezza dovrebbe essere normale. Ho usato i valori 100 e 44, ma puoi usare quello che si adatta alle tue esigenze.

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    // Compares the index path for the current cell to the index path stored in the expanded 
    // index path variable. If the two match, return a height of 100 points, otherwise return 
    // a height of 44 points. 
    if ([indexPath compare:self.expandedIndexPath] == NSOrderedSame) { 
     return 100.0; // Expanded height 
    } 
    return 44.0; // Normal height 
} 
+0

Potrebbe spiegare come la vista tabella gestisce l'animazione nella nuova versione in cui non vi è nulla tra 'beginUpdates' e' endUpdates'? Grazie – Leo

+0

dovrebbe essere la risposta accettata! bella risposta! Il progetto – fabersky

9

Ho creato una libreria open source per questo. Implementa semplicemente la compressione ed espandi i delegati nel tuo codice e voilà! puoi anche eseguire disegni e animazioni. controlla this.

enter image description here

+2

potrebbe essere migliorato se si sottoclassi le celle tableview anziché utilizzare viewWithTag – SleepsOnNewspapers

+0

HVTableView ha un nuovo aggiornamento. ora viene fornito anche su cocoapod. –

0
initialize iSelectedIndex = -1; and declare 
UITableView *urTableView; 

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ 

return 10; //Section count 

} 

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
{ 

return 3; //row count 

} 

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 

static NSString *CellIdentifier = @"Cell"; 

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 

if(cell == nil) 
{ 
    cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; 

} 

[cell.textLabel setText:[NSString stringWithFormat:@"sec:%d,row:%d",indexPath.section,indexPath.row]]; 

return cell; 

} 


- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{ 

// adding a label with the tap gesture to the header in each section 

headerLabel = [[UILabel alloc]init]; 

headerLabel.tag = section; 

headerLabel.userInteractionEnabled = YES; 

headerLabel.backgroundColor = [UIColor greenColor]; 

headerLabel.text = [NSString stringWithFormat:@"Header No.%d",section]; 

headerLabel.frame = CGRectMake(0, 0, tableView.tableHeaderView.frame.size.width, tableView.tableHeaderView.frame.size.height); 

UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(gestureTapped:)]; 

[headerLabel addGestureRecognizer:tapGesture]; 

return headerLabel; 

} 

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{ 

return 50.0; //adjust the height as you need 

} 

- (void)gestureTapped:(UITapGestureRecognizer *)sender{ 

UIView *theSuperview = self.view; // whatever view contains 

CGPoint touchPointInSuperview = [sender locationInView:theSuperview]; 

UIView *touchedView = [theSuperview hitTest:touchPointInSuperview withEvent:nil]; 

if([touchedView isKindOfClass:[UILabel class]]) 
{ 

    if (iSelectedIndex != touchedView.tag) { //if new header is selected , need to expand 

     iSelectedIndex = touchedView.tag; 

    }else{ // if the header is already expanded , need to collapse 

     iSelectedIndex = -1; 

    } 

    [urTableView beginUpdates]; 

    [urTableView endUpdates]; 

} 

} 

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 

// Show or hide cell 

float height = 0.0; 

if (indexPath.section == iSelectedIndex) { 

    height = 44.0; // Show the cell - adjust the height as you need 

} 

return height; 

} 
+3

Basta non pubblicare molto codice. Prova a spiegarlo. :) – Rashad

8

Invece di usare [tableView beginUpdates] e [tableView endUpdates], sto usando il metodo [tableView reloadRowsAtIndexPath:... withRowAnimation:...] all'interno del metodo didSelectRowAtIndexPath.

Preferisco questo, perché ho avuto alcuni problemi con gli elementi che dovrebbero mostrare, quando espongo il mio UITableViewCell, quando ho usato i metodi di aggiornamento degli aggiornamenti iniziali &. Un altro punto è che si può scegliere tra alcune animazioni come: superiore, inferiore, sinistra, destra ...

+0

Ottima visione per un miglior controllo sulle animazioni! –

0

Per aggiungere alla rispostadi 0x7fffffff, ho trovato avevo bisogno di una condizione supplementare nel if all'interno didSelectRowAtIndexPath - così:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
{ 

    [tableView beginUpdates]; 

    if (self.expandedIndexPath && [indexPath compare:self.expandedIndexPath] == NSOrderedSame) { 
     self.expandedIndexPath = nil; 
    } else { 
     self.expandedIndexPath = indexPath; 
    } 

    [tableView endUpdates]; 

} 
0

Questa è la risposta di Mick ma per Swift 4. (IndexPath sostituisce NSIndexPath, che viene fornito con un IndexPath vuoto come nulla sarebbe bloccarsi Swift Inoltre, il confronto metodo è ora differente..Non è più possibile utilizzare NSOrderedSame)

Dichiarare la proprietà expandIndexPath.

var expandedIndexPath = IndexPath() 

viewDidLoad parte opzionale.

expandedIndexPath = IndexPath(row: 1, section: 2) 

Quindi la parte didSelectRow.

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 
    tableView.beginUpdates() 

    if indexPath.compare(expandedIndexPath) == .orderedSame { 
     expandedIndexPath = IndexPath() 
    } else { 
     expandedIndexPath = indexPath 
    } 

    tableView.endUpdates() 
} 

Quindi la parte heightForRow.

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 
    if indexPath.compare(expandedIndexPath) == .orderedSame { 
     return 100 
    } 

    return 44 
}