iOS Calculer les limites de contenu (c.-à-d. Hauteurs de cellules dynamiques)


Exemple

Un cas d'utilisation courant pour vouloir calculer la trame qu'une étiquette va prendre est de dimensionner les cellules de vue de table de manière appropriée. La méthode recommandée consiste à utiliser la méthode NSString boundingRectWithSize:options:attributes:context:

options prend les options dessin de chaîne:

  • NSStringDrawingUsesLineFragmentOrigin doit être utilisé pour les étiquettes comportant plusieurs lignes
  • NSStringDrawingTruncatesLastVisibleLine devrait être ajouté en utilisant le | opérateur s'il y a un nombre maximum de lignes

attributes est un NSDictionary d'attributs qui NSDictionary chaînes attribuées (liste complète: Apple Docs ), mais les facteurs qui affectent la hauteur sont les suivants:

  • NSFontAttributeName : Très important, la taille et la famille de polices constituent une partie critique de la taille affichée de l'étiquette.

  • NSParagraphStyleAttributeName : permet de personnaliser l'affichage du texte. Cela inclut l'interligne, l'alignement du texte, le style de troncature et quelques autres options. Si vous n'avez modifié explicitement aucune de ces valeurs, vous ne devriez pas avoir à vous soucier de cela, mais cela peut être important si vous modifiez certaines valeurs sur IB.

context doit être nil puisque le cas d'utilisation principal de NSStringDrawingContext permet à la police de redimensionner pour correspondre à un rect spécifié, ce qui ne devrait pas être le cas si nous calculons une hauteur dynamique.

Objectif c

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

    NSString *labelContent = cell.theLabel.text;
    // you may choose to get the content directly from the data source if you have done minimal customizations to the font or are comfortable with hardcoding a few values
//    NSString *labelContent = [self.dataSource objectAtIndexPath:indexPath];
    
    // value may be hardcoded if retrieved from data source
    NSFont *labelFont = [cell.theLabel font];
    
    // The NSParagraphStyle, even if you did not code any changes these values may have been altered in IB
    NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
    paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping; 
    paragraphStyle.alignment = NSTextAlignmentCenter;

    NSDictionary *attributes = @{NSFontAttributeName: labelFont,
                                 NSParagraphStyleAttributeName: paragraphStyle};

    // The width is also important to the height
    CGFloat labelWidth = CGRectGetWidth(cell.theLabel.frame);
    // If you have been hardcoding up to this point you will be able to get this value by subtracting the padding on left and right from tableView.bounds.size.width
//    CGFloat labelWidth = CGRectGetWidth(tableView.frame) - 20.0f - 20.0f;

    CGRect bodyBounds = [labelContent boundingRectWithSize:CGSizeMake(width, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil];

    return CGRectGetHeight(bodyBounds) + heightOfObjectsOnTopOfLabel + heightOfObjectBelowLabel;
}

Swfit 3

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    var cell = tableView.cellForRow(atIndexPath: indexPath)!
    var labelContent = cell.theLabel.text
    var labelFont = cell.theLabel.font
    var paragraphStyle = NSMutableParagraphStyle()

    paragraphStyle.lineBreakMode = .byWordWrapping
    paragraphStyle.alignment = .center

    var attributes = [NSFontAttributeName: labelFont, NSParagraphStyleAttributeName: paragraphStyle]

    var labelWidth: CGFloat = cell.theLabel.frame.width

    var bodyBounds = labelContent.boundingRect(withSize: CGSize(width: width, height: CGFLOAT_MAX), options: .usesLineFragmentOrigin, attributes: attributes, context: nil)

    return bodyBounds.height + heightOfObjectsOnTopOfLabel + heightOfObjectBelowLabel
}

À l'inverse, si vous avez défini un nombre maximum de lignes, vous devrez d'abord calculer la hauteur d'une seule ligne pour vous assurer de ne pas obtenir une valeur supérieure à la taille autorisée:

    // We calculate the height of a line by omitting the NSStringDrawingUsesLineFragmentOrigin option, which will assume an infinitely wide label
    CGRect singleLineRect = [labelContent boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)
                                                 options:NSStringDrawingTruncatesLastVisibleLine
                                                 context:nil];
    CGFloat lineHeight = CGRectGetHeight(singleLineRect);
    CGFloat maxHeight = lineHeight * cell.theLabel.numberOfLines;

    // Now you can call the method appropriately
    CGRect bodyBounds = [labelContent boundingRectWithSize:CGSizeMake(width, maxHeight) options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingTruncatesLastVisibleLine) attributes:attributes context:nil];

    return CGRectGetHeight(bodyBounds) + heightOfObjectsOnTopOfLabel + heightOfObjectBelowLabel;