web-dev-qa-db-fra.com

UITableViewCell développer sur clic

Disons que nous avons un UITableViewCell personnalisé

Ainsi, chaque fois que je clique sur un bouton personnalisé sur une cellule .. il devrait s’étendre dans une certaine mesure (vous pouvez dire 40 hauteur de plus ...) et lorsque je clique à nouveau sur le même bouton personnalisé, il devrait s’effondrer à la hauteur précédente.

Les développeurs doivent me guider. Comment puis-je accomplir cette tâche

52
Tariq

Implémentez heightForRowAtIndexPath pour calculer la bonne hauteur. Ensuite, dans le code de votre bouton, forcez la table à réévaluer la hauteur de chaque cellule avec beginUpdates plus endUpdates:

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

Les modifications apportées aux hauteurs des cellules de la table seront automatiquement calculées avec heightForRowAtIndexPath et les modifications seront également animées.

En fait, au lieu d'un bouton sur votre cellule qui le fait, vous pouvez même faire en sélectionnant la cellule le faire dans didSelectRowAtIndexPath.

67
kris

Je ne vais rien dire ici pour contredire la réponse acceptée, considérant que c'est parfaitement correct. Cependant, je vais entrer dans les détails pour savoir comment y parvenir. Si vous ne voulez pas lire tout cela et êtes plus intéressé par jouer avec le code source dans un projet en cours, j'ai téléchargé un projet example dans GitHub .

L'idée de base est d'avoir une condition à l'intérieur de la méthode -tableView: heightForRowAtIndexPath: qui détermine si la cellule en cours doit ou non être développée. Cela sera déclenché en appelant les mises à jour de début/fin sur la table à partir de -tableView: didSelectRowAtIndexPath:. Dans cet exemple, je vais montrer comment créer une vue de table permettant de développer une cellule à la fois.

La première chose à faire est de déclarer une référence à un objet NSIndexPath . Vous pouvez le faire comme bon vous semble, mais je vous recommande d'utiliser une déclaration de propriété comme celle-ci:

@property (strong, nonatomic) NSIndexPath *expandedIndexPath;

NOTE: Vous n'avez pas besoin de créer ce chemin d'index dans viewDidLoad, ou toute autre méthode similaire. Le fait que l'indice soit initialement nul signifiera seulement que la table n'aura pas initialement une ligne développée. Si vous préférez que la table commence avec une ligne de votre choix développée, vous pouvez ajouter quelque chose comme ceci à votre méthode viewDidLoad:

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

L'étape suivante consiste à passer à votre UITableViewDelegate method -tableView: didSelectRowAtIndexPath: pour ajouter la logique permettant de modifier l'index de cellule développé en fonction de la sélection des utilisateurs. L'idée ici est de vérifier le chemin d'index qui vient d'être sélectionné par rapport au chemin d'index stocké dans la variable expandedIndexPath. Si les deux correspondent, nous savons que l'utilisateur essaie de désélectionner la cellule développée, auquel cas, nous définissons la variable sur nil. Sinon, nous affectons la variable expandedIndexPath à l'index qui vient d'être sélectionné. Tout cela est fait entre les appels à beginUpdates/endUpdates, afin de permettre à la vue tabulaire de gérer automatiquement l'animation de transition. 

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

Ensuite, la dernière étape est dans un autre UITableViewDelegate method -tableView: heightForRowAtIndexPath:. Cette méthode sera appelée une fois que vous avez déclenché beginUpdates une fois pour chaque chemin d'index que la table détermine nécessitant une mise à jour. C’est là que vous comparerez la expandedIndexPath au chemin d’index en cours de réévaluation.

Si les deux chemins d'index sont identiques, il s'agit de la cellule que vous souhaitez développer, sinon sa hauteur devrait être normale. J'ai utilisé les valeurs 100 et 44, mais vous pouvez utiliser ce qui vous convient le mieux.

- (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
}
94
Mick MacCallum

J'ai créé une bibliothèque open source pour cela. Vous venez d'implémenter collapse et développez les délégués dans votre code et voilà! vous pouvez également effectuer des dessins et des animations. Découvrez this .

enter image description here

9
Hamid Vakilian

J'ai fabriqué un composant réutilisable qui fera exactement ce dont vous parlez. C'est assez facile à utiliser, et il y a un projet de démonstration.

GCRetractableSectionController sur GitHub.

8
gcamp

Au lieu d'utiliser [tableView beginUpdates] et [tableView endUpdates], j'utilise la méthode [tableView reloadRowsAtIndexPath:... withRowAnimation:...] dans la méthode didSelectRowAtIndexPath.

Je préfère cela, car j’ai eu quelques problèmes avec les éléments à afficher lorsque je développe ma UITableViewCell lorsque j’utilise les méthodes de mises à jour de début et de fin. Un autre point est que vous pouvez choisir entre certaines animations telles que: Haut, Bas, Gauche, Droite ...

8
Mike_NotGuilty

C'est la réponse de Mick, mais pour Swift 4. (IndexPath remplace NSIndexPath, qui est livré avec un IndexPath vide car rien ne ferait planter Swift. De plus, vous pouvez comparer deux instances d'IndexPath à l'aide de ==)

Déclarez la propriété ExpandIndexPath.

var expandedIndexPath = IndexPath()

Pièce viewDidLoad facultative.

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

Ensuite, la partie didSelectRow.

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

    if indexPath == expandedIndexPath {
        expandedIndexPath = IndexPath()
    } else {
        expandedIndexPath = indexPath
    }

    tableView.endUpdates()
}

Ensuite, la partie heightForRow.

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

    return 44
}
3
David H

J'ai utilisé le code source de Gcamp et créé ma propre version. 

1) Dans une méthode loadView, initialisez un tableau mutable dans lequel vous sauvegarderez les états étendus ou non développés de vos sections. Il est essentiel de sauvegarder les statuts développés dans un tableau séparé, qui ne soit pas détruit lors du défilement de la vue tableau (par exemple, si vous le stockez dans un headerView, il sera redessiné et oubliera s'il a été développé ou non). Dans mon cas, il s'agit du tableau _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) Créez un headerView personnalisé pour une section avec un bouton d’agrandissement. Déléguez une action d'un bouton de votre headerView à votre TableViewController à l'aide d'un modèle de délégation. Vous pouvez trouver des images appropriées dans le code source de Gcamp.

3) Créez une action pour supprimer ou ajouter des lignes. Ici _foldersArray est ma structure, qui contient toutes les données. HeaderView de ma section - MCExpandableAccountHeaderView connaît son propre numéro de section. Je le transfère ici lorsque je crée des vues d'en-tête pour chaque section. Il est essentiel de le transférer dans cette méthode, car vous devez savoir quelle section est maintenant développée ou étendue.

- (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) Il est également important de renvoyer le nombre ou les lignes corrects dans une section, que celle-ci soit étendue ou non. 

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

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

Suite à cet article moyen sur la manière d’agrandir les cellules en appuyant simplement sur un bouton et en définissant la valeur numbersOfLine pour une étiquette spécifique, j’ai pu exécuter l’animation à l’aide de

tableView.beginUpdates()
tableView.performBatchUpdates({
  cell.description.numberOfLines = !expanded ? 0 : 3
}, completion: nil)
tableView.endUpdates()

Remarque performBatchUpdates est uniquement disponible dans iOS 11

0
idelfonsogutierrez
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;

}
0
archana

Pour ajouter à la réponse (0x7fffffff}, j'ai trouvé qu'il me fallait une condition supplémentaire dans l'instruction if dans didSelectRowAtIndexPath _ - ainsi:

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