web-dev-qa-db-fra.com

Calcul de la taille du texte UILabel

Je dessine UILabels par programme. Ils obtiennent leurs tailles d'une base de données. Je ne peux donc pas simplement utiliser sizeToFit. J'ai déjà implémenté une fonction qui redessine UILabels avec un ratio passé. Donc, tout ce que j'ai besoin de trouver, c’est le texte dans UILabel de mon point de vue qui exigerait le ratio maximum pour redessiner UILabels. Donc, finalement, je dois faire quelque chose comme ceci:

    double ratio = 1.00;
    for (UILabel* labels in sec.subviews) {

        float widthLabel = labels.frame.size.width;
        float heightLabel = labels.frame.size.height;
        float heightText = //get the text height here
        float widthText = //get the text width here
        if (widthLabel < widthText) {
            ratio = MAX(widthText/widthLabel,ratio);
        }
        if (heightLabel < heightText) {
            ratio = MAX(heightText/heightLabel, ratio);
        }
    }
    //redraw UILabels with the given ratio here

Alors, comment puis-je obtenir la hauteur et la largeur d'un texte, certains de mes textes ne rentrant pas dans l'étiquette, je ne peux pas simplement utiliser les limites de l'étiquette? J'utilise Xcode 5 et iOS 7.

49
Sarp Kaya

Le problème avec 

CGRect r = [text boundingRectWithSize:CGSizeMake(200, 0)
                              options:NSStringDrawingUsesLineFragmentOrigin
                           attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:fontSize]}
                              context:nil];

est boundingRectWithSize qui détermine la valeur maximale que CGRect peut avoir.

Ma solution à ce problème est de vérifier si elle dépasse, sinon le texte peut tenir dans l'étiquette. Je l'ai fait en utilisant des boucles.

NSString *text = @"This is a long sentence. Wonder how much space is needed?";
CGFloat width = 100;
CGFloat height = 100;
bool sizeFound = false;
while (!sizeFound) {
    NSLog(@"Begin loop");
    CGFloat fontSize = 14;
    CGFloat previousSize = 0.0;
    CGFloat currSize = 0.0;
    for (float fSize = fontSize; fSize < fontSize+6; fSize++) {
        CGRect r = [text boundingRectWithSize:CGSizeMake(width, height)
                                      options:NSStringDrawingUsesLineFragmentOrigin
                                   attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:fSize]}
                                      context:nil];
        currSize =r.size.width*r.size.height;
        if (previousSize >= currSize) {
            width = width*11/10;
            height = height*11/10;
            fSize = fontSize+10;
        }
        else {
            previousSize = currSize;
        }
        NSLog(@"fontSize = %f\tbounds = (%f x %f) = %f",
              fSize,
              r.size.width,
              r.size.height,r.size.width*r.size.height);
    }
    if (previousSize == currSize) {
        sizeFound = true;
    }

}
NSLog(@"Size found with width %f and height %f", width, height);

Après chaque itération, la taille et la largeur augmentent de 10% de sa valeur.

La raison pour laquelle j'ai choisi 6, c'est parce que je ne voulais pas que l'étiquette soit trop épaisse.

Pour une solution n'utilisant pas de boucles:

NSString *text = @"This is a long sentence. Wonder how much space is needed?";
CGFloat width = 100;
CGFloat height = 100;

CGFloat currentFontSize = 12;
CGRect r1 = [text boundingRectWithSize:CGSizeMake(width, height)
                              options:NSStringDrawingUsesLineFragmentOrigin
                           attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:currentFontSize+6]}
                              context:nil];

CGRect r2 = [text boundingRectWithSize:CGSizeMake(width, height)
                               options:NSStringDrawingUsesFontLeading
                            attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:currentFontSize+6]}
                               context:nil];

CGFloat firstVal =r1.size.width*r1.size.height;
CGFloat secondVal =r2.size.width*r2.size.height;

NSLog(@"First val %f and second val is %f", firstVal, secondVal);

if (secondVal > firstVal) {
    float initRat = secondVal/firstVal;

    float ratioToBeMult = sqrtf(initRat);

    width *= ratioToBeMult;
    height *= ratioToBeMult;
}

NSLog(@"Final width %f and height %f", width, height);

//for verifying
for (NSNumber *n in @[@(12.0f), @(14.0f), @(17.0f)]) {
    CGFloat fontSize = [n floatValue];
    CGRect r = [text boundingRectWithSize:CGSizeMake(width, height)
                                  options:NSStringDrawingUsesLineFragmentOrigin
                               attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:fontSize]}
                                  context:nil];
    NSLog(@"fontSize = %f\tbounds = (%f x %f) = %f",
          fontSize,
          r.size.width,
          r.size.height,r.size.width*r.size.height);
    firstVal =r.size.width*r.size.height;
}

Où la dernière boucle est la preuve qu'une police plus grande peut donner un résultat de taille supérieure.

2
Sarp Kaya

Toutes les méthodes [NSString sizeWithFont...] sont obsolètes dans iOS 7. Utilisez plutôt cette option.

CGRect labelRect = [text
                    boundingRectWithSize:labelSize
                    options:NSStringDrawingUsesLineFragmentOrigin
                    attributes:@{
                     NSFontAttributeName : [UIFont systemFontOfSize:14]
                    }
                    context:nil];

Voir aussi https://developer.Apple.com/documentation/foundation/nsstring/1619914-sizewithfont .

UPDATE - exemple de sortie boundingRectWithSize

Par votre commentaire, j'ai fait un test simple. Le code et la sortie sont ci-dessous.

// code to generate a bounding rect for text at various font sizes
NSString *text = @"This is a long sentence. Wonder how much space is needed?";
for (NSNumber *n in @[@(12.0f), @(14.0f), @(18.0f)]) {
    CGFloat fontSize = [n floatValue];
    CGRect r = [text boundingRectWithSize:CGSizeMake(200, 0)
                                  options:NSStringDrawingUsesLineFragmentOrigin
                               attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:fontSize]}
                                  context:nil];
    NSLog(@"fontSize = %f\tbounds = (%f x %f)",
          fontSize,
          r.size.width,
          r.size.height);
}

cela produit la sortie suivante (notez que les limites changent comme prévu à mesure que la taille de la police augmente):

fontSize = 12.000000    bounds = (181.152008 x 28.632000)
fontSize = 14.000000    bounds = (182.251999 x 50.105999)
fontSize = 18.000000    bounds = (194.039993 x 64.421997)
64
XJones

Longueur obtient le nombre de caractères. Si vous voulez obtenir la largeur du texte:

Objectif c

CGSize textSize = [label.text sizeWithAttributes:@{NSFontAttributeName:[label font]}];

Swift 4

let size = label.text?.size(withAttributes: [.font: label.font]) ?? .zero

Cela vous donne la taille. Et vous pouvez comparer le textSize.width de chaque étiquette.

47
H. Serdar Çınar

Un autre moyen simple de faire cela que je n'ai pas encore vu mentionné:

CGSize textSize = [label intrinsicContentSize];

(Cela ne fonctionne correctement qu'après que vous ayez défini le texte et la police de l'étiquette, bien sûr.)

24
poff

Voici une variante de Swift.

let font = UIFont(name: "HelveticaNeue", size: 25)!
let text = "This is some really long text just to test how it works for calculating heights in Swift of string sizes. What if I add a couple lines of text?"

let textString = text as NSString

let textAttributes = [NSFontAttributeName: font]

textString.boundingRectWithSize(CGSizeMake(320, 2000), options: .UsesLineFragmentOrigin, attributes: textAttributes, context: nil)
15
Collin

Petit conseil les gars, si comme moi vous utilisez, boundingRectWithSize with [UIFont systemFontOFSize:14] 

Si votre chaîne est longue de deux lignes, la hauteur du rectangle retournée est quelque chose comme 33,4 points.

Ne commettez pas l'erreur, comme moi, de la transformer en un int, car 33,4 devient 33 et l'étiquette de hauteur 33 points passe de deux à une ligne!

3
Martin

En utilisant cette ligne de code, nous pouvons obtenir la taille du texte sur l'étiquette.

let str = "Sample text"
let size = str.sizeWithAttributes([NSFontAttributeName:UIFont.systemFontOfSize(17.0)])

Donc, nous pouvons utiliser à la fois la largeur et la hauteur.

1
Ramakrishna

Une solution qui fonctionne avec des étiquettes multilignes (Swift 4), pour calculer la hauteur à partir d’une largeur fixe:

let label = UILabel(frame: .zero)
label.numberOfLines = 0 // multiline
label.font = UIFont.systemFont(ofSize: UIFont.labelFontSize) // your font
label.preferredMaxLayoutWidth = width // max width
label.text = "This is a sample text.\nWith a second line!" // the text to display in the label

let height = label.intrinsicContentSize.height
0
chrisben

msgStr string obtenir la taille:

let msgStr:NSString = Data["msg"]! as NSString
let messageSize = msgStr.boundingRect(with: CGSize(width: ChatTable.frame.width-116, height: CGFloat.infinity), options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName:UIFont(name: "Montserrat-Light", size: 14)!], context: nil).size
0
Jayesh Miruliya

Swift 3.0

func getLabelHeight() -> CGFloat {
    let font = UIFont(name: "OpenSans", size: 15)!
    let textString = "Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." as NSString

    let textAttributes = [NSFontAttributeName: font]

    let rect = textString.boundingRect(with: CGSize(width: 320, height: 2000), options: .usesLineFragmentOrigin, attributes: textAttributes, context: nil)
    return rect.size.height
}
0
Abhishek Jain