Le paramètre de hauteur de ligne de cellule personnalisé dans le storyboard ne répond pas

213

J'essaie d'ajuster la hauteur de cellule pour l'une des cellules de ma vue de table. J'ajuste la taille à partir du paramètre "hauteur de ligne" à l'intérieur de "l'inspecteur de taille" de la cellule en question. Lorsque j'exécute l'application sur mon iPhone, la taille par défaut de la cellule est définie à partir de la "taille de ligne" dans la vue du tableau.

Si je modifie la "taille de ligne" de la vue tableau, la taille de toutes les cellules change. Je ne veux pas faire cela car je veux une taille personnalisée uniquement pour une cellule. J'ai vu beaucoup de messages qui ont une solution programmatique au problème, mais je préférerais le faire via le storyboard, si cela est possible.

zirinisp
la source

Réponses:

295

Sur les cellules dynamiques , rowHeightdéfini sur UITableView remplace toujours le rowHeight des cellules individuelles.

Mais sur les cellules statiques , rowHeightdéfinir sur des cellules individuelles peut remplacer les UITableView.

Vous ne savez pas s'il s'agit d'un bug, Apple pourrait le faire intentionnellement?

pixelfreak
la source
36
Bonne réponse n ° 3, et en particulier parce que cette réponse s'applique à Interface Builder / Storyboards. Si vous sélectionnez la cellule dans IB, l' inspecteur de taille affiche la hauteur de ligne en haut (avec une case à cocher "personnalisée"), mais si vous sélectionnez la vue entière du tableau, l' inspecteur de taille affiche également la hauteur de ligne en haut (pas de "personnalisé"). " dans ce cas). Comme le dit pixelfreak, seul le paramètre d'affichage de la table est utilisé pour les cellules dynamiques. (Je ne sais pas si c'est intentionnel)
Rhubarbe
8
cette réponse implique que la solution serait tout simplement de changer le UITableViewcontenu de Dynamic Prototypesla Static Cells, je l' ai fait, et mon projet tout fait sauter .. presque moi - même a été tué.
abbood
4
Existe-t-il un moyen de récupérer ce numéro? La seule façon de creuser en profondeur est de plonger dans le storyboard et de le retirer de là?
Biclops
Ce n'est certainement PAS un bug, car votre table est dynamique, alors comment le système pourrait-il savoir où vous êtes allé utiliser chacune de ces cellules? Et combien de fois chaque prototype serait utilisé.
Vincent Bernier
Il semble également y avoir un problème si vous configurez initialement une vue de table en tant que «cellules statiques», puis la modifiez en «prototypes dynamiques». J'ai eu un problème où même la méthode déléguée pour rowHeight était ignorée. Je l'ai d'abord résolu en modifiant directement le XML du story-board, puis j'ai finalement reconstruit la scène du story-board à partir de zéro, en utilisant des prototypes dynamiques dès le départ.
Eric Goldberg
84

Si vous utilisez UITableViewController, implémentez cette méthode:

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

Dans la fonction d'une ligne, vous pouvez choisir la hauteur. Par exemple,

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (indexPath.row == 0) {
       return 100;
    } 
    else {
       return 60;
    }
}

Dans cet exemple, la hauteur de la première ligne est de 100 pixels et les autres de 60 pixels.

J'espère que celui-ci pourra vous aider.

Beber
la source
2
Beber, faut-il TOUJOURS faire les deux (cochez Personnaliser et modifiez la valeur de la hauteur de ligne dans le storyboard && spécifiez-la dans heightForRowAtIndexPath)?
marciokoko
Pourriez-vous m'aider à ce sujet? J'ai besoin de cellules dynamiques avec une hauteur dynamique, mais si j'utilise cette méthode, peu importe ce que je retourne, toutes les cellules disparaissent à la place. Sauf sur iPhone 5 l'appareil , où il fonctionne comme prévu. Pouvez-vous comprendre pourquoi?
Ostmeistro
34

Pour les cellules dynamiques, rowHeightla valeur sur UITableViewremplace toujours les cellules individuelles 'rowHeight .

Ce comportement est, IMO, un bug. Chaque fois que vous devez gérer votre interface utilisateur à deux endroits, elle est sujette à erreur. Par exemple, si vous modifiez la taille de votre cellule dans le storyboard, vous devez vous rappeler de les modifier heightForRowAtIndexPath:également. Jusqu'à ce qu'Apple corrige le bogue, la meilleure solution de contournement actuelle consiste à remplacer heightForRowAtIndexPath:, mais utilisez les cellules prototypes réelles du storyboard pour déterminer la hauteur plutôt que d'utiliser des nombres magiques . Voici un exemple:

- (CGFloat)tableView:(UITableView *)tableView 
           heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    /* In this example, there is a different cell for
       the top, middle and bottom rows of the tableView.
       Each type of cell has a different height.
       self.model contains the data for the tableview 
    */
    static NSString *CellIdentifier;
    if (indexPath.row == 0) 
        CellIdentifier = @"CellTop";
    else if (indexPath.row + 1 == [self.model count] )
        CellIdentifier = @"CellBottom";
    else
        CellIdentifier = @"CellMiddle";

    UITableViewCell *cell = 
              [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    return cell.bounds.size.height;
}

Cela garantira que toutes les modifications apportées aux hauteurs de cellule de votre prototype seront automatiquement détectées au moment de l'exécution et vous n'aurez qu'à gérer votre interface utilisateur en un seul endroit: le storyboard.

memmons
la source
2
J'ai publié une réponse avec le code complet, y compris la mise en cache; voir stackoverflow.com/a/16881312/292166
JosephH
1
Waouh Waouh Waouh! J'ai voté pour la réponse, mais attention! Cette méthode entraîne non seulement une baisse des performances, comme l'a dit @Brennan dans les commentaires, elle entraîne également une augmentation de l'allocation de mémoire à chaque reloadData quelque chose comme une fuite de mémoire! Il est nécessaire d'utiliser la solution de contournement de lensovet ci-dessus! Passez une journée pour attraper cette fuite de mémoire!
skywinder
Ne fonctionne pas dans Swift, car cell.bounds.size.height renvoie toujours 0,0
King-Wizard
1
Les cellules n'existent pas encore au moment où le système a appelé votre méthode tableView: heightForRowAtIndexPath. Vous êtes censé pouvoir utiliser l'indexPath qui vous a été transmis pour indexer dans votre modèle de données et voir quel type de cellule sera utilisé à cet endroit, puis calculer la hauteur de cette cellule et la renvoyer. Cela permet au système de disposer les cellules dans le tableau avant de créer des cellules.
King-Wizard
1
En outre, vous ne devez pas appeler votre propre méthode cellForRowAtIndexPath. Les vues de table ont une méthode cellForRowAtIndexPath qui retournera une cellule pour ce chemin d'index si elle est actuellement visible à l'écran, mais souvent ce chemin d'index n'a pas de cellule associée.
King-Wizard
22

J'ai construit le code à partir des différentes réponses / commentaires pour que cela fonctionne pour les storyboards qui utilisent des cellules prototypes.

Ce code:

  • Ne nécessite pas que la hauteur de la cellule soit réglée ailleurs qu'à l'endroit évident dans le storyboard
  • Caches la hauteur pour des raisons de performances
  • Utilise une fonction commune pour obtenir l'identifiant de cellule d'un chemin d'index pour éviter la logique en double

Merci à Answerbot, Brennan et lensovet.

- (NSString *)cellIdentifierForIndexPath:(NSIndexPath *)indexPath
{
    NSString *cellIdentifier = nil;

    switch (indexPath.section)
    {
        case 0:
            cellIdentifier = @"ArtworkCell";
            break;
         <... and so on ...>
    }

    return cellIdentifier;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *cellIdentifier = [self cellIdentifierForIndexPath:indexPath];
    static NSMutableDictionary *heightCache;
    if (!heightCache)
        heightCache = [[NSMutableDictionary alloc] init];
    NSNumber *cachedHeight = heightCache[cellIdentifier];
    if (cachedHeight)
        return cachedHeight.floatValue;

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    CGFloat height = cell.bounds.size.height;
    heightCache[cellIdentifier] = @(height);
    return height;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *cellIdentifier = [self cellIdentifierForIndexPath:indexPath];

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];

    <... configure cell as usual...>
JosephH
la source
Je sais que c'est une vieille réponse, mais quelqu'un d'autre trouve-t-il que cela entraîne un débordement de pile? Lorsque la vue se charge, j'obtiens UISectionRowData refreshWithSection: tableViewRowData: qui appelle tableView: heightForRowAtIndexPath: qui appelle dequeueReusableCellWithIdentifier: qui appelle [UISectionRowData ...], j'ai donc un débordement de pile récursif. Je voulais vraiment utiliser cette solution, mais elle ne semble pas fonctionner avec iOS7.
sbaker
Ne fonctionne pas dans Swift, car cell.bounds.size.height renvoie toujours 0,0
King-Wizard
Les cellules n'existent pas encore au moment où le système a appelé votre méthode tableView: heightForRowAtIndexPath. Vous êtes censé pouvoir utiliser l'indexPath qui vous a été transmis pour indexer dans votre modèle de données et voir quel type de cellule sera utilisé à cet endroit, puis calculer la hauteur de cette cellule et la renvoyer. Cela permet au système de disposer les cellules du tableau avant de créer des cellules.
King-Wizard
En outre, vous ne devez pas appeler votre propre méthode cellForRowAtIndexPath. Les vues de table ont une méthode cellForRowAtIndexPath qui retournera une cellule pour ce chemin d'index si elle est actuellement visible à l'écran, mais souvent ce chemin d'index n'a pas de cellule associée.
King-Wizard
@ snow-tiger Je n'ai pas essayé ça rapidement, mais je ne comprends pas vos commentaires. Je «n'appelle pas [ma] propre méthode cellForRowAtIndexPath», et il est délibéré que je ne le fasse pas. Je suis également conscient que les cellules n'existent pas au moment où la tableView: heightForRowAtIndexPath est appelée, c'est tout l'intérêt de ce code, et pourquoi il utilise dequeueReusableCellWithIdentifier pour obtenir une cellule. Le code qui est publié dans cette réponse fonctionne. Soit il y a quelque chose de plus en cours dans swift, il y a quelque chose qui ne va pas dans la conversion rapide, soit vous essayez de résoudre un problème différent que cette question / réponse cible.
JosephH
19

Il y a en fait deux endroits où vous devez modifier la hauteur de la ligne, d'abord la cellule (vous l'avez déjà modifiée), puis sélectionnez la vue Tableau et vérifiez l'inspecteur de taille

Kristjan H.
la source
dans tableview (pas cellview)> dans l'onglet inspecteur de taille> hauteur de ligne
iman kazemayni
Me rend fou chaque fois que j'oublie ce détail. Aucune idée pourquoi l'ajustement de la cellule ne met pas à jour la tableView automatiquement lors de l'utilisation de storyboards!
Scooter
11

Je pense que c'est un bug.

Essayez d'ajuster la hauteur non pas par l'inspecteur de l'utilitaire, mais en faisant glisser la souris directement sur le storyboard.

J'ai résolu ce problème avec cette méthode.

Fogni
la source
10

Si vous utilisez swift, utilisez comme ceci. N'utilisez pas le storyboard pour sélectionner la hauteur de ligne. Définissez par programme la hauteur de ligne du tableau comme ceci,

 func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    if indexPath.row == 0 || indexPath.row == 1{
        let cell = self.tableView.dequeueReusableCellWithIdentifier("section1", forIndexPath: indexPath) as! Section1TableViewCell
        self.tableView.rowHeight = 150
        cell.label1.text = "hiiiiii"
        cell.label2.text = "Huiiilllllll"
        return cell

    } else {

        let cell = self.tableView.dequeueReusableCellWithIdentifier("section2", forIndexPath: indexPath) as! Section2TableViewCell
        self.tableView.rowHeight = 60
        cell.label3.text = "llll"
        return cell
    }

}
Chathuranga Silva
la source
Cela fonctionne, mais plus généralement vous pouvez faire:cell.sizeToFit(); self.tableView.rowHeight = cell.frame.height
Andy Chou
7

Vous pouvez obtenir la hauteur de UITableviewCell (dans UITableviewController - cellules statiques) à partir du storyboard à l'aide des lignes suivantes.

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

    return height;
}
Hiren Panchal
la source
6

Ouvrez le storyboard dans la vue XML et essayez de modifier le rowHeight attribut de l'élément souhaité.

Cela a fonctionné pour moi lorsque j'ai essayé de définir une hauteur de ligne personnalisée pour ma ligne prototypée. Cela ne fonctionne pas via l'inspecteur, mais via XML cela fonctionne.

Shtirlic
la source
1
J'ai fait un clic droit et sélectionné "ouvrir en tant que" -> "code source". Le rowHeight était déjà défini sur 120. Je pense que le storyboard a un bug et ignore la hauteur de cellule personnalisée.
zirinisp
6

Vous pouvez utiliser un prototype cellsavec une personnalisation height, puis invoquer cellForRowAtIndexPath:et renvoyer son frame.height.:.

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [self tableView:tableView
                    cellForRowAtIndexPath:indexPath];
    return cell.frame.size.height;
}
Henrik Hartz
la source
L'ajout de cette méthode a très bien fonctionné et conserve maintenant toute la personnalisation dans le storyboard comme elle devrait appartenir.
daspianist
De loin la meilleure solution
TheJeff
4

Si vous souhaitez définir une hauteur de ligne statique, vous pouvez faire quelque chose comme ceci:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 120;
}
Victor Palhares
la source
4

J'ai récemment été aux prises avec cela. Mon problème était les solutions affichées ci-dessus en utilisant leheightForRowAtIndexPath: méthode fonctionneraient pour iOS 7.1 dans le simulateur mais auraient ensuite complètement foiré les résultats en passant simplement à iOS 8.1.

J'ai commencé à en savoir plus sur les cellules à dimensionnement automatique (introduit dans iOS 8, lire ici ). Il était évident que l'utilisation de UITableViewAutomaticDimensionaiderait dans iOS 8. J'ai essayé d'utiliser cette technique et j'ai supprimé l'utilisation de heightForRowAtIndexPath:et le tour est joué, cela fonctionnait parfaitement dans iOS 8 maintenant. Mais alors iOS 7 ne l'était pas. Que devais-je faire? J'avais besoin heightForRowAtIndexPath:d'iOS 7 et non d'iOS 8.

Voici ma solution (raccourcie par souci de concision) qui emprunte à la réponse @JosephH publiée ci-dessus:

- (void)viewDidLoad {
    [super viewDidLoad];

    self.tableView.estimatedRowHeight = 50.;
    self.tableView.rowHeight = UITableViewAutomaticDimension;

    // ...
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")) {
        return UITableViewAutomaticDimension;

    } else {
        NSString *cellIdentifier = [self reuseIdentifierForCellAtIndexPath:indexPath];
        static NSMutableDictionary *heightCache;
        if (!heightCache)
            heightCache = [[NSMutableDictionary alloc] init];
        NSNumber *cachedHeight = heightCache[cellIdentifier];
        if (cachedHeight)
            return cachedHeight.floatValue;

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    CGFloat height = cell.bounds.size.height;
    heightCache[cellIdentifier] = @(height);
    return height;
    }
}

- (NSString *)reuseIdentifierForCellAtIndexPath:(NSIndexPath *)indexPath {
    NSString * reuseIdentifier;
    switch (indexPath.row) {
        case 0:
            reuseIdentifier = EventTitleCellIdentifier;
            break;
        case 2:
            reuseIdentifier = EventDateTimeCellIdentifier;
            break;
        case 4:
            reuseIdentifier = EventContactsCellIdentifier;
            break;
        case 6:
            reuseIdentifier = EventLocationCellIdentifier;
            break;
        case 8:
            reuseIdentifier = NotesCellIdentifier;
            break;
        default:
            reuseIdentifier = SeparatorCellIdentifier;
            break;
    }

    return reuseIdentifier;
}

SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO (@ "8.0") provient en fait d'un ensemble de définitions de macros que j'utilise et que j'ai trouvées quelque part (très utile). Ils sont définis comme:

#define SYSTEM_VERSION_EQUAL_TO(v)                  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
#define SYSTEM_VERSION_GREATER_THAN(v)              ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v)                 ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v)     ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)
speby
la source
2

Le même problème s'est produit lors du travail sur XCode 9 à l'aide de Swift 4 .

Ajoutez AutoLayout pour les éléments d'interface utilisateur à l'intérieur de la cellule et la hauteur de ligne de cellule personnalisée fonctionnera en conséquence, comme spécifié.

Murari Varma
la source
1
Salut, comment as-tu fait ça?
Back Packer du
Il vous suffit d'ajouter un ensemble de contraintes au bas de la cellule.
Justin
Lorsque je fais cela et que je sélectionne "automatique" dans le storyboard pour la hauteur des cellules, il veut définir toutes les hauteurs à 44 dans le storyboard bien qu'il semble correct lorsque je cours. Comment afficher correctement le storyboard?
David
1

Pour les cellules dynamiques, rowHeight défini sur UITableView remplace toujours rowHeight des cellules individuelles. Il suffit de calculer la hauteur dynamique si le contenu à l'intérieur de la ligne.

Aks
la source
0

La seule vraie solution que j'ai pu trouver est la suivante

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = ...; // Instantiate with a "common" method you'll use again in cellForRowAtIndexPath:
    return cell.frame.size.height;
}

Cela fonctionne et permet de ne pas avoir un horrible interrupteur / en cas de duplication de la logique déjà dans le StoryBoard. Je ne suis pas sûr des performances mais je suppose qu'en arrivantcellForRow: la cellule, c'est déjà aussi rapide. Bien sûr, il y a probablement des dommages collatéraux ici, mais il semble que cela fonctionne bien pour moi ici.

J'ai également posté ceci ici: https://devforums.apple.com/message/772464

EDIT: Ortwin Gentz m'a rappelé que heightForRowAtIndexPath:sera appelé pour toutes les cellules de la TableView, pas seulement les cellules visibles. Cela semble logique car iOS doit connaître la hauteur totale pour pouvoir afficher les barres de défilement appropriées. Cela signifie que c'est probablement bien sur de petites tables (comme 20 cellules), mais oubliez-les sur une table de 1000 cellules.

En outre, l'astuce précédente avec XML: identique au premier commentaire pour moi. La valeur correcte était déjà là.

StuFF mc
la source
0

Ajouté comme commentaire, mais posté comme réponse pour la visibilité:

Il semble également y avoir un problème si vous configurez initialement une vue de table en tant que «cellules statiques», puis la modifiez en «prototypes dynamiques». J'avais un problème où même la méthode déléguée pour heightForRowAtIndexPathétait ignorée. Je l'ai d'abord résolu en modifiant directement le XML du story-board, puis j'ai finalement reconstruit la scène du story-board à partir de zéro, en utilisant des prototypes dynamiques dès le départ.

Eric Goldberg
la source
0

Étant donné que je n'ai trouvé aucune solution à ce problème via Interface Builder, j'ai décidé de publier une solution programmatique au problème dans Swift en utilisant deux cellules dynamiques , même si la question initiale demandait une solution via Interface Builder. Quoi qu'il en soit, je pense que cela pourrait être utile pour la communauté Stack Overflow:

    import UIKit

    enum SignInUpMenuTableViewControllerCellIdentifier: String {
       case BigButtonCell = "BigButtonCell"
       case LabelCell = "LabelCell"
    }

    class SignInUpMenuTableViewController: UITableViewController {
            let heightCache = [SignInUpMenuTableViewControllerCellIdentifier.BigButtonCell : CGFloat(50),
                              SignInUpMenuTableViewControllerCellIdentifier.LabelCell : CGFloat(115)]

    private func cellIdentifierForIndexPath(indexPath: NSIndexPath) -> SignInUpMenuTableViewControllerCellIdentifier {
        if indexPath.row == 2 {
            return SignInUpMenuTableViewControllerCellIdentifier.LabelCell
        } else {
            return SignInUpMenuTableViewControllerCellIdentifier.BigButtonCell
        }
    }

   override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
       return self.heightCache[self.cellIdentifierForIndexPath(indexPath)]!
   }

   ...

  }
Roi-sorcier
la source
Tout cela va bien et bien, mais à moins que je ne mal interprète le code, il repose simplement sur des nombres magiques et ignore complètement la valeur que vous définissez dans IB pour les cellules dynamiques, donc ... pas exactement une solution à la question d'OP
Danny
0

Une autre chose que vous pouvez faire est d’accéder à votre plan de document, sélectionnez la vue tabulaire dans laquelle votre cellule prototype est imbriquée. Ensuite, dans l'inspecteur de taille, modifiez la hauteur de ligne de votre vue de tableau à la valeur souhaitée et décochez la case Automatique.

Amin Rezapour
la source