UITableView défini sur des cellules statiques. Est-il possible de masquer certaines des cellules par programme?

132

UITableView défini sur des cellules statiques.

Est-il possible de masquer certaines des cellules par programme?

Shmidt
la source

Réponses:

48

Vous recherchez cette solution:

StaticDataTableViewController 2.0

https://github.com/xelvenone/StaticDataTableViewController

qui peut afficher / masquer / recharger n'importe quelle (s) cellule (s) statique (s) avec ou sans animation!

[self cell:self.outletToMyStaticCell1 setHidden:hide]; 
[self cell:self.outletToMyStaticCell2 setHidden:hide]; 
[self reloadDataAnimated:YES];

Remarque à toujours utiliser uniquement (reloadDataAnimated: YES / NO) (ne pas appeler [self.tableView reloadData] directement)

Cela n'utilise pas la solution hacky avec le réglage de la hauteur à 0 et vous permet d'animer le changement et de masquer des sections entières

Peter Lapisu
la source
6
Un problème avec cette solution est qu'elle laisse des espaces où se trouvaient les cellules cachées.
Jack
comment voulez-vous dire des lacunes? pouvez-vous mieux décrire la situation, peut-être directement sur github? avez-vous exécuté l'échantillon?
Peter Lapisu
1
Je veux dire que si vous avez par exemple trois sections et que vous masquez les cellules de la section centrale, l'espace où se trouvait cette section reste. Je n'ai pas essayé l'exemple de projet, mais j'ai essayé le code. Je vais essayer l'échantillon.
Jack
1
consultez l'exemple, il y avait une mise à jour plus importante, peut-être que vous aviez un vieux code ... fonctionne parfaitement pour moi
Peter Lapisu
J'ai essayé l'échantillon et il semble fonctionner là-bas, la seule chose que je fais de différent est d'utiliser un IBOutletCollectionmais je ne vois pas en quoi cela ferait une différence. Je n'ai téléchargé le code qu'hier, donc je ne pense pas que ce soit une ancienne version.
Jack
164

Pour masquer les cellules statiques dans UITable:

  1. Ajoutez cette méthode :

Dans votre classe de délégué de contrôleur UITableView:

Objectif c:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell* cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];

    if(cell == self.cellYouWantToHide)
        return 0; //set the hidden cell's height to 0

    return [super tableView:tableView heightForRowAtIndexPath:indexPath];
}

Rapide:

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    var cell = super.tableView(tableView, cellForRowAtIndexPath: indexPath)

    if cell == self.cellYouWantToHide {
        return 0
    }

    return super.tableView(tableView, heightForRowAtIndexPath: indexPath)
}

Cette méthode sera appelée pour chaque cellule de l'UITable. Une fois qu'il l'appelle pour la cellule que vous souhaitez masquer, nous définissons sa hauteur sur 0. Nous identifions la cellule cible en lui créant une sortie:

  1. Dans le concepteur, créez une sortie pour la ou les cellules que vous souhaitez masquer. La sortie d'une de ces cellules est appelée "cellYouWantToHide" ci-dessus.
  2. Cochez "Sous-vues de clip" dans l'IB pour les cellules que vous souhaitez masquer. Les cellules que vous masquez doivent avoir ClipToBounds = YES. Sinon, le texte s'empilera dans UITableView.
Tout comme
la source
10
Belle solution. Pour éviter le ClipToBoundsproblème, vous pouvez également définir la cellule sur masquée. Cela me paraîtrait plus propre. ;-)
MonsieurDart
13
+1 pour ClipToBounds = OUI. Les solutions Numeorus dans d'autres threads ont raté cela.
Jeff
2
Bonne idée. C'est la solution la plus simple que j'ai trouvée. D'autres solutions avaient du code à deux emplacements ou plus. BTW, dans IB, ClipToBounds est répertorié comme "Clip Subviews".
VaporwareWolf
1
Juste une petite correction, utilisez self .cellYouWantToHide au lieu de this .cellYouWantToHide.
Zoltan Vinkler
2
sans créer d'objet de cellule, nous pouvons également utiliser "index.row" pour masquer le "uitableviewcell"
g212gs
38

Le meilleur moyen est celui décrit dans le blog suivant http://ali-reynolds.com/2013/06/29/hide-cells-in-static-table-view/

Concevez votre vue de tableau statique comme d'habitude dans le générateur d'interface - avec toutes les cellules potentiellement masquées. Mais il y a une chose que vous devez faire pour chaque cellule potentielle que vous souhaitez masquer - cochez la propriété "Clip sous-vues" de la cellule, sinon le contenu de la cellule ne disparaît pas lorsque vous essayez de la masquer (en réduisant sa hauteur - plus tard).

SO - vous avez un commutateur dans une cellule et le commutateur est censé se cacher et afficher des cellules statiques. Connectez-le à une IBAction et faites-le:

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

Cela vous donne de belles animations pour les cellules qui apparaissent et disparaissent. Maintenant, implémentez la méthode de délégation de vue table suivante:

- (float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.section == 1 && indexPath.row == 1) { // This is the cell to hide - change as you need
    // Show or hide cell
        if (self.mySwitch.on) {
            return 44; // Show the cell - adjust the height as you need
        } else {
            return 0; // Hide the cell
        }
   }
   return 44;
}

Et c'est tout. Actionnez le commutateur et la cellule se cache et réapparaît avec une belle animation fluide.

Mohamed Saleh
la source
Mise à jour: vous devez également définir le libellé de la cellule sur Masquer / Afficher avec vous Masquer / Afficher la cellule, sinon les libellés créeront un désordre.
Mohamed Saleh
1
Vous devez noter que si un UIView dans le contenu de votre cellule statique a des contraintes envers le cell.content, vous pouvez obtenir une erreur d'exécution si ces contraintes deviennent invalides pour votre nouvelle hauteur de cellule.
Pedro Borges
1
Meilleure réponse. Merci.
Eric Chen
1
Je savais que cette astuce fonctionnait avec du contenu dynamique. Fonctionne assez bien avec l'électricité statique aussi. Tellement simple aussi.
Ants
2
Celui-ci fonctionne vraiment, mais pour couvrir un point manquant: si la ligne à masquer / afficher contient un sélecteur de date, ne faire que [self.tableView beginUpdates]; [self.tableView endUpdates]; laissera toujours un pépin tout en le cachant. Pour éliminer le problème, vous devez appeler [self.tableView reloadRowsAtIndexPaths: @ [indexPathOfTargetRow] withRowAnimation: UITableViewRowAnimationNone]; Notez également que l'animation de ligne est en quelque sorte "désactivée" ici.
Chris
34

Ma solution va dans le même sens que Gareth, bien que je fasse certaines choses différemment.

Voici:

1. Cachez les cellules

Il n'y a aucun moyen de masquer directement les cellules. UITableViewControllerest la source de données qui fournit les cellules statiques, et actuellement il n'y a aucun moyen de lui dire "ne pas fournir la cellule x". Nous devons donc fournir notre propre source de données, qui délègue au UITableViewControllerafin d'obtenir les cellules statiques.

Le plus simple est de sous UITableViewController-classer et de remplacer toutes les méthodes qui doivent se comporter différemment lors du masquage de cellules .

Dans le cas le plus simple (table à section unique, toutes les cellules ont la même hauteur), cela ressemblerait à ceci:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section    
{
    return [super tableView:tableView numberOfRowsInSection:section] - numberOfCellsHidden;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Recalculate indexPath based on hidden cells
    indexPath = [self offsetIndexPath:indexPath];

    return [super tableView:tableView cellForRowAtIndexPath:indexPath];
}

- (NSIndexPath*)offsetIndexPath:(NSIndexPath*)indexPath
{
    int offsetSection = indexPath.section; // Also offset section if you intend to hide whole sections
    int numberOfCellsHiddenAbove = ... // Calculate how many cells are hidden above the given indexPath.row
    int offsetRow = indexPath.row + numberOfCellsHiddenAbove;

    return [NSIndexPath indexPathForRow:offsetRow inSection:offsetSection];
}

Si votre tableau comporte plusieurs sections ou si les cellules ont des hauteurs différentes, vous devez remplacer d'autres méthodes. Le même principe s'applique ici: vous devez décaler indexPath, section et row avant de déléguer à super.

Gardez également à l'esprit que le paramètre indexPath pour des méthodes telles que didSelectRowAtIndexPath: sera différent pour la même cellule, en fonction de l'état (c'est-à-dire le nombre de cellules masquées). C'est donc probablement une bonne idée de toujours compenser tout paramètre indexPath et de travailler avec ces valeurs.

2. Animer le changement

Comme Gareth l'a déjà déclaré, vous obtenez des problèmes majeurs si vous animez les modifications à l'aide de la reloadSections:withRowAnimation:méthode.

J'ai découvert que si vous appelez reloadData:immédiatement après, l'animation est beaucoup améliorée (il ne reste que de petits problèmes). Le tableau s'affiche correctement après l'animation.

Donc ce que je fais, c'est:

- (void)changeState
{
     // Change state so cells are hidden/unhidden
     ...

    // Reload all sections
    NSIndexSet* reloadSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [self numberOfSectionsInTableView:tableView])];

    [tableView reloadSections:reloadSet withRowAnimation:UITableViewRowAnimationAutomatic];
    [tableView reloadData];
}
henning77
la source
2
Bénis toi, douce et douce personne.
guptron
avez-vous trouvé une solution pour les petits problèmes d'animation? pour moi, les lignes de séparation entre les cellules ne s'animeront pas correctement et n'utiliseront pas d'animation plutôt qu'une glichée. excellente solution cependant!
Maximilian Körner
Mon problème est que les cellules statiques numberOfRowsInSection:sont appelées uniquement lorsque la table se charge pour la première fois. Quand j'appelle [self.tableView reloadData] - numberOfRowsInSection:n'est plus jamais appelé. Seul cellForRowAtIndexPath:est appelé. Qu'est-ce que je rate?
Roman
13
  1. Dans le concepteur, créez une sortie pour la ou les cellules que vous souhaitez masquer. Par exemple, vous voulez masquer 'cellOne', donc dans viewDidLoad () faites ceci

cellOneOutlet.hidden = true

remplacez maintenant la méthode ci-dessous, vérifiez quel état de cellule est masqué et renvoyez la hauteur 0 pour ces cellules. C'est l'une des nombreuses façons de masquer n'importe quelle cellule dans tableView statique dans Swift.

override func tableView(tableView: UITableView, heightForRowAtIndexPathindexPath: NSIndexPath) -> CGFloat 
{

let tableViewCell = super.tableView(tableView,cellForRowAtIndexPath: indexPath)

        if tableViewCell.hidden == true
        {
            return 0
        }
        else{
             return super.tableView(tableView, heightForRowAtIndexPath: indexPath)
        }

}
Saleh Masum
la source
La meilleure solution à ce jour!
derdida
ajoutez tableView.reloadData () après avoir apporté des modifications et c'est parfait. M'a épargné beaucoup d'efforts pour changer de solution! merci
Shayan C
1
Swift 4 ne me laisse pas utiliser let tableViewCell = super.tableView(tableView,cellForRowAtIndexPath: indexPath). Je suppose qu'il est remplacé par let tableViewCell = tableView.cellForRow(at: indexPath as IndexPath).
Clifton Labrum
Puisque j'utilise des cellules statiques sur un UITableViewController, je n'ai aucune UITableViewméthode de délégation. Pour pouvoir l'appeler heightForRow, ai-je également besoin d'autres méthodes?
Clifton Labrum
@CliftonLabrum non, vous ne pouvez remplacer que cette méthode.
Alexander Ershov
11

J'ai trouvé une alternative qui cache en fait les sections et ne les supprime pas. J'ai essayé l'approche de @ henning77, mais j'ai continué à rencontrer des problèmes lorsque j'ai changé le nombre de sections de l'UITableView statique. Cette méthode a très bien fonctionné pour moi, mais j'essaie principalement de masquer des sections au lieu de lignes. Je supprime certaines lignes à la volée avec succès, mais c'est beaucoup plus compliqué, j'ai donc essayé de regrouper les choses en sections que je dois afficher ou masquer. Voici un exemple de la façon dont je cache des sections:

Je déclare d'abord une propriété NSMutableArray

@property (nonatomic, strong) NSMutableArray *hiddenSections;

Dans le viewDidLoad (ou après avoir interrogé vos données), vous pouvez ajouter des sections que vous souhaitez masquer au tableau.

- (void)viewDidLoad
{
    hiddenSections = [NSMutableArray new];

    if(some piece of data is empty){
        // Add index of section that should be hidden
        [self.hiddenSections addObject:[NSNumber numberWithInt:1]];
    }

    ... add as many sections to the array as needed

    [self.tableView reloadData];
}

Ensuite, implémentez les méthodes de délégué TableView suivantes

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    if([self.hiddenSections containsObject:[NSNumber numberWithInt:section]]){
        return nil;
    }

    return [super tableView:tableView titleForHeaderInSection:section];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if([self.hiddenSections containsObject:[NSNumber numberWithInt:indexPath.section]]){
        return 0;
    }

    return [super tableView:tableView heightForRowAtIndexPath:[self offsetIndexPath:indexPath]];
}

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if([self.hiddenSections containsObject:[NSNumber numberWithInt:indexPath.section]]){
        [cell setHidden:YES];
    }
}

Ensuite, définissez la hauteur de l'en-tête et du pied de page sur 1 pour les sections masquées car vous ne pouvez pas définir la hauteur sur 0. Cela entraîne un espace supplémentaire de 2 pixels, mais nous pouvons le compenser en ajustant la hauteur de l'en-tête visible suivant.

-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section 
{
    CGFloat height = [super tableView:tableView heightForHeaderInSection:section];

    if([self.hiddenSections containsObject:[NSNumber numberWithInt:section]]){
        height = 1; // Can't be zero
    }
    else if([self tableView:tableView titleForHeaderInSection:section] == nil){ // Only adjust if title is nil
        // Adjust height for previous hidden sections
        CGFloat adjust = 0;

        for(int i = (section - 1); i >= 0; i--){
            if([self.hiddenSections containsObject:[NSNumber numberWithInt:i]]){
                adjust = adjust + 2;
            }
            else {
                break;
            }
        }

        if(adjust > 0)
        {                
            if(height == -1){
                height = self.tableView.sectionHeaderHeight;
            }

            height = height - adjust;

            if(height < 1){
                height = 1;
            }
        }
    }

    return height;
}

-(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section 
{   
    if([self.hiddenSections containsObject:[NSNumber numberWithInt:section]]){
        return 1;
    }
    return [super tableView:tableView heightForFooterInSection:section];
}

Ensuite, si vous avez des lignes spécifiques à masquer, vous pouvez ajuster le numberOfRowsInSection et les lignes renvoyées dans cellForRowAtIndexPath. Dans cet exemple, j'ai une section qui comporte trois lignes où trois d'entre elles peuvent être vides et doivent être supprimées.

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    NSInteger rows = [super tableView:tableView numberOfRowsInSection:section];

    if(self.organization != nil){
        if(section == 5){ // Contact
            if([self.organization objectForKey:@"Phone"] == [NSNull null]){     
                rows--;
            }

            if([self.organization objectForKey:@"Email"] == [NSNull null]){     
                rows--;
            }

            if([self.organization objectForKey:@"City"] == [NSNull null]){     
                rows--;
            }
        }
    }

    return rows;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{    
    return [super tableView:tableView cellForRowAtIndexPath:[self offsetIndexPath:indexPath]];
}

Utilisez ce offsetIndexPath pour calculer l'indexPath pour les lignes où vous supprimez conditionnellement des lignes. Pas nécessaire si vous ne masquez que des sections

- (NSIndexPath *)offsetIndexPath:(NSIndexPath*)indexPath
{
    int row = indexPath.row;

    if(self.organization != nil){
        if(indexPath.section == 5){
            // Adjust row to return based on which rows before are hidden
            if(indexPath.row == 0 && [self.organization objectForKey:@"Phone"] == [NSNull null] && [self.organization objectForKey:@"Email"] != [NSNull null]){     
                row++;
            }
            else if(indexPath.row == 0 && [self.organization objectForKey:@"Phone"] == [NSNull null] && [self.organization objectForKey:@"Address"] != [NSNull null]){     
                row = row + 2;
            }
            else if(indexPath.row == 1 && [self.organization objectForKey:@"Phone"] != [NSNull null] && [self.organization objectForKey:@"Email"] == [NSNull null]){     
                row++;
            }
            else if(indexPath.row == 1 && [self.organization objectForKey:@"Phone"] == [NSNull null] && [self.organization objectForKey:@"Email"] != [NSNull null]){     
                row++;
            }
        }
    }

    NSIndexPath *offsetPath = [NSIndexPath indexPathForRow:row inSection:indexPath.section];

    return offsetPath;
}

Il existe de nombreuses méthodes à remplacer, mais ce que j'aime dans cette approche, c'est qu'elle est réutilisable. Configurez le tableau hiddenSections, ajoutez-y et il masquera les sections correctes. Cacher les rangées c'est un peu plus délicat, mais possible. Nous ne pouvons pas simplement définir la hauteur des lignes que nous voulons masquer à 0 si nous utilisons un UITableView groupé car les bordures ne seront pas dessinées correctement.

Austin
la source
1
J'utiliserais probablement NSMutableSetpour à la hiddenSectionsplace. C'est beaucoup plus rapide car vous testez principalement l'adhésion.
pixelfreak
Bonne réponse, Austin. J'ai voté cette réponse et je l'utiliserai comme référence pour mes projets à l'avenir. pixelfreak fait également un bon point sur l'utilisation de NSMutableSetfor hiddenSections, même si je comprends que le point de votre réponse était plus conceptuel que pointilleux quant au type de structure de données à utiliser.
BigSauce
10

Il s'avère que vous pouvez masquer et afficher des cellules dans un UITableView statique - et avec une animation. Et ce n'est pas si difficile à accomplir.

Projet de démonstration

Vidéo du projet de démonstration

L'essentiel:

  1. Use tableView:heightForRowAtIndexPath: pour spécifier les hauteurs de cellule de manière dynamique en fonction d'un état.
  2. Lorsque l'état change, animez les cellules affichées / masquées en appelant tableView.beginUpdates();tableView.endUpdates()
  3. N'appelle pas à l' tableView.cellForRowAtIndexPath:intérieurtableView:heightForRowAtIndexPath: . Utilisez les indexPaths mis en cache pour différencier les cellules.
  4. Ne cachez pas les cellules. Définissez plutôt la propriété "Clip Subviews" dans Xcode.
  5. Utilisez des cellules personnalisées (pas Plain, etc.) pour obtenir une belle animation de masquage. De plus, gérez correctement la disposition automatique pour le cas où la hauteur de cellule == 0.

Plus d'informations dans mon blog (langue russe)

Sergey Skoblikov
la source
1
Ce point était important pour moi: "Ne masquez pas les cellules. Définissez plutôt la propriété" Clip Subviews "dans Xcode."
balkoth
8

Oui, c'est certainement possible, même si je suis aux prises avec le même problème pour le moment. J'ai réussi à masquer les cellules et tout fonctionne bien, mais je ne peux pas actuellement faire en sorte que la chose s'anime proprement. Voici ce que j'ai trouvé:

Je cache des lignes en fonction de l'état d'un interrupteur ON / OFF dans la première ligne de la première section. Si le commutateur est sur ON, il y a 1 ligne en dessous dans la même section, sinon il y a 2 lignes différentes.

J'ai un sélecteur appelé lorsque le commutateur est basculé, et je règle une variable pour indiquer dans quel état je me trouve. Ensuite, j'appelle:

[[self tableView] reloadData];

Je remplace la tableView: willDisplayCell: forRowAtIndexPath: function et si la cellule est censée être masquée, je fais ceci:

[cell setHidden:YES];

Cela masque la cellule et son contenu, mais ne supprime pas l'espace qu'elle occupe.

Pour supprimer l'espace, remplacez la tableView: heightForRowAtIndexPath: function et renvoyez 0 pour les lignes qui doivent être masquées.

Vous devez également remplacer tableView: numberOfRowsInSection: et renvoyer le nombre de lignes dans cette section. Vous devez faire quelque chose d'étrange ici pour que si votre tableau est un style groupé, les coins arrondis apparaissent sur les cellules correctes. Dans mon tableau statique, il y a l'ensemble complet de cellules pour la section, donc il y a la première cellule contenant l'option, puis 1 cellule pour les options d'état ON et 2 autres cellules pour les options d'état OFF, un total de 4 cellules. Lorsque l'option est activée, je dois retourner 4, cela inclut l'option masquée afin que la dernière option affichée ait une boîte arrondie. Lorsque l'option est désactivée, les deux dernières options ne sont pas affichées, donc je retourne 2. Tout cela semble maladroit. Désolé si ce n'est pas très clair, c'est difficile à décrire. Juste pour illustrer la configuration, voici la construction de la section de table dans IB:

  • Rangée 0: Option avec interrupteur ON / OFF
  • Ligne 1: affichée lorsque l'option est activée
  • Ligne 2: affichée lorsque l'option est désactivée
  • Ligne 3: affichée lorsque l'option est désactivée

Ainsi, lorsque l'option est activée, le tableau indique deux lignes qui sont:

  • Rangée 0: Option avec interrupteur ON / OFF
  • Ligne 1: affichée lorsque l'option est activée

Lorsque l'option est désactivée, le tableau indique quatre lignes qui sont:

  • Rangée 0: Option avec interrupteur ON / OFF
  • Ligne 1: affichée lorsque l'option est activée
  • Ligne 2: affichée lorsque l'option est désactivée
  • Ligne 3: affichée lorsque l'option est désactivée

Cette approche ne semble pas correcte pour plusieurs raisons, c'est tout ce que j'ai obtenu avec mon expérimentation jusqu'à présent, alors faites-moi savoir si vous trouvez un meilleur moyen. Les problèmes que j'ai observés jusqu'à présent sont:

  • Il est faux de dire à la table que le nombre de lignes est différent de ce qui est vraisemblablement contenu dans les données sous-jacentes.

  • Je n'arrive pas à animer le changement. J'ai essayé d'utiliser tableView: reloadSections: withRowAnimation: au lieu de reloadData et les résultats ne semblent pas avoir de sens, j'essaie toujours de faire fonctionner cela. Actuellement, ce qui semble se produire, c'est que tableView ne met pas à jour les lignes correctes, donc l'une reste cachée qui doit être affichée et un vide est laissé sous la première ligne. Je pense que cela pourrait être lié au premier point concernant les données sous-jacentes.

J'espère que quelqu'un pourra suggérer des méthodes alternatives ou peut-être comment étendre avec l'animation, mais peut-être que cela vous aidera à démarrer. Mes excuses pour le manque d'hyperliens vers les fonctions, je les ai mis mais ils ont été rejetés par le filtre anti-spam car je suis un utilisateur assez récent.

Gareth Clarke
la source
Je suis presque sûr que vous devriez appeler [[self tableView] reloadData];une fois de plus après avoir caché les cellules
Shmidt
Désolé de relancer cela, mais comment avez-vous pu sous-classer UITableViewController et l'utiliser avec des cellules statiques? Xcode ne me laisse pas.
Dany Joumaa
Nessup, vous n'avez pas besoin de sous-classer UITableView. Dans le storyboard, choisissez votre TableViewController, puis cliquez sur TableView. Vous pouvez maintenant choisir entre les cellules dynamiques / statiques.
Shmidt
1
J'ai trouvé la solution comment animer les cellules statiques masquer / afficher. Appelez simplement une return [super tableView:tableView cellForRowAtIndexPath:indexPath];sous- UITableViewControllerclasse pour créer des cellules statiques. Les chemins d'index sont indexés statiquement - pas dynamiques ...
k06a
8

Selon la réponse de Justas, mais pour Swift 4:

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    let cell = super.tableView(tableView, cellForRowAt: indexPath)

    if cell == self.cellYouWantToHide {
        return 0
    }

    return super.tableView(tableView, heightForRowAt: indexPath)
}
Chris Herbst
la source
1
vous devrez peut-être tableView.reloadRows(at:, with:)mettre à jour les cellules si vous modifiez la hauteur alors que la ligne est déjà visible.
Frost-Lee
5

D'accord, après quelques essais, j'ai une réponse non commune. J'utilise la variable "isHidden" ou "hidden" pour vérifier si cette cellule doit être masquée.

  1. créez un IBOutlet dans votre contrôleur de vue. @IBOutlet weak var myCell: UITableViewCell!

  2. Mettez à jour le myCelldans votre fonction personnalisée, par exemple, vous pouvez l'ajouter dans viewDidLoad:

override func viewDidLoad() { super.viewDidLoad() self.myCell.isHidden = true }

  1. dans votre méthode déléguée:

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { let cell = super.tableView(tableView, cellForRowAt: indexPath) guard !cell.isHidden else { return 0 } return super.tableView(tableView, heightForRowAt: indexPath) }

Cela réduira votre logique dans la méthode de délégué et vous n'aurez qu'à vous concentrer sur vos besoins commerciaux.

BananZ
la source
4

Les réponses ci-dessus qui masquent / affichent les cellules, modifient rowHeight ou gâchent les contraintes de mise en page automatique ne fonctionnaient pas pour moi en raison de problèmes de mise en page automatique. Le code est devenu intolérable.

Pour une simple table statique, ce qui fonctionnait le mieux pour moi était de:

  1. Créez une prise pour chaque cellule du tableau statique
  2. Créez un tableau uniquement avec les sorties de cellules à afficher
  3. Remplacer cellForRowAtIndexPath pour renvoyer la cellule à partir du tableau
  4. Remplacer numberOfRowsInSection pour renvoyer le nombre du tableau
  5. Implémentez une méthode pour déterminer quelles cellules doivent être dans ce tableau, appelez cette méthode chaque fois que nécessaire, puis reloadData.

Voici un exemple de mon contrôleur de vue de table:

@IBOutlet weak var titleCell: UITableViewCell!
@IBOutlet weak var nagCell: UITableViewCell!
@IBOutlet weak var categoryCell: UITableViewCell!

var cellsToShow: [UITableViewCell] = []

override func viewDidLoad() {
    super.viewDidLoad()
    determinCellsToShow()
}

func determinCellsToShow() {
    if detail!.duration.type != nil {
        cellsToShow = [titleCell, nagCell, categoryCell]
    }
    else {
        cellsToShow = [titleCell,  categoryCell]
    }
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    return cellsToShow[indexPath.row]
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return cellsToShow.count
}
David Barta
la source
Cela a très bien fonctionné, même 5 ans plus tard! Merci beaucoup.
Schrockwell
2020 et c'est la meilleure solution à cela. Aucun hack nécessaire.
mdonati le
3

Méthode simple compatible iOS 11 et IB / Storyboard

Pour iOS 11, j'ai trouvé qu'une version modifiée de la réponse de Mohamed Saleh fonctionnait le mieux, avec quelques améliorations basées sur la documentation d'Apple. Il s'anime bien, évite les hacks ou les valeurs codées en dur et utilise des hauteurs de ligne déjà définies dans Interface Builder .

Le concept de base est de définir la hauteur de ligne sur 0 pour toutes les lignes masquées. Utilisez ensuite tableView.performBatchUpdatespour déclencher une animation qui fonctionne de manière cohérente.

Définir les hauteurs de cellule

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    if indexPath == indexPathOfHiddenCell {
        if cellIsHidden {
            return 0
        }
    }
    // Calling super will use the height set in your storyboard, avoiding hardcoded values
    return super.tableView(tableView, heightForRowAt: indexPath)
}

Vous voudrez vous assurer cellIsHiddenet indexPathOfHiddenCellsont définis de manière appropriée à votre cas d'utilisation. Pour mon code, ce sont des propriétés sur mon contrôleur de vue table.

Basculer la cellule

Quelle que soit la méthode qui contrôle la visibilité (probablement une action de bouton ou didSelectRow), basculez l'état cellIsHidden, à l'intérieur d'un performBatchUpdatesbloc:

tableView.performBatchUpdates({
                // Use self to capture for block
                self.cellIsHidden = !self.cellIsHidden 
            }, completion: nil)

Apple recommande performBatchUpdatesplus beginUpdates/endUpdates chaque fois que possible.

voleurs
la source
1

J'ai trouvé une solution pour animer les cellules de masquage dans un tableau statique.

// Class for wrapping Objective-C block
typedef BOOL (^HidableCellVisibilityFunctor)();
@interface BlockExecutor : NSObject
@property (strong,nonatomic) HidableCellVisibilityFunctor block;
+ (BlockExecutor*)executorWithBlock:(HidableCellVisibilityFunctor)block;
@end
@implementation BlockExecutor
@synthesize block = _block;
+ (BlockExecutor*)executorWithBlock:(HidableCellVisibilityFunctor)block
{
    BlockExecutor * executor = [[BlockExecutor alloc] init];
    executor.block = block;
    return executor;
}
@end

Un seul dictionnaire supplémentaire nécessaire:

@interface MyTableViewController ()
@property (nonatomic) NSMutableDictionary * hidableCellsDict;
@property (weak, nonatomic) IBOutlet UISwitch * birthdaySwitch;
@end

Et regardez l'implémentation de MyTableViewController. Nous avons besoin de deux méthodes pour convertir indexPath entre les index visibles et invisibles ...

- (NSIndexPath*)recoverIndexPath:(NSIndexPath *)indexPath
{
    int rowDelta = 0;
    for (NSIndexPath * ip in [[self.hidableCellsDict allKeys] sortedArrayUsingSelector:@selector(compare:)])
    {
        BlockExecutor * executor = [self.hidableCellsDict objectForKey:ip];
        if (ip.section == indexPath.section
            && ip.row <= indexPath.row + rowDelta
            && !executor.block())
        {
            rowDelta++;
        }
    }
    return [NSIndexPath indexPathForRow:indexPath.row+rowDelta inSection:indexPath.section];
}

- (NSIndexPath*)mapToNewIndexPath:(NSIndexPath *)indexPath
{
    int rowDelta = 0;
    for (NSIndexPath * ip in [[self.hidableCellsDict allKeys] sortedArrayUsingSelector:@selector(compare:)])
    {
        BlockExecutor * executor = [self.hidableCellsDict objectForKey:ip];
        if (ip.section == indexPath.section
            && ip.row < indexPath.row - rowDelta
            && !executor.block())
        {
            rowDelta++;
        }
    }
    return [NSIndexPath indexPathForRow:indexPath.row-rowDelta inSection:indexPath.section];
}

Une IBAction sur la modification de la valeur UISwitch:

- (IBAction)birthdaySwitchChanged:(id)sender
{
    NSIndexPath * indexPath = [self mapToNewIndexPath:[NSIndexPath indexPathForRow:1 inSection:1]];
    if (self.birthdaySwitch.on)
        [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
    else
        [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}

Certaines méthodes UITableViewDataSource et UITableViewDelegate:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    int numberOfRows = [super tableView:tableView numberOfRowsInSection:section];
    for (NSIndexPath * indexPath in [self.hidableCellsDict allKeys])
        if (indexPath.section == section)
        {
            BlockExecutor * executor = [self.hidableCellsDict objectForKey:indexPath];
            numberOfRows -= (executor.block()?0:1);
        }
    return numberOfRows;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    indexPath = [self recoverIndexPath:indexPath];
    return [super tableView:tableView cellForRowAtIndexPath:indexPath];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    indexPath = [self recoverIndexPath:indexPath];
    return [super tableView:tableView heightForRowAtIndexPath:indexPath];
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    // initializing dictionary
    self.hidableCellsDict = [NSMutableDictionary dictionary];
    [self.hidableCellsDict setObject:[BlockExecutor executorWithBlock:^(){return self.birthdaySwitch.on;}] forKey:[NSIndexPath indexPathForRow:1 inSection:1]];
}

- (void)viewDidUnload
{
    [self setBirthdaySwitch:nil];
    [super viewDidUnload];
}

@end
k06a
la source
1

Répondez rapidement :

Ajoutez la méthode suivante dans votre TableViewController:

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    return indexPathOfCellYouWantToHide == indexPath ? 0 : super.tableView(tableView, heightForRowAtIndexPath: indexPath)
}

si le tableView essaie de dessiner la cellule que vous souhaitez masquer, il ne l'affichera pas car sa hauteur sera fixée à 0pt grâce à la méthode ci-dessus, tout le reste reste inchangé.

Veuillez noter que cela indexPathOfCellYouWantToHidepeut être modifié à tout moment :)

Federico Zanetello
la source
1

Dans> Swift 2.2, j'ai combiné quelques réponses ici.

Créez une sortie à partir du storyboard pour créer un lien vers votre staticCell.

@IBOutlet weak var updateStaticCell: UITableViewCell!

override func viewDidLoad() {
    ...
    updateStaticCell.hidden = true
}

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    if indexPath.row == 0 {
        return 0
    } else {
        return super.tableView(tableView, heightForRowAtIndexPath: indexPath)
    }
}

Je veux masquer ma première cellule, donc je règle la hauteur sur 0 comme décrit ci-dessus.

CodeOverRide
la source
1

Swift 4:

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    var height = super.tableView(tableView, heightForRowAt: indexPath)
    if (indexPath.row == HIDDENROW) {
        height = 0.0
    }
    return height
}
pizzamonster
la source
0

Pour le scénario le plus simple lorsque vous masquez des cellules tout en bas de la vue tableau, vous pouvez ajuster le contentInset de tableView après avoir masqué la cellule:

- (void)adjustBottomInsetForHiddenSections:(NSInteger)numberOfHiddenSections
{
    CGFloat bottomInset = numberOfHiddenSections * 44.0; // or any other 'magic number
    self.tableView.contentInset = UIEdgeInsetsMake(self.tableView.contentInset.top, self.tableView.contentInset.left, -bottomInset, self.tableView.contentInset.right);
}
Vladimir Shutyuk
la source
0

C'est une nouvelle façon de faire cela en utilisant https://github.com/k06a/ABStaticTableViewController

NSIndexPath *ip = [NSIndexPath indexPathForRow:1 section:1];
[self deleteRowsAtIndexPaths:@[ip] withRowAnimation:UITableViewRowAnimationFade]
k06a
la source
0

La solution de k06a ( https://github.com/k06a/ABStaticTableViewController ) est meilleure car elle cache toute la section, y compris les en-têtes et les pieds de page des cellules, où cette solution ( https://github.com/peterpaulis/StaticDataTableViewController ) cache tout sauf le pied de page.

ÉDITER

Je viens de trouver une solution si vous souhaitez masquer le pied de page StaticDataTableViewController. Voici ce que vous devez copier dans le fichier StaticTableViewController.m:

- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
    if ([tableView.dataSource tableView:tableView numberOfRowsInSection:section] == 0) {
        return nil;
    } else {
        return [super tableView:tableView titleForFooterInSection:section];
    }
}

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {

    CGFloat height = [super tableView:tableView heightForFooterInSection:section];

    if (self.originalTable == nil) {
        return height;
    }

    if (!self.hideSectionsWithHiddenRows) {
        return height;
    }

    OriginalSection * os = self.originalTable.sections[section];
    if ([os numberOfVissibleRows] == 0) {
       //return 0;
        return CGFLOAT_MIN;
    } else {
        return height;
    }

    //return 0;
    return CGFLOAT_MIN;
}
pawel221
la source
0

Vous pouvez sûrement. Tout d'abord, revenez à votre tableau Afficher le nombre de cellules que vous souhaitez afficher, puis appelez superpour obtenir une certaine cellule de votre storyboard et renvoyez-le pour tableView:

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return self.mode.numberOfCells()
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = super.tableView(tableView, cellForRowAtIndexPath: self.mode.indexPathForIndexPath(indexPath))

    return cell
}

Si vos cellules ont une hauteur différente, renvoyez-la également:

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    return super.tableView(tableView, heightForRowAtIndexPath: self.mode.indexPathForIndexPath(indexPath))
}
Petr Syrov
la source
0

En plus de la solution @Saleh Masum:

Si vous obtenez des erreurs de mise en page automatique , vous pouvez simplement supprimer les contraintes dutableViewCell.contentView

Swift 3:

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    let tableViewCell = super.tableView(tableView, cellForRowAt: indexPath)

    if tableViewCell.isHidden == true
    {
        tableViewCell.contentView.removeConstraints(tableViewCell.contentView.constraints)
        return 0
    }
    else{
        return super.tableView(tableView, heightForRowAt: indexPath)
    }

}

Cette solution dépend du flux de votre application . Si vous souhaitez afficher / masquer la cellule dans la même instance de contrôleur de vue, ce n'est peut-être pas le meilleur choix, car cela supprime les contraintes .

Luca
la source
0

J'ai eu un meilleur moyen de masquer les cellules statiques et même les sections de manière dynamique sans aucun piratage.

La définition de la hauteur de ligne à 0 peut masquer une ligne, mais cela ne fonctionne pas si vous souhaitez masquer une section entière qui contiendra des espaces même si vous masquez toutes les lignes.

Mon approche consiste à créer un tableau de sections de cellules statiques. Ensuite, le contenu de la vue de table sera piloté par le tableau de sections.

Voici un exemple de code:

var tableSections = [[UITableViewCell]]()

private func configTableSections() {
    // seciton A
    tableSections.append([self.cell1InSectionA, self.cell2InSectionA])

    // section B
    if shouldShowSectionB {
        tableSections.append([self.cell1InSectionB, self.cell2InSectionB])
    }

    // section C
    if shouldShowCell1InSectionC {
        tableSections.append([self.cell1InSectionC, self.cell2InSectionC, self.cell3InSectionC])
    } else {
        tableSections.append([self.cell2InSectionC, self.cell3InSectionC])
    }
}

func numberOfSections(in tableView: UITableView) -> Int {
    return tableSections.count
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return tableSections[section].count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    return tableSections[indexPath.section][indexPath.row]
}

De cette façon, vous pouvez rassembler tout votre code de configuration sans avoir à écrire le mauvais code pour calculer le nombre de lignes et de sections. Et bien sûr, non0 hauteurs.

Ce code est également très facile à maintenir. Par exemple, si vous souhaitez ajouter / supprimer d'autres cellules ou sections.

De même, vous pouvez créer un tableau de titre d'en-tête de section et un tableau de titre de pied de page pour configurer dynamiquement vos titres de section.

Simon Wang
la source