Können Sie bei Auswahl eine Höhenänderung auf einer UITableViewCell animieren?


393

Ich verwende eine UITableViewin meiner iPhone-App und habe eine Liste von Personen, die zu einer Gruppe gehören. Ich möchte, dass beim Klicken des Benutzers auf eine bestimmte Person (wodurch die Zelle ausgewählt wird) die Höhe der Zelle zunimmt, um mehrere Steuerelemente für die Benutzeroberfläche zum Bearbeiten der Eigenschaften dieser Person anzuzeigen.

Ist das möglich?

Antworten:


879

Ich fand eine WIRKLICH EINFACHE Lösung dafür als Nebeneffekt zu einer, an der UITableViewich arbeitete .....

Speichern Sie die Zellenhöhe in einer Variablen, die die ursprüngliche Höhe normalerweise über das tableView: heightForRowAtIndexPath:anzeigt. Wenn Sie eine Höhenänderung animieren möchten, ändern Sie einfach den Wert der Variablen und rufen Sie diese ...

[tableView beginUpdates];
[tableView endUpdates];

Sie werden feststellen, dass es nicht vollständig neu geladen wird, aber es reicht aus, um UITableViewzu wissen, dass es die Zellen neu zeichnen muss, um den neuen Höhenwert für die Zelle zu ermitteln ... und wissen Sie was? Es animiert die Veränderung für Sie. Süss.

Ich habe eine detailliertere Erklärung und vollständige Codebeispiele in meinem Blog ... Animieren Sie die Änderung der UITableView-Zellenhöhe


6
Das ist brilliant. Aber gibt es eine Möglichkeit, die Animationsgeschwindigkeit der Tabelle zu steuern?
Josh Kahane

7
Dies funktioniert, aber wenn ich eine Zelle größer mache, sagen wir von 44 auf 74, und sie dann wieder auf 44 verkleinere, wirkt die Trennlinie völlig seltsam. Kann jemand dies bestätigen?
Plaetzchen

46
Dies ist eine bizarre Lösung, die Apple jedoch auch in der WWDC 2010-Sitzung "Mastering Table View" empfiehlt. Ich werde einen Fehlerbericht über das Hinzufügen zur Dokumentation einreichen, da ich gerade 2 Stunden lang recherchiert habe.
Papa

5
Ich habe diese Lösung ausprobiert und sie funktioniert nur manchmal. Wenn Sie eine Zelle drücken, besteht eine Wahrscheinlichkeit von 50%, dass sie funktioniert. Hat jemand den gleichen Fehler? Es ist, weil iOS7?
Joan Cardona

7
Jetzt ist es auch in der offiziellen Dokumentation geschrieben: 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

Ich mag die Antwort von Simon Lee. Ich habe diese Methode nicht ausprobiert, aber es sieht so aus, als würde sie die Größe aller Zellen in der Liste ändern. Ich hatte auf eine Veränderung nur der Zelle gehofft, die abgehört wird. Ich habe es irgendwie wie Simon gemacht, aber mit nur einem kleinen Unterschied. Dies ändert das Aussehen einer Zelle, wenn sie ausgewählt wird. Und es animiert. Nur ein anderer Weg, es zu tun.

Erstellen Sie ein int, um einen Wert für den aktuell ausgewählten Zellenindex zu speichern:

int currentSelection;

Dann:

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

Dann:

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

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


}

Ich bin sicher, dass Sie ähnliche Änderungen in tableView vornehmen können: cellForRowAtIndexPath: um den Zelltyp zu ändern oder sogar eine xib-Datei für die Zelle zu laden.

Auf diese Weise beginnt die aktuelle Auswahl bei 0. Sie müssten Anpassungen vornehmen, wenn die erste Zelle der Liste (bei Index 0) nicht standardmäßig ausgewählt aussehen soll.


2
Überprüfen Sie den Code, der meinem Beitrag beigefügt ist. Ich habe genau dies getan und die Höhe einer Zelle verdoppelt, wenn sie ausgewählt wurde. :)
Simon Lee

8
"Ich habe diese Methode nicht wirklich ausprobiert, aber es sieht so aus, als würde sie die Größe aller Zellen in der Liste ändern" - dann haben Sie nicht sehr genau hingeschaut.
Jamie

13
Die aktuelle Auswahl ist bereits in tableView.indexPathForSelectedRow gespeichert.
Nick

22

Fügen Sie eine Eigenschaft hinzu, um die ausgewählte Zelle zu verfolgen

@property (nonatomic) int currentSelection;

Stellen Sie einen Sentinel-Wert in (zum Beispiel) ein viewDidLoad, um sicherzustellen, dass der UITableViewStart in der 'normalen' Position erfolgt

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

    //sentinel
    self.currentSelection = -1;
}

In heightForRowAtIndexPathkönnen Sie die gewünschte Höhe für die ausgewählte Zelle festlegen

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

In didSelectRowAtIndexPathspeichern Sie die aktuelle Auswahl und speichern bei Bedarf eine dynamische Höhe

- (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 didDeselectRowAtIndexPathauf Normalform den Auswahlindex zurück zum Sentinel - Wert und animiert die Zelle zurück

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

        // sentinel
        self.currentSelection = -1;

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

Danke danke danke! Ich habe ein bisschen Code hinzugefügt, um die Zelle umschaltbar zu machen. Ich habe den folgenden Code hinzugefügt.
Septronic

14

reloadData ist nicht gut, weil es keine Animation gibt ...

Folgendes versuche ich gerade:

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

Es funktioniert fast richtig. Fast. Ich vergrößere die Höhe der Zelle, und manchmal tritt in der Tabellenansicht ein kleiner "Schluckauf" auf, wenn die Zelle ersetzt wird, als ob eine Bildlaufposition in der Tabellenansicht erhalten bleibt, die neue Zelle (die erste Zelle) in der Tabelle) endet mit einem zu hohen Versatz, und die Bildlaufansicht springt zurück, um ihn neu zu positionieren.


Persönlich fand ich, dass die Verwendung dieser Methode mit UITableViewRowAnimationNone ein glatteres, aber immer noch nicht perfektes Ergebnis liefert.
Ron Srebro

11

Ich weiß nicht, was all das Zeug über das Aufrufen von beginUpdates / endUpdates nacheinander ist, Sie können es einfach verwenden -[UITableView reloadRowsAtIndexPaths:withAnimation:]. Hier ist ein Beispielprojekt .


Hervorragend, dies dehnt meine Textansichten in meiner Auto-Layout-Zelle nicht aus. Die Animation muss jedoch flackern, da die Option Keine Animation beim Aktualisieren der Zellengröße fehlerhaft aussieht.
h3dkandi

10

Ich beschloss mit reloadRowsAtIndexPaths.

Ich speichere im didSelectRowAtIndexPathindexPath der ausgewählten Zelle und rufe reloadRowsAtIndexPathsam Ende auf (Sie können NSMutableArray für eine Liste der Elemente senden, die Sie neu laden möchten).

Im heightForRowAtIndexPath Sie überprüfen, ob indexPath in der Liste der expandIndexPath-Zellen enthalten ist oder nicht, und die Sendehöhe festlegen.

Sie können dieses grundlegende Beispiel überprüfen: https://github.com/ferminhg/iOS-Examples/tree/master/iOS-UITableView-Cell-Height-Change/celdascambiadetam Es ist eine einfache Lösung.

Ich füge eine Art Code hinzu, wenn ich dir helfe

- (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

Versuchen Sie dies zum Erweitern der indexweisen Zeile:

@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

Nur eine Notiz für jemanden wie mich, der nach "Weitere Details" in einer benutzerdefinierten Zelle sucht.

[tableView beginUpdates];
[tableView endUpdates];

Hat eine hervorragende Arbeit geleistet, aber vergessen Sie nicht, die Zellansicht zu "beschneiden". Wählen Sie im Interface Builder Ihre Zelle -> Inhaltsansicht -> Wählen Sie im Eigenschafteninspektor " Clip-Unteransicht ".


2

Hier ist eine kürzere Version von Simons Antwort für Swift 3. Ermöglicht auch das Umschalten der Zellenauswahl

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 und höher

Fügen Sie den folgenden Code in die didselect-Zeilendelegatmethode von tableview ein

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

1

Schnelle Version von Simon Lees Antwort.

// 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
}

Dann 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

Ja es ist möglich.

UITableView hat eine Delegate-Methode 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) {
    }];
}

Aber in Ihrem Fall , wenn der Benutzer blättert und wählt eine andere Zelle dann müssen u die letzte ausgewählte Zelle haben , zu schrumpfen und die aktuell ausgewählte Zelle zu erweitern reloadRowsAtIndexPaths:Anrufe heightForRowAtIndexPath:so handelte entsprechend.


0

Hier ist mein Code für benutzerdefinierte UITableViewUnterklassen, die UITextViewin der Tabellenzelle erweitert werden, ohne neu geladen zu werden (und den Tastaturfokus zu verlieren):

- (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

Ich habe die großartige Antwort von @ Joy verwendet und sie hat perfekt mit ios 8.4 und XCode 7.1.1 funktioniert.

Für den Fall, dass Sie Ihre Zelle umschaltbar machen möchten, habe ich die -tableViewDidSelect wie folgt geändert:

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

}

Ich hoffe, irgendetwas davon hat dir geholfen.


0

Überprüfen Sie diese Methode nach iOS 7 und höher.

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

Dies wurde in iOS 8 verbessert. Wir können es als Eigenschaft der Tabellenansicht selbst festlegen.


0

Schnelle Version von Simon Lees Antwort :

tableView.beginUpdates()
tableView.endUpdates()

Beachten Sie, dass Sie die Höheneigenschaften VORHER ändern sollten endUpdates().


0

Eingänge -

tableView.beginUpdates () tableView.endUpdates () Diese Funktionen werden nicht aufgerufen

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

Wenn Sie dies jedoch tun, tableView.reloadRows (unter: [selectedIndexPath! As IndexPath], mit: .none)

Es wird die Funktion func tableView (_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {} dieser Funktion aufrufen .


-1

Ich habe dieses Problem gerade mit einem kleinen Hack gelöst:

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

Wenn ich die Zellenhöhe erweitern muss, setze ich isInEdit = YESdie Methode und rufe [self onTimer]sie auf. Sie animiert das Zellwachstum, bis es den Wert s_CellHeightEditing erreicht :-)


Im Simulator funktioniert das super, aber im iPhone ist die Hardware verzögert. Mit einer Verzögerung von 0,05 Timern und einer Erhöhung der Zellenhöhe um 5 Einheiten ist es viel besser, aber nichts wie CoreAnimation
Dzamir

1
Bestätigen Sie vor dem nächsten Mal.
Ajay_nasa

-2

Ruft den Indexpfad der ausgewählten Zeile ab. Laden Sie die Tabelle neu. Legen Sie in der heightForRowAtIndexPath-Methode von UITableViewDelegate die Höhe der ausgewählten Zeile auf eine andere Höhe fest und geben Sie für die anderen die normale Zeilenhöhe zurück


2
-1, funktioniert nicht. Das Aufrufen [table reloadData]führt dazu, dass die Höhenänderung sofort erfolgt und nicht animiert wird.
Mark Amery
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.