2010-12-08 3 views
39

У меня есть многострочный UILabel, размер шрифта которого я бы хотел настроить в зависимости от длины текста. Весь текст должен вставляться в рамку метки без его усечения.Многострочный UILabel с настройкамиFontSizeToFitWidth

К сожалению, в соответствии с документацией свойство adjustsFontSizeToFitWidth "действует только в том случае, если для свойства numberOfLines установлено значение 1".

Я попытался определить скорректированный размер шрифта, используя

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

, а затем декремент размера шрифта, пока он не подходит. К сожалению, этот метод внутренне усекает текст, чтобы он вписывался в указанный размер и возвращал размер результирующей усеченной строки.

ответ

48

В this question 0x90 обеспечивает решение, которое - хотя и немного уродливое - делает то, что я хочу. В частности, он правильно относится к ситуации, когда одно слово не соответствует ширине при первоначальном размере шрифта. Я немного изменил код так, что он работает как категория на 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; 
} 

Чтобы использовать его с UILabel:

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

EDIT: Исправлен код инициализации newFont с font , Исправляет сбой при определенных обстоятельствах.

+1

я нашел более точным, чтобы использовать '[собственного componentsSeparatedByCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]]' вместо просто разбивая пространство. – Jilouc

+0

Jilouc, спасибо, хороший совет. Я обновил код. –

+5

Это замечательно. Я добавил поддержку минимального размера шрифта и категории на 'UILabel' для удобства и [загрузил его как сущность] (https://gist.github.com/2766074) для всех, кто интересуется. – kevboh

3

Спасибо, с этим и немного больше от кого-то другого, я сделал это обычное UILabel, которое будет уважать минимальный размер шрифта, и есть бонусная опция для выравнивания текста до вершины.

ч:

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

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

м:

#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 

Я надеюсь, что это помогает.

+0

Блестящий !! Спасибо. – Smikey

+0

Однако при всех этих подходах, если ваш текст не будет вписываться в указанное количество строк при стартовом (теперь максимальном) размере шрифта, вызов с ограничениемToSize с удовольствием обрезает текст для вас на основе разрыва строки, чтобы соответствовать ширина. таким образом, вы в конечном итоге сокращаетесь только для того, чтобы соответствовать тому, что было оставлено, а не тому, что я думаю, что вы действительно хотите, что «сжимается, чтобы вместить весь этот текст в n-число строк». –

4

Код, указанный в this blog post, также решает эту проблему. Однако я обнаружил, что циклический выбор всех возможных размеров шрифта был слишком медленным, если вам нужно рассчитать размеры шрифтов для многих ярлыков одновременно, поэтому я немного изменил код, и теперь он работает намного быстрее.

Единственная проблема с моим решением, насколько я знаю, заключается в том, что он не учитывает ситуацию, когда одно слово не соответствует ширине при первоначальном размере шрифта, как указывает Ортвин.

Так что если вы ищете решение, которое работает быстрее, попробуйте это. (Он работает в качестве категории по 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; 
} 
+1

Поскольку 'sizeWithFont:' устарел от iOS 7, вы должны заменить его на 'boundingRectWithSize:'. – AppsolutEinfach

4

В некоторых случаях изменение «разрывов строк» ​​от «Word Wrap» на «усечение хвоста» может быть все, что вам нужно, если вы знаете, сколько строк вы хотите (например, «2»): Кредит: Becky Hansmeyer

1

В комментариях предусмотрено расширение ObjC, которое вычисляет шрифты, необходимые для соответствия многострочному тексту в UILabel. Он был переписан в Swift (так как он 2016):

// 
// NSString+KBAdditions.swift 
// 
// Created by Alexander Mayatsky on 16/03/16. 
// 
// Original code from http://stackoverflow.com/a/4383281/463892 & http://stackoverflow.com/a/18951386 
// 

import Foundation 
import UIKit 

protocol NSStringKBAdditions { 
    func fontSizeWithFont(font: UIFont, constrainedToSize size: CGSize, minimumScaleFactor: CGFloat) -> CGFloat 
} 

extension NSString : NSStringKBAdditions { 
    func fontSizeWithFont(font: UIFont, constrainedToSize size: CGSize, minimumScaleFactor: CGFloat) -> CGFloat { 
     var fontSize = font.pointSize 
     let minimumFontSize = fontSize * minimumScaleFactor 


     var attributedText = NSAttributedString(string: self as String, attributes:[NSFontAttributeName: font]) 
     var height = attributedText.boundingRectWithSize(CGSize(width: size.width, height: CGFloat.max), options:NSStringDrawingOptions.UsesLineFragmentOrigin, context:nil).size.height 

     var newFont = font 
     //Reduce font size while too large, break if no height (empty string) 
     while (height > size.height && height != 0 && fontSize > minimumFontSize) { 
      fontSize--; 
      newFont = UIFont(name: font.fontName, size: fontSize)! 

      attributedText = NSAttributedString(string: self as String, attributes:[NSFontAttributeName: newFont]) 
      height = attributedText.boundingRectWithSize(CGSize(width: size.width, height: CGFloat.max), options:NSStringDrawingOptions.UsesLineFragmentOrigin, context:nil).size.height 
     } 

     // Loop through words in string and resize to fit 
     for word in self.componentsSeparatedByCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()) { 
      var width = word.sizeWithAttributes([NSFontAttributeName:newFont]).width 
      while (width > size.width && width != 0 && fontSize > minimumFontSize) { 
       fontSize-- 
       newFont = UIFont(name: font.fontName, size: fontSize)! 
       width = word.sizeWithAttributes([NSFontAttributeName:newFont]).width 
      } 
     } 
     return fontSize; 
    } 
} 

Ссылка на полный код: https://gist.github.com/amayatsky/e6125a2288cc2e4f1bbf

Смежные вопросы