web-dev-qa-db-fra.com

Pouvez-vous animer un changement de hauteur sur un UITableViewCell lorsqu'il est sélectionné?

J'utilise UITableView dans mon application iPhone et j'ai une liste de personnes appartenant à un groupe. J'aimerais que, lorsque l'utilisateur clique sur une personne particulière (et sélectionne ainsi la cellule), la cellule s'agrandisse pour afficher plusieurs commandes de l'interface utilisateur permettant de modifier les propriétés de cette personne. 

Est-ce possible? 

379
Ted

J'ai trouvé une solution VRAIMENT SIMPLE à cet effet secondaire d'un UITableView sur lequel je travaillais .....

Enregistrez la hauteur de la cellule dans une variable qui indique normalement la hauteur d'origine via le tableView: heightForRowAtIndexPath:. Lorsque vous souhaitez animer un changement de hauteur, il vous suffit de modifier la valeur de la variable et de l'appeler.

[tableView beginUpdates];
[tableView endUpdates];

Vous constaterez que cela ne fait pas un rechargement complet mais suffit pour que UITableView sache qu'il doit redessiner les cellules, saisissant la nouvelle valeur de hauteur pour la cellule ... et devinez quoi? Cela ANIMATE le changement pour vous. Sucré.

J'ai une explication plus détaillée et des exemples de code complets sur mon blog ... Animate UITableView Cell Height Change

862
Simon Lee

J'aime la réponse de Simon Lee. Je n'ai pas réellement essayé cette méthode, mais il semble que cela changerait la taille de toutes les cellules de la liste. J'espérais un changement de la seule cellule qui est mise sur écoute. Je l'ai un peu fait comme Simon mais avec juste une petite différence. Cela modifiera l'apparence d'une cellule lorsqu'elle sera sélectionnée. Et ça anime. Juste une autre façon de le faire.

Créez un int pour contenir une valeur pour l'index de cellules actuellement sélectionné:

int currentSelection;

Ensuite: 

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

Ensuite:

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

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


}

Je suis sûr que vous pouvez effectuer des modifications similaires dans tableView: cellForRowAtIndexPath: pour modifier le type de cellule ou même charger un fichier xib pour la cellule.

Ainsi, currentSelection commencera à 0. Vous devrez effectuer des ajustements si vous ne voulez pas que la première cellule de la liste (à l'index 0) soit sélectionnée par défaut.

59
Mark A.

Ajouter une propriété pour garder une trace de la cellule sélectionnée

@property (nonatomic) int currentSelection;

Définissez-la sur une valeur sentinelle dans (par exemple) viewDidLoad, pour vous assurer que la UITableView commence à la position 'normale'

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

    //sentinel
    self.currentSelection = -1;
}

Dans heightForRowAtIndexPath, vous pouvez définir la hauteur souhaitée pour la cellule sélectionnée.

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

Dans didSelectRowAtIndexPath vous enregistrez la sélection actuelle et enregistrez une hauteur dynamique, si nécessaire

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

Dans didDeselectRowAtIndexPath, redéfinissez l'index de sélection sur la valeur sentinelle et animez la cellule à la forme normale

- (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 n'est pas bon car il n'y a pas d'animation ...

C'est ce que je suis en train d'essayer:

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

Cela fonctionne presque bien. Presque. J'augmente la hauteur de la cellule, et parfois il y a un petit "hoquet" dans la vue tableau lorsque la cellule est remplacée, comme si une position de défilement dans la vue tableau était préservée, la nouvelle cellule dans le tableau) se termine avec son décalage trop élevé et le scrollview rebondit pour le repositionner.

14
lawrence

Je ne sais pas ce que sont tous ces problèmes concernant l’appel successif de beginUpdates/endUpdates, vous pouvez simplement utiliser -[UITableView reloadRowsAtIndexPaths:withAnimation:]. Voici un exemple de projet .

11
axiixc

J'ai résolu avec reloadRowsAtIndexPaths

J'enregistre dans didSelectRowAtIndexPath le chemin indexPath de la cellule sélectionnée et appelle reloadRowsAtIndexPaths à la fin (vous pouvez envoyer NSMutableArray pour la liste des éléments que vous souhaitez recharger).

Dans heightForRowAtIndexPath, vous pouvez vérifier si indexPath figure ou non dans la liste des cellules expandIndexPath et la hauteur d'envoi.

Vous pouvez vérifier cet exemple de base: https://github.com/ferminhg/iOS-Examples/tree/master/iOS-UITableView-Cell-Height-Change/celdascambiadetam C'est une solution simple .

j'ajoute une sorte de code si je vous aide

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

Essayez ceci pour développer une ligne indexée:

@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
Roman Solodyashkin

juste une note pour quelqu'un comme moi cherchant à ajouter "Plus de détails" sur une cellule personnalisée. 

[tableView beginUpdates];
[tableView endUpdates];

Fait un excellent travail, mais n'oubliez pas de "rogner" la vue des cellules. Dans Interface Builder, sélectionnez votre cellule -> Vue du contenu -> à partir de l'inspecteur des propriétés, sélectionnez " Sous-vue Clip

2
Anthony Marchenko

Voici une version abrégée de Simons répondre pour Swift 3. Permet également de basculer la sélection de la cellule

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

Version rapide de la réponse de 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
}

Puis dans 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

Au lieu de beginUpdates()/endUpdates(), l'appel recommandé est le suivant:

tableView.performBatchUpdates(nil, completion: nil)

Apple déclare à propos de beginUpdates/endUpdates: "Utilisez la méthode performBatchUpdates (_: completion :) au lieu de celle-ci chaque fois que cela est possible."

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

1
raf

Contributions -

tableView.beginUpdates () tableView.endUpdates () ces fonctions n'appelleront pas 

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

Mais, si vous le faites, tableView.reloadRows (à: [selectedIndexPath! As IndexPath], avec: .none)

Il appellera le func tableView (_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {} cette fonction.

0
sRoy

J'ai utilisé la réponse géniale de @ Joy, et cela fonctionnait parfaitement avec ios 8.4 et XCode 7.1.1. 

Si vous souhaitez rendre votre cellule basculable, j'ai modifié l'option -tableViewDidSelect comme suit:

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

}

J'espère que tout cela vous a aidé. 

0
Septronic

Vérifiez cette méthode après iOS 7 et versions ultérieures. 

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

Des améliorations ont été apportées à cela dans iOS 8. Nous pouvons le définir comme propriété de la vue de table elle-même.

0
iGo

Swift 4 et plus

ajoutez le code ci-dessous dans la méthode de délégué de la ligne didselect de votre table

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

Version rapide de Réponse de Simon Lee :

tableView.beginUpdates()
tableView.endUpdates()

N'oubliez pas que vous devez modifier les propriétés de hauteur AVANTendUpdates().

0
Tamás Sengel

Voici mon code de sous-classe UITableView personnalisée, qui développe UITextView à la cellule du tableau, sans rechargement (et perte du focus clavier):

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