Puoi animare un cambio di altezza su un UITableViewCell quando selezionato?


393

Sto usando UITableViewun'app sul mio iPhone e ho un elenco di persone che appartengono a un gruppo. Mi piacerebbe che quando l'utente fa clic su una determinata persona (selezionando così la cella), la cella aumenta in altezza per visualizzare diversi controlli dell'interfaccia utente per la modifica delle proprietà di quella persona.

È possibile?

Risposte:


879

Ho trovato una soluzione DAVVERO SEMPLICE a questo come effetto collaterale su UITableViewcui stavo lavorando .....

Memorizza l'altezza della cella in una variabile che riporta normalmente l'altezza originale tramite il tableView: heightForRowAtIndexPath:, quindi quando vuoi animare un cambio di altezza, cambia semplicemente il valore della variabile e chiama questo ...

[tableView beginUpdates];
[tableView endUpdates];

Scoprirai che non esegue un ricaricamento completo, ma è sufficiente per UITableViewsapere che deve ridisegnare le celle, afferrando il nuovo valore di altezza per la cella .... e indovina un po '? ANIMA il cambiamento per te. Dolce.

Ho una spiegazione più dettagliata e esempi di codice completo sul mio blog ... Animate UITableView Modifica altezza cella


6
È brillante. Ma c'è un modo per controllare la velocità di animazione della tabella?
Josh Kahane,

7
Funziona ma se ingrandisco una cella diciamo da 44 a 74 e poi la ridimensiono di nuovo a 44, la linea di separazione si comporta in modo del tutto strano. Qualcuno può confermarlo?
plaetzchen,

46
Questa è una soluzione bizzarra, ma è ciò che Apple consiglia anche nella sessione "Mastering Table View" del WWDC 2010. Ho intenzione di presentare una segnalazione di bug sull'aggiunta di questo alla documentazione perché ho appena trascorso circa 2 ore a cercare.
bpapa,

5
Ho provato questa soluzione e funziona solo a volte, quando si preme una cella c'è il 50% di probabilità di funzionare. Qualcuno ha lo stesso bug? È perché iOS7?
Joan Cardona,

7
Ora è anche scritto nella documentazione ufficiale: You can also use this method followed by the endUpdates method to animate the change in the row heights without reloading the cell. developer.apple.com/library/ios/documentation/UIKit/Reference/…
Jaroslav,

63

Mi piace la risposta di Simon Lee. In realtà non ho provato quel metodo ma sembra che cambierebbe la dimensione di tutte le celle dell'elenco. Speravo in un cambiamento solo della cella che viene sfruttata. L'ho fatto come Simon, ma con solo una piccola differenza. Questo cambierà l'aspetto di una cella quando è selezionata. E si anima. Solo un altro modo per farlo.

Crea un int per contenere un valore per l'indice di cella selezionato corrente:

int currentSelection;

Poi:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    int row = [indexPath row];
    selectedNumber = row;
    [tableView beginUpdates];
    [tableView endUpdates];
}

Poi:

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

    if ([indexPath row] == currentSelection) {
        return  80;
    }
    else return 40;


}

Sono sicuro che puoi apportare modifiche simili in tableView: cellForRowAtIndexPath: per cambiare il tipo di cella o persino caricare un file xib per la cella.

In questo modo, currentSelection inizierà da 0. Dovresti apportare modifiche se non desideri che la prima cella dell'elenco (all'indice 0) appaia selezionata per impostazione predefinita.


2
Controlla il codice allegato al mio post, ho fatto esattamente questo, raddoppiando l'altezza per una cella quando selezionato. :)
Simon Lee,

8
"In realtà non ho provato quel metodo, ma sembra che cambierebbe la dimensione di tutte le celle dell'elenco" - quindi non hai fatto molta figura.
Jamie,

13
La selezione corrente è già memorizzata in tableView.indexPathForSelectedRow.
Nick,

22

Aggiungi una proprietà per tenere traccia della cella selezionata

@property (nonatomic) int currentSelection;

Impostalo su un valore sentinella in (ad esempio) viewDidLoad, per assicurarti che UITableViewinizi nella posizione 'normale'

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    //sentinel
    self.currentSelection = -1;
}

In heightForRowAtIndexPathpuoi impostare l'altezza desiderata per la cella selezionata

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    int rowHeight;
    if ([indexPath row] == self.currentSelection) {
        rowHeight = self.newCellHeight;
    } else rowHeight = 57.0f;
    return rowHeight;
}

In didSelectRowAtIndexPathsi salva la selezione corrente e si salva un'altezza dinamica, se necessario

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
        // do things with your cell here

        // set selection
        self.currentSelection = indexPath.row;
        // save height for full text label
        self.newCellHeight = cell.titleLbl.frame.size.height + cell.descriptionLbl.frame.size.height + 10;

        // animate
        [tableView beginUpdates];
        [tableView endUpdates];
    }
}

In didDeselectRowAtIndexPathreimpostare l'indice di selezione sul valore sentinella e animare la cella in forma normale

- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {       
        // do things with your cell here

        // sentinel
        self.currentSelection = -1;

        // animate
        [tableView beginUpdates];
        [tableView endUpdates];
    }
}

Grazie, grazie, grazie! Ho aggiunto un po 'di codice per rendere la cella attivabile. Ho aggiunto il codice qui sotto.
Septronic,

14

reloadData non va bene perché non c'è animazione ...

Questo è quello che sto provando attualmente:

NSArray* paths = [NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:0]];
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView deleteRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];

Funziona quasi bene. Quasi. Sto aumentando l'altezza della cella e a volte c'è un piccolo "singhiozzo" nella vista tabella mentre la cella viene sostituita, come se si conservasse una posizione di scorrimento nella vista tabella, la nuova cella (che è la prima cella nella tabella) finisce con un offset troppo alto e la vista di scorrimento rimbalza per riposizionarla.


Personalmente ho scoperto che l'utilizzo di questo metodo ma con UITableViewRowAnimationNone fornisce un risultato più fluido ma comunque non perfetto.
Ron Srebro,

11

Non so cosa siano tutte queste cose su come chiamare beginUpdates / endUpdates in successione, puoi semplicemente usare -[UITableView reloadRowsAtIndexPaths:withAnimation:]. Ecco un esempio di progetto .


Eccellente, questo non allunga le visualizzazioni del testo nella mia cella di layout automatico. Ma deve avere uno sfarfallio nell'animazione perché l'opzione di animazione Nessuno sembra glitch durante l'aggiornamento delle dimensioni della cella.
h3dkandi,

10

Ho risolto con reloadRowsAtIndexPaths.

Salvo in didSelectRowAtIndexPathindexPath della cella selezionata e chiamo reloadRowsAtIndexPathsalla fine (è possibile inviare NSMutableArray per l'elenco degli elementi che si desidera ricaricare).

In heightForRowAtIndexPathè possibile verificare se indexPath è nell'elenco o meno delle celle espandiIndexPath e inviare l'altezza.

È possibile controllare questo esempio di base: https://github.com/ferminhg/iOS-Examples/tree/master/iOS-UITableView-Cell-Height-Change/celdascambiadetam È una soluzione semplice.

aggiungo una specie di codice se ti aiuto

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 20;
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath: (NSIndexPath*)indexPath
{
    if ([indexPath isEqual:_expandIndexPath])
        return 80;

    return 40;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"Celda";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    [cell.textLabel setText:@"wopwop"];

    return cell;
}

#pragma mark -
#pragma mark Tableview Delegate Methods

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    NSMutableArray *modifiedRows = [NSMutableArray array];
    // Deselect cell
    [tableView deselectRowAtIndexPath:indexPath animated:TRUE];
    _expandIndexPath = indexPath;
    [modifiedRows addObject:indexPath];

    // This will animate updating the row sizes
    [tableView reloadRowsAtIndexPaths:modifiedRows withRowAnimation:UITableViewRowAnimationAutomatic];
}


3

Prova questo per espandere la riga indicizzata:

@property (nonatomic) NSIndexPath *expandIndexPath;
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath
{
if ([indexPath isEqual:self.expandedIndexPath])
    return 100;

return 44;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSMutableArray *modifiedRows = [NSMutableArray array];
if ([indexPath isEqual:self.expandIndexPath]) {
    [modifiedRows addObject:self.expandIndexPath];
    self.expandIndexPath = nil;
} else {
    if (self.expandedIndexPath)
        [modifiedRows addObject:self.expandIndexPath];

    self.expandIndexPath = indexPath;
    [modifiedRows addObject:indexPath];
}

// This will animate updating the row sizes
[tableView reloadRowsAtIndexPaths:modifiedRows withRowAnimation:UITableViewRowAnimationAutomatic];

// Preserve the deselection animation (if desired)
[tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ViewControllerCellReuseIdentifier];
    cell.textLabel.text = [NSString stringWithFormat:@"I'm cell %ld:%ld", (long)indexPath.section, (long)indexPath.row];

return cell;
}

3
BOOL flag;

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    flag = !flag;
    [tableView beginUpdates];
    [tableView reloadRowsAtIndexPaths:@[indexPath] 
                     withRowAnimation:UITableViewRowAnimationAutomatic];
    [tableView endUpdates];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return YES == flag ? 20 : 40;
}

2

solo una nota per qualcuno come me che cerca di aggiungere "Maggiori dettagli" sulla cella personalizzata.

[tableView beginUpdates];
[tableView endUpdates];

Ha fatto un lavoro eccellente, ma non dimenticare di "ritagliare" la vista cellulare. Da Interface Builder selezionare la cella -> Visualizzazione contenuto -> dalla finestra di ispezione Proprietà selezionare " Clip subview "


2

Ecco una versione più breve della risposta di Simons per Swift 3. Consente anche di attivare / disattivare la selezione della cella

var cellIsSelected: IndexPath?


  func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    cellIsSelected = cellIsSelected == indexPath ? nil : indexPath
    tableView.beginUpdates()
    tableView.endUpdates()
  }


  func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    if cellIsSelected == indexPath {
      return 250
    }
    return 65
  }

2

Swift 4 e versioni successive

aggiungi il codice seguente nel metodo del delegato della riga didselect di tableview

tableView.beginUpdates()
tableView.setNeedsLayout()
tableView.endUpdates()

1

Versione rapida della risposta di Simon Lee.

// MARK: - Variables 
  var isCcBccSelected = false // To toggle Bcc.



    // MARK: UITableViewDelegate
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {

    // Hide the Bcc Text Field , until CC gets focused in didSelectRowAtIndexPath()
    if self.cellTypes[indexPath.row] == CellType.Bcc {
        if (isCcBccSelected) {
            return 44
        } else {
            return 0
        }
    }

    return 44.0
}

Quindi in didSelectRowAtIndexPath ()

  func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    self.tableView.deselectRowAtIndexPath(indexPath, animated: true)

    // To Get the Focus of CC, so that we can expand Bcc
    if self.cellTypes[indexPath.row] == CellType.Cc {

        if let cell = tableView.cellForRowAtIndexPath(indexPath) as? RecipientTableViewCell {

            if cell.tag == 1 {
                cell.recipientTypeLabel.text = "Cc:"
                cell.recipientTextField.userInteractionEnabled = true
                cell.recipientTextField.becomeFirstResponder()

                isCcBccSelected = true

                tableView.beginUpdates()
                tableView.endUpdates()
            }
        }
    }
}

1

Si è possibile.

UITableView ha un metodo delegato didSelectRowAtIndexPath

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [UIView animateWithDuration:.6
                          delay:0
         usingSpringWithDamping:UIViewAnimationOptionBeginFromCurrentState
          initialSpringVelocity:0
                        options:UIViewAnimationOptionBeginFromCurrentState animations:^{

                            cellindex = [NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section];
                            NSArray* indexArray = [NSArray arrayWithObjects:indexPath, nil];
                            [violatedTableView beginUpdates];
                            [violatedTableView reloadRowsAtIndexPaths:indexArray withRowAnimation:UITableViewRowAnimationAutomatic];
                            [violatedTableView endUpdates];
                        }
                     completion:^(BOOL finished) {
    }];
}

Ma nel tuo caso se l'utente scorre e seleziona una cella diversa, allora devi avere l'ultima cella selezionata per ridurre ed espandere le reloadRowsAtIndexPaths:chiamate di cella attualmente selezionate, heightForRowAtIndexPath:quindi gestisci di conseguenza.


0

Ecco il mio codice della UITableViewsottoclasse personalizzata , che si espande UITextViewnella cella della tabella, senza ricaricare (e perdere lo stato attivo della tastiera):

- (void)textViewDidChange:(UITextView *)textView {
    CGFloat textHeight = [textView sizeThatFits:CGSizeMake(self.width, MAXFLOAT)].height;
    // Check, if text height changed
    if (self.previousTextHeight != textHeight && self.previousTextHeight > 0) {
        [self beginUpdates];

        // Calculate difference in height
        CGFloat difference = textHeight - self.previousTextHeight;

        // Update currently editing cell's height
        CGRect editingCellFrame = self.editingCell.frame;
        editingCellFrame.size.height += difference;
        self.editingCell.frame = editingCellFrame;

        // Update UITableView contentSize
        self.contentSize = CGSizeMake(self.contentSize.width, self.contentSize.height + difference);

        // Scroll to bottom if cell is at the end of the table
        if (self.editingNoteInEndOfTable) {
            self.contentOffset = CGPointMake(self.contentOffset.x, self.contentOffset.y + difference);
        } else {
            // Update all next to editing cells
            NSInteger editingCellIndex = [self.visibleCells indexOfObject:self.editingCell];
            for (NSInteger i = editingCellIndex; i < self.visibleCells.count; i++) {
                UITableViewCell *cell = self.visibleCells[i];
                CGRect cellFrame = cell.frame;
                cellFrame.origin.y += difference;
                cell.frame = cellFrame;
            }
        }
        [self endUpdates];
    }
    self.previousTextHeight = textHeight;
}

0

Ho usato la fantastica risposta di @ Joy e ha funzionato perfettamente con iOS 8.4 e XCode 7.1.1.

Nel caso in cui tu stia cercando di abilitare / disabilitare il tuo cellulare, ho cambiato -tableViewDidSelect nel modo seguente:

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
//This is the bit I changed, so that if tapped once on the cell, 
//cell is expanded. If tapped again on the same cell, 
//cell is collapsed. 
    if (self.currentSelection==indexPath.row) {
        self.currentSelection = -1;
    }else{
        self.currentSelection = indexPath.row;
    }
        // animate
        [tableView beginUpdates];
        [tableView endUpdates];

}

Spero che tutto ciò ti abbia aiutato.


0

Controlla questo metodo dopo iOS 7 e versioni successive.

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath{
    return UITableViewAutomaticDimension;
}

Sono stati apportati miglioramenti a questo in iOS 8. Possiamo impostarlo come proprietà della vista tabella stessa.



0

Ingressi -

tableView.beginUpdates () tableView.endUpdates () queste funzioni non chiameranno

func tableView (_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {}

Ma, se lo fai, tableView.reloadRows (at: [selectedIndexPath! As IndexPath], con: .none)

Si chiamerà la tableView func (_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {} questa funzione.


-1

Ho appena risolto questo problema con un piccolo trucco:

static int s_CellHeight = 30;
static int s_CellHeightEditing = 60;

- (void)onTimer {
    cellHeight++;
    [tableView reloadData];
    if (cellHeight < s_CellHeightEditing)
        heightAnimationTimer = [[NSTimer scheduledTimerWithTimeInterval:0.001 target:self selector:@selector(onTimer) userInfo:nil repeats:NO] retain];
}

- (CGFloat)tableView:(UITableView *)_tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
        if (isInEdit) {
            return cellHeight;
        }
        cellHeight = s_CellHeight;
        return s_CellHeight;
}

Quando ho bisogno di espandere l'altezza della cella, imposto isInEdit = YESe chiamo il metodo [self onTimer]e anima la crescita della cella fino a raggiungere il valore s_CellHeightEditing :-)


Nel simulatore funziona alla grande, ma nell'iPhone l'hardware è lento. Con un ritardo di 0,05 timer e con un aumento della cella di 5 unità, è molto meglio, ma niente come CoreAnimation
Dzamir

1
Conferma, prima di inviare ... la prossima volta.
ajay_nasa,

-2

Ottieni il percorso dell'indice della riga selezionata. Ricarica la tabella. Nel metodo heightForRowAtIndexPath di UITableViewDelegate, imposta l'altezza della riga selezionata su un'altezza diversa e per gli altri restituisce la normale altezza della riga


2
-1, non funziona. La chiamata [table reloadData]provoca il cambiamento di altezza in modo istantaneo, piuttosto che animare.
Mark Amery,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.