web-dev-qa-db-fra.com

Hauteur de cellule UITableView dynamique basée sur le contenu

J'ai une UITableView remplie de cellules personnalisées (héritée de UITableViewCell), chaque cellule contient une UIWebView qui est automatiquement redimensionnée en fonction de son contenu. Voici comment je peux changer la hauteur des cellules UITableView en fonction de leur contenu (variable webView).

La solution doit être dynamique car le code HTML utilisé pour renseigner la variable UIWebViews est analysé à partir d'un flux en constante évolution.

J'ai le sentiment que je dois utiliser la méthode de délégué UITableViewheightForRowAtIndexPath mais à partir de sa définition:

    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    ;//This needs to be variable
}

Je ne peux pas accéder à la cellule ou à son contenu. Puis-je changer la hauteur de la cellule dans cellForRowAtIndexPath?

Toute aide serait grande. Merci.

Remarque

J'ai posé cette question il y a plus de 2 ans. Avec l'introduction de la mise en page automatique, la meilleure solution pour iOS7 peut être trouvée:

http://t.co/PdUywcTjhu

et sur iOS8, cette fonctionnalité est intégrée au SDK.

21
Adam Waite

Le meilleur moyen que j’ai trouvé pour la hauteur dynamique est de la calculer à l’avance et de la stocker dans une collection (probablement un tableau). En supposant que la cellule contienne principalement du texte, vous pouvez utiliser -[NSString sizeWithFont:constrainedToSize:lineBreakMode:] pour calculer la hauteur, puis renvoyer la valeur correspondante dans heightForRowAtIndexPath:

[EDIT] Si le contenu change constamment, vous pouvez implémenter une méthode qui met à jour le tableau de hauteurs lorsque de nouvelles données sont fournies.

19
Nyth

Cela fonctionne généralement plutôt bien:

Objectif c:

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

Rapide:

override func tableView(tableView: UITableView!, heightForRowAtIndexPath indexPath: NSIndexPath!) -> CGFloat {
    return UITableViewAutomaticDimension;
}
23
Santa Claus

J'ai essayé plusieurs solutions, mais celle qui a fonctionné était celle-ci, suggérée par un ami:

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

    int height = [StringUtils findHeightForText:yourLabel havingWidth:yourWidth andFont:[UIFont systemFontOfSize:17.0f]];

    height += [StringUtils findHeightForText:yourOtherLabel havingWidth:yourWidth andFont:[UIFont systemFontOfSize:14.0f]];

    return height + CELL_SIZE_WITHOUT_LABELS; //important to know the size of your custom cell without the height of the variable labels
}

La classe StringUtils.h:

#import <Foundation/Foundation.h>

@interface StringUtils : NSObject

+ (CGFloat)findHeightForText:(NSString *)text havingWidth:(CGFloat)widthValue andFont:(UIFont *)font;

@end

StringUtils.m class:

#import "StringUtils.h"

@implementation StringUtils

+ (CGFloat)findHeightForText:(NSString *)text havingWidth:(CGFloat)widthValue andFont:(UIFont *)font {
    CGFloat result = font.pointSize+4;
    if (text) {
        CGSize size;

        CGRect frame = [text boundingRectWithSize:CGSizeMake(widthValue, CGFLOAT_MAX)
                                          options:NSStringDrawingUsesLineFragmentOrigin
                                       attributes:@{NSFontAttributeName:font}
                                          context:nil];
        size = CGSizeMake(frame.size.width, frame.size.height+1);
        result = MAX(size.height, result); //At least one row
    }
    return result;
}

@end

Cela a fonctionné parfaitement pour moi. J'avais une cellule personnalisée avec 3 images de taille fixe, 2 étiquettes de taille fixe et 2 étiquettes variables. Travaillé comme un charme. J'espère que ça marchera pour toi aussi.

Cordialement, Alexandre.

5
Alexandre
self.tblVIew.estimatedRowHeight = 500.0; // put max you expect here.
self.tblVIew.rowHeight = UITableViewAutomaticDimension;
5
user3693546

Le gros problème avec les cellules de hauteur dynamique dans iOS est que la table vc doit calculer et renvoyer une hauteur de chaque cellule avant que les cellules ne soient dessinées. Avant de dessiner une cellule, elle n’a pas de cadre et donc pas de largeur. Cela pose un problème si votre cellule doit modifier sa hauteur en fonction, par exemple, de la quantité de texte dans le libellé textLabel, puisque vous ne connaissez pas sa largeur.

Une solution courante que j'ai vue est que les gens définissent une valeur numérique pour la largeur de la cellule. C'est une mauvaise approche, car les tableaux peuvent être simples ou groupés, utiliser le style iOS 7 ou iOS 6, être affichés sur un iPhone ou un iPad, en mode paysage ou portrait, etc.

J'ai eu du mal à résoudre ces problèmes dans une de mes applications iOS, qui prend en charge iOS5 +, ainsi que les iPhone et iPad à orientations multiples. J'avais besoin d'un moyen pratique d'automatiser cela et de laisser la logique en dehors du contrôleur de vue. Le résultat est devenu une sous-classe UITableViewController (afin de pouvoir conserver l'état) qui prend en charge les cellules par défaut (style Par défaut et Sous-titre) ainsi que les cellules personnalisées.

Vous pouvez le récupérer sur GitHub ( https://github.com/danielsaidi/AutoSizeTableView ). J'espère que cela aidera ceux d'entre vous qui ont encore du mal à résoudre ce problème. Si vous le vérifiez, j'aimerais entendre ce que vous pensez et si cela a fonctionné pour vous.

3
Daniel Saidi

Voici le code que j'ai utilisé pour la hauteur de cellule dynamique lors de la récupération de tweets sur Twitter, puis de leur stockage dans CoreData pour une lecture hors connexion.

Cela montre non seulement comment obtenir le contenu de la cellule et des données, mais aussi comment dimensionner dynamiquement un UILabel au contenu avec un remplissage.

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

    Tweet *Tweet = [self.fetchedResultsController objectAtIndexPath:indexPath];

    NSString* text = Tweet.Text;

    TweetTableViewCell *cell = (TweetTableViewCell*)[self tableView:tableView cellForRowAtIndexPath:indexPath];

    //Set the maximum size
    CGSize maximumLabelSize = cell.tweetLabel.frame.size;
    CGPoint originalLocation = cell.tweetLabel.frame.Origin;

    //Calculate the new size based on the text
    CGSize expectedLabelSize = [text sizeWithFont:cell.tweetLabel.font constrainedToSize:maximumLabelSize lineBreakMode:cell.tweetLabel.lineBreakMode];


    //Dynamically figure out the padding for the cell
    CGFloat topPadding = cell.tweetLabel.frame.Origin.y - cell.frame.Origin.y;


    CGFloat bottomOfLabel = cell.tweetLabel.frame.Origin.y + cell.tweetLabel.frame.size.height;
    CGFloat bottomPadding = cell.frame.size.height - bottomOfLabel;


    CGFloat padding = topPadding + bottomPadding;


    CGFloat topPaddingForImage = cell.profileImage.frame.Origin.y - cell.frame.Origin.y;
    CGFloat minimumHeight = cell.profileImage.frame.size.height + topPaddingForImage + bottomPadding;

    //adjust to the new size
    cell.tweetLabel.frame = CGRectMake(originalLocation.x, originalLocation.y, cell.tweetLabel.frame.size.width, expectedLabelSize.height);


    CGFloat cellHeight = expectedLabelSize.height + padding;

    if (cellHeight < minimumHeight) {

        cellHeight = minimumHeight;
    }

    return cellHeight;
}
3
Joseph DeCarlo

Aussi, je pense qu'un tel algorithme vous conviendra:

1) dans cellForrowAtIndexPath, vous activez le chargement de vos vues Web et leur attribuez des balises égales à indexPath.row.

2) dans webViewDidFinishLoading, vous calculez la hauteur du contenu de la cellule et composez un dictionnaire avec des clés et des valeurs telles que: clé = indexPath.row valeur = hauteur

3) appelez [tableview reloadData] 

4) dans [tableview cellForRowAtIndexPath: indexPath] définissez les hauteurs appropriées pour les cellules correspondantes

3
alex

Ceci est l'une de mes solutions de Nice. ça a fonctionné pour moi.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    cell.textLabel.text = [_nameArray objectAtIndex:indexPath.row];
    cell.textLabel.numberOfLines = 0;
    cell.textLabel.lineBreakMode = NSLineBreakByWordWrapping;
}

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

Nous devons appliquer ces 2 changements.

1)cell.textLabel.numberOfLines = 0;
  cell.textLabel.lineBreakMode = NSLineBreakByWordWrapping;

2)return UITableViewAutomaticDimension;
1
iOS

J'implémente toujours cela dans toutes mes cellules dans une classe de super-cellules car pour une raison quelconque, UITableViewAutomaticDimension ne fonctionne pas très bien.

-(CGFloat)cellHeightWithData:(id)data{
    CGFloat height = [[self contentView] systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
    [self fillCellWithData:data]; //set the label's text or anything that may affect the size of the cell
    [self layoutIfNeeded];
    height = [[self contentView] systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
    return height+1; //must add one because of the cell separator
}

il suffit d'appeler cette méthode sur votre -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath en utilisant une cellule factice.

remarque: cela fonctionne uniquement avec autolayout, mais cela fonctionne également avec ios 7 et versions ultérieures.

pd: n'oubliez pas de cocher la case "largeur préférée explicite" dans la xib ou le storyboard et de définir la largeur statique (dans le menu cmd + alt + 5 menu) 

0
user2387149

Swift Utilisez des cellules et des étiquettes personnalisées. Configurez les contraintes pour UILabel. (en haut, à gauche, en bas, à droite) Définit les lignes de UILabel sur 0

Ajoutez le code suivant dans la méthode viewDidLoad du ViewController:

tableView.estimatedRowHeight = 68.0
tableView.rowHeight = UITableViewAutomaticDimension

// Délégué & source de données

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    return UITableViewAutomaticDimension;
}
0
user7587085

J'ai eu un très gros test à UILabel. Surtout ne pas travailler, alors je crée une catégorie pour chaîne comme ci-dessous et a la hauteur exacte

- (CGFloat)heightStringWithEmojifontType:(UIFont *)uiFont ForWidth:(CGFloat)width {

// Get text
CFMutableAttributedStringRef attrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
CFAttributedStringReplaceString (attrString, CFRangeMake(0, 0), (CFStringRef) self );
CFIndex stringLength = CFStringGetLength((CFStringRef) attrString);

// Change font
CTFontRef ctFont = CTFontCreateWithName((__bridge CFStringRef) uiFont.fontName, uiFont.pointSize, NULL);
CFAttributedStringSetAttribute(attrString, CFRangeMake(0, stringLength), kCTFontAttributeName, ctFont);

// Calc the size
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrString);
CFRange fitRange;
CGSize frameSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRangeMake(0, 0), NULL, CGSizeMake(width, CGFLOAT_MAX), &fitRange);

CFRelease(ctFont);
CFRelease(framesetter);
CFRelease(attrString);

return frameSize.height + 10;}
0
GSK