it-swarm.com.de

Können Sie eine Höhenänderung auf einer UITableViewCell animieren, wenn diese ausgewählt ist?

Ich verwende eine UITableView in meiner iPhone-App und habe eine Liste von Personen, die einer Gruppe angehören. Ich möchte es so, dass, wenn der Benutzer auf eine bestimmte Person klickt (wodurch die Zelle ausgewählt wird), die Zelle anwächst und mehrere Steuerelemente der Benutzeroberfläche zum Bearbeiten der Eigenschaften dieser Person anzeigt. 

Ist das möglich? 

379
Ted

Ich fand eine WIRKLICH EINFACHE Lösung dafür als Nebeneffekt auf einen UITableView, an dem ich gerade gearbeitet habe .....

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

[tableView beginUpdates];
[tableView endUpdates];

Sie werden feststellen, dass es kein vollständiges Nachladen gibt, aber es ist genug, damit UITableView weiß, dass es die Zellen neu zeichnen muss, um den neuen Höhenwert für die Zelle zu erfassen ... und wissen Sie was? Es animiert die Veränderung für Sie. Süss.

Ich habe eine ausführlichere Erklärung und vollständige Codebeispiele in meinem Blog ... Animate UITableView Cell Height Change

862
Simon Lee

Ich mag die Antwort von Simon Lee. Ich habe diese Methode eigentlich nicht ausprobiert, aber es sieht so aus, als würde sie die Größe aller Zellen in der Liste ändern. Ich hoffte auf eine Änderung nur der Zelle, die angezapft wird. Ich habe es irgendwie wie Simon gemacht, aber mit einem kleinen Unterschied. Dies ändert das Aussehen einer Zelle, wenn sie ausgewählt wird. Und es animiert. Nur eine andere Möglichkeit, 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, Sie können ähnliche Änderungen in tableView vornehmen: cellForRowAtIndexPath: um den Zellentyp zu ändern oder sogar eine Xib-Datei für die Zelle zu laden.

So beginnt die currentSelection bei 0. Sie müssten Anpassungen vornehmen, wenn die erste Zelle der Liste (Index 0) nicht standardmäßig ausgewählt sein soll.

59
Mark A.

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

@property (nonatomic) int currentSelection;

Setzen Sie ihn auf einen Sentinel-Wert (zum Beispiel) viewDidLoad, um sicherzustellen, dass die UITableView an der 'normalen' Position beginnt

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

    //sentinel
    self.currentSelection = -1;
}

In heightForRowAtIndexPath können Sie die gewünschte Höhe für die ausgewählte Zelle einstellen

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

In didSelectRowAtIndexPath speichern Sie die aktuelle Auswahl und 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];
    }
}

Setzen Sie in didDeselectRowAtIndexPath den Auswahlindex auf den Sentinel-Wert zurück und animieren Sie die Zelle wieder in Normalform

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

        // sentinel
        self.currentSelection = -1;

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

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

Das 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 gibt es einen kleinen "Schluckauf" in der Tabellenansicht, wenn die Zelle ersetzt wird, als würde eine bestimmte Bildlaufposition in der Tabellenansicht beibehalten, die neue Zelle (die erste Zelle) in der Tabelle) endet der Versatz zu hoch und der Bildlauf springt auf, um ihn neu zu positionieren.

14
lawrence

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

11
axiixc

Ich habe mit reloadRowsAtIndexPaths aufgelöst. 

Ich speichere in didSelectRowAtIndexPath den Indexpfad der ausgewählten Zelle und rufe am Ende reloadRowsAtIndexPaths auf (Sie können NSMutableArray für eine Liste der Elemente, die Sie neu laden möchten, senden).

In heightForRowAtIndexPath können Sie überprüfen, ob indexPath in der Liste der expandIndexPath-Zelle und der Sendehöhe enthalten ist.

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

ich füge eine Art Code hinzu, wenn dir geholfen wird

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

Versuchen Sie Folgendes, um die indexweise Zeile zu erweitern:

@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
Nagarjun
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;
}
3

nur eine Notiz für jemanden wie mich, der nach "Mehr Details" in der benutzerdefinierten Zelle sucht. 

[tableView beginUpdates];
[tableView endUpdates];

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

2

Hier ist eine kürzere Version von Simons Antwort auf Swift 3. Ermöglicht auch das Umschalten der Auswahl der Zelle

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
  }
1
aBikis

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
ioopl

Anstelle von beginUpdates()/endUpdates() lautet der empfohlene Aufruf jetzt:

tableView.performBatchUpdates(nil, completion: nil)

Apple sagt in Bezug auf beginUpdates/endUpdates: "Verwenden Sie die performBatchUpdates (_: completion :) -Methode, wann immer dies möglich ist."

Siehe: https://developer.Apple.com/documentation/uikit/uitableview/1614908-beginupdates

1
raf

Ja es ist möglich.

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

Wenn der Benutzer jedoch einen Bildlauf durchführt und eine andere Zelle auswählt, muss die letzte ausgewählte Zelle zum Verkleinern und Erweitern der aktuell ausgewählten Zelle reloadRowsAtIndexPaths: aufgerufen werden, damit heightForRowAtIndexPath: so behandelt wird.

1
Koushik

Eingänge -

tableView.beginUpdates () tableView.endUpdates () Diese Funktionen rufen nicht auf 

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

Wenn dies jedoch der Fall ist, tableView.reloadRows (at: [selectedIndexPath! Als IndexPath], mit: .none)

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

0
sRoy

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

Falls Sie Ihre Zelle umschaltbar machen möchten, habe ich die Option -tableViewDidSelect folgendermaßen 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 hat dir geholfen. 

0
Septronic

Ü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
iGo

Swift 4 und höher

fügen Sie den folgenden Code in die Didselect-Zeilendeponentenmethode Ihres Tableview ein

tableView.beginUpdates()
tableView.setNeedsLayout()
tableView.endUpdates()
0
midhun p

Schnelle Version von Simon Lees Antwort :

tableView.beginUpdates()
tableView.endUpdates()

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

0
Tamás Sengel

Hier ist mein Code der benutzerdefinierten UITableView-Unterklasse, die UITextView in der Tabellenzelle erweitert, ohne erneut zu laden (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