48 votes

Multiline UILabel avec adjustsFontSizeToFitWidth

J'ai une multiligne UILabel dont la taille de police que j'aimerais régler en fonction de la longueur du texte. L'ensemble du texte devrait correspondre à l'étiquette de l'image sans la tronquant.

Malheureusement, selon la documentation de l' adjustsFontSizeToFitWidth propriété "n'est efficace que lorsque l' numberOfLines propriété est définie à 1".

J'ai essayé de déterminer le réglage de la taille de la police à l'aide de

-[NSString (CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode]

et puis la décrémentation de la taille de la police jusqu'à ce qu'il s'adapte. Malheureusement, cette méthode interne tronque le texte pour l'adapter à la taille spécifiée et renvoie la taille de la chaîne tronquée.

51voto

Ortwin Gentz Points 15102

Dans cette question, 0x90 fournit une solution qui - bien qu'un peu moche - fait ce que je veux. Plus précisément, il traite correctement avec la situation qu'un seul mot ne correspond pas à la largeur initiale lors de la taille de la police. J'ai légèrement modifié le code pour qu'il fonctionne comme une catégorie sur NSString:

- (CGFloat)fontSizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size {
    CGFloat fontSize = [font pointSize];
    CGFloat height = [self sizeWithFont:font constrainedToSize:CGSizeMake(size.width,FLT_MAX) lineBreakMode:UILineBreakModeWordWrap].height;
    UIFont *newFont = font;

    //Reduce font size while too large, break if no height (empty string)
    while (height > size.height && height != 0) {   
        fontSize--;  
        newFont = [UIFont fontWithName:font.fontName size:fontSize];   
        height = [self sizeWithFont:newFont constrainedToSize:CGSizeMake(size.width,FLT_MAX) lineBreakMode:UILineBreakModeWordWrap].height;
    };

    // Loop through words in string and resize to fit
    for (NSString *word in [self componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]) {
        CGFloat width = [word sizeWithFont:newFont].width;
        while (width > size.width && width != 0) {
            fontSize--;
            newFont = [UIFont fontWithName:font.fontName size:fontSize];   
            width = [word sizeWithFont:newFont].width;
        }
    }
    return fontSize;
}

Pour l'utiliser avec un UILabel:

    CGFloat fontSize = [label.text fontSizeWithFont:[UIFont boldSystemFontOfSize:15] constrainedToSize:label.frame.size];
    label.font = [UIFont boldSystemFontOfSize:fontSize];

EDIT: correction du code pour initialiser newFont avec font. Correction d'un crash dans certaines circonstances.

4voto

Niels Points 211

Le code fourni dans cet article de blog traite également de cette question. Cependant j'ai trouvé que la boucle dans toutes les tailles de police était un peu trop lent si vous avez besoin de calculer la taille de la police pour plusieurs étiquettes à la fois, j'ai donc modifié un peu le code, et il dirige maintenant beaucoup plus rapide.

Le seul problème avec ma solution, pour autant que je sais, c'est qu'il ne tient pas compte de la situation où un seul mot ne correspond pas à la largeur initiale lors de la taille de la police, comme l'a souligné Ortwin.

Donc, si vous êtes à la recherche d'une solution qui fonctionne plus rapidement, essayez ceci. (Il fonctionne comme une catégorie sur UIFont)

+ (UIFont*)fontWithName:(NSString *)fontName minSize:(CGFloat)minSize maxSize:(CGFloat)maxSize constrainedToSize:(CGSize)labelSize forText:(NSString*)text {

UIFont* font = [UIFont fontWithName:fontName size:maxSize];

CGSize constraintSize = CGSizeMake(labelSize.width, MAXFLOAT);
NSRange range = NSMakeRange(minSize, maxSize);

int fontSize = 0;
for (NSInteger i = maxSize; i > minSize; i--)
{
    fontSize = ceil(((float)range.length + (float)range.location) / 2.0);

    font = [font fontWithSize:fontSize];
    CGSize size = [text sizeWithFont:font constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];

    if (size.height <= labelSize.height) 
        range.location = fontSize;
    else 
        range.length = fontSize - 1;

    if (range.length == range.location) 
    {
        font = [font fontWithSize:range.location];
        break;
    }
}

return font;
}

3voto

Everton Cunha Points 145

Merci, avec ça et un peu plus de la part de quelqu'un d'autre j'ai fait cette coutume UILabel, le respect de la taille minimale de la police et il y a un bonus option pour aligner le texte vers le haut.

h:

@interface EPCLabel : UILabel {
    float originalPointSize;
    CGSize originalSize;
}

@property (nonatomic, readwrite) BOOL alignTextOnTop;
@end

m:

#import "EPCLabel.h"

@implementation EPCLabel
@synthesize alignTextOnTop;

-(void)verticalAlignTop {
    CGSize maximumSize = originalSize;
    NSString *dateString = self.text;
    UIFont *dateFont = self.font;
    CGSize dateStringSize = [dateString sizeWithFont:dateFont 
                                   constrainedToSize:CGSizeMake(self.frame.size.width, maximumSize.height)
                                       lineBreakMode:self.lineBreakMode];

    CGRect dateFrame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, dateStringSize.height);

    self.frame = dateFrame;
}

- (CGFloat)fontSizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size {
    CGFloat fontSize = [font pointSize];
    CGFloat height = [self.text sizeWithFont:font             
                           constrainedToSize:CGSizeMake(size.width,FLT_MAX)  
                               lineBreakMode:UILineBreakModeWordWrap].height;
    UIFont *newFont = font;

    //Reduce font size while too large, break if no height (empty string)
    while (height > size.height && height != 0 && fontSize > self.minimumFontSize) { 
        fontSize--;  
        newFont = [UIFont fontWithName:font.fontName size:fontSize];   
        height = [self.text sizeWithFont:newFont  
                       constrainedToSize:CGSizeMake(size.width,FLT_MAX) 
                           lineBreakMode:UILineBreakModeWordWrap].height;
    };

    // Loop through words in string and resize to fit
    if (fontSize > self.minimumFontSize) {
        for (NSString *word in [self.text componentsSeparatedByString:@" "]) {
            CGFloat width = [word sizeWithFont:newFont].width;
            while (width > size.width && width != 0 && fontSize > self.minimumFontSize) {
                fontSize--;
                newFont = [UIFont fontWithName:font.fontName size:fontSize];   
                width = [word sizeWithFont:newFont].width;
            }
        }
    }
    return fontSize;
}

-(void)setText:(NSString *)text {
    [super setText:text];
    if (originalSize.height == 0) {
        originalPointSize = self.font.pointSize;
        originalSize = self.frame.size;
    }

    if (self.adjustsFontSizeToFitWidth && self.numberOfLines > 1) {
        UIFont *origFont = [UIFont fontWithName:self.font.fontName size:originalPointSize];
        self.font = [UIFont fontWithName:origFont.fontName size:[self fontSizeWithFont:origFont constrainedToSize:originalSize]];
    }

    if (self.alignTextOnTop) [self verticalAlignTop];
}

-(void)setAlignTextOnTop:(BOOL)flag {
    alignTextOnTop = YES;
    if (alignTextOnTop && self.text != nil)
        [self verticalAlignTop];
}

@end

J'espère que cela aide.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X