2010-05-16 73 views

回答

48

如果你想確保標籤在寬度和高度方面都符合矩形,你可以在標籤上嘗試不同的字體大小,看看是否適合。

該片段始於300 pt,並嘗試通過減小字體大小來使標籤適合目標矩形。

- (void) sizeLabel: (UILabel *) label toRect: (CGRect) labelRect { 

    // Set the frame of the label to the targeted rectangle 
    label.frame = labelRect; 

    // Try all font sizes from largest to smallest font size 
    int fontSize = 300; 
    int minFontSize = 5; 

    // Fit label width wize 
    CGSize constraintSize = CGSizeMake(label.frame.size.width, MAXFLOAT); 

    do { 
     // Set current font size 
     label.font = [UIFont fontWithName:label.font.fontName size:fontSize]; 

     // Find label size for current font size 
     CGRect textRect = [[label text] boundingRectWithSize:constraintSize 
                options:NSStringDrawingUsesLineFragmentOrigin 
                attributes:@{NSFontAttributeName: label.font} 
                context:nil]; 

     CGSize labelSize = textRect.size; 

     // Done, if created label is within target size 
     if(labelSize.height <= label.frame.size.height) 
      break; 

     // Decrease the font size and try again 
     fontSize -= 2; 

    } while (fontSize > minFontSize); 
} 
+0

尋找幾個小時後的無限數量,這一直是隻爲我工作的解決方案。我只是想知道當視圖上有50-100個標籤時,while循環的性能如何。 – agarcian 2013-01-31 03:23:52

+1

我從來沒有任何理由去優化它。我不用它來重複計算。唯一的地方,我有很多標籤來選擇最長的標籤文字和尺寸。 – 2014-03-04 12:17:47

+0

@NielsCastle你的解決方案是否也採用了文字包裝技術?我在agarcian的答案中寫了一個案例。它是否也滿足這種情況? – 2014-11-19 01:06:56

1

還設置myLabel.numberOfLines = 10或無論您想要的最大數量的行。

+4

或者乾脆0線 – 2011-09-21 15:47:28

+2

^這傢伙知道他在談論 – GangstaGraham 2013-08-26 10:29:20

31

我發現尼爾斯的答案是這個問題的最佳答案。不過,我有一個UIView,可以有100個標籤,我需要適合文本,所以這個過程效率很低,我可以感受到性能上的衝擊。

這是他的代碼修改爲使用二進制搜索,而不是線性搜索。現在它工作非常有效。

- (NSInteger)binarySearchForFontSizeForLabel:(UILabel *)label withMinFontSize:(NSInteger)minFontSize withMaxFontSize:(NSInteger)maxFontSize withSize:(CGSize)size { 
    // If the sizes are incorrect, return 0, or error, or an assertion. 
    if (maxFontSize < minFontSize) { 
     return 0; 
    } 

    // Find the middle 
    NSInteger fontSize = (minFontSize + maxFontSize)/2; 
    // Create the font 
    UIFont *font = [UIFont fontWithName:label.font.fontName size:fontSize]; 
    // Create a constraint size with max height 
    CGSize constraintSize = CGSizeMake(size.width, MAXFLOAT); 
    // Find label size for current font size 
    CGRect rect = [label.text boundingRectWithSize:constraintSize 
              options:NSStringDrawingUsesLineFragmentOrigin 
             attributes:@{NSFontAttributeName : font} 
              context:nil]; 
    CGSize labelSize = rect.size; 

    // EDIT: The next block is modified from the original answer posted in SO to consider the width in the decision. This works much better for certain labels that are too thin and were giving bad results. 
    if (labelSize.height >= (size.height + 10) && labelSize.width >= (size.width + 10) && labelSize.height <= (size.height) && labelSize.width <= (size.width)) { 
     return fontSize; 
    } else if (labelSize.height > size.height || labelSize.width > size.width) { 
     return [self binarySearchForFontSizeForLabel:label withMinFontSize:minFontSize withMaxFontSize:fontSize - 1 withSize:size]; 
    } else { 
     return [self binarySearchForFontSizeForLabel:label withMinFontSize:fontSize + 1 withMaxFontSize:maxFontSize withSize:size]; 
    } 
} 

- (void)sizeBinaryLabel:(UILabel *)label toRect:(CGRect)labelRect { 

    // Set the frame of the label to the targeted rectangle 
    label.frame = labelRect; 

    // Try all font sizes from largest to smallest font 
    int maxFontSize = 300; 
    int minFontSize = 5; 

    NSInteger size = [self binarySearchForFontSizeForLabel:label withMinFontSize:minFontSize withMaxFontSize:maxFontSize withSize:label.frame.size]; 

    label.font = [UIFont fontWithName:label.font.fontName size:size]; 

} 

信用此亦https://gist.github.com/988219

+1

+1 - 我只是想寫一份二進制搜索解決方案,然後向下滾動以找出已經發布的二進制解決方案!這絕對是大多數情況下更好的解決方案。真棒agarcian。 – Pavan 2013-02-14 21:32:53

+2

謝謝帕文。這是SO的美麗。協作將爲我們節省幾小時和幾小時的開發時間:-) – agarcian 2013-02-15 19:27:41

+0

非常優雅的解決方案 – 2013-03-21 13:04:54

0

尼爾斯城代碼工作的發現。

下面是使用不同實現的相同想法。
我的解決方案更精確,但也要佔用更多的CPU資源。

將此函數添加到繼承UILabel的類。

-(void)fitCurrentFrame{ 

    CGSize iHave = self.frame.size; 

    BOOL isContained = NO; 
    do{ 
     CGSize iWant = [self.text sizeWithFont:self.font]; 
     if(iWant.width > iHave.width || iWant.height > iHave.height){ 
      self.font = [UIFont fontWithName:self.font.fontName size:self.font.pointSize - 0.1]; 
      isContained = NO; 
     }else{ 
      isContained = YES; 
     } 

    }while (isContained == NO); 
} 
2

如果有人正在尋找的MonoTouch/Xamarin.iOS實現,像我一樣......在這裏你去:

private int BinarySearchForFontSizeForText(NSString text, int minFontSize, int maxFontSize, SizeF size) 
{ 
    if (maxFontSize < minFontSize) 
     return minFontSize; 

    int fontSize = (minFontSize + maxFontSize)/2; 
    UIFont font = UIFont.BoldSystemFontOfSize(fontSize); 

    var constraintSize = new SizeF(size.Width, float.MaxValue); 
    SizeF labelSize = text.StringSize(font, constraintSize, UILineBreakMode.WordWrap); 

    if (labelSize.Height >= size.Height + 10 && labelSize.Width >= size.Width + 10 && labelSize.Height <= size.Height && labelSize.Width <= size.Width) 
     return fontSize; 
    else if (labelSize.Height > size.Height || labelSize.Width > size.Width) 
     return BinarySearchForFontSizeForText(text, minFontSize, fontSize - 1, size); 
    else 
     return BinarySearchForFontSizeForText(text, fontSize + 1, maxFontSize, size); 
} 

private void SizeLabelToRect(UILabel label, RectangleF labelRect) 
{ 
    label.Frame = labelRect; 

    int maxFontSize = 300; 
    int minFontSize = 5; 
    int size = BinarySearchForFontSizeForText(new NSString(label.Text), minFontSize, maxFontSize, label.Frame.Size); 

    label.Font = UIFont.SystemFontOfSize(size); 
} 

它的agarcian的距離的Objective-C到C#代碼的轉換,稍作修改:由於返回結果始終爲0(see the comment of borked)我正在返回計算的minFontSize,這會導致正確的字體大小。

+0

非常感謝您的「小修改」。 – 2014-04-30 12:52:09

0

我已根據@ agarcian的回答爲UILabel創建了類別。但我計算fontSize取決於屏幕上繪製文本所需的平方。這種方法不需要循環,計算是通過一次迭代完成的。

這裏.h文件:

// UILabel+Extended.h 
// Created by Firuz on 16/08/14. 
// Copyright (c) 2014. All rights reserved. 

#import <UIKit/UIKit.h> 

@interface UILabel (Extended) 

/** This method calculate the optimal font size for current number of lines in UILable. Mus be called after drawing UILabel view */ 
- (NSInteger)fontSizeWithMinFontSize:(NSInteger)minFontSize withMaxFontSize:(NSInteger)maxFontSize; 

@end 

這裏.m文件:

// UILabel+Extended.m 
// Created by Firuz on 16/08/14. 
// Copyright (c) 2014. All rights reserved. 

#import "UILabel+Extended.h" 

@implementation UILabel (Extended) 

- (NSInteger)fontSizeWithMinFontSize:(NSInteger)minFontSize withMaxFontSize:(NSInteger)maxFontSize 
{ 
    if (maxFontSize < minFontSize) { 
     return 0; 
    } 

    UIFont *font = [UIFont fontWithName:self.font.fontName size:maxFontSize]; 

    CGFloat lineHeight = [font lineHeight]; 

    CGSize constraintSize = CGSizeMake(MAXFLOAT, lineHeight); 

    CGRect rect = [self.text boundingRectWithSize:constraintSize 
              options:NSStringDrawingUsesLineFragmentOrigin 
             attributes:@{NSFontAttributeName : font} 
              context:nil]; 

    CGFloat labelSqr = self.frame.size.width * self.frame.size.height; 
    CGFloat stringSqr = rect.size.width/self.frame.size.width * (lineHeight + font.pointSize) * self.frame.size.width; 

    CGFloat multiplyer = labelSqr/stringSqr; 

    if (multiplyer < 1) { 
     if (minFontSize < maxFontSize*multiplyer) { 
      return maxFontSize * multiplyer; 
     } else { 
      return minFontSize; 
     } 
    } 
    return maxFontSize; 
} 

@end 
0

所有二進制搜索都是不錯的,但使用幀檢查沒有那麼邏輯上停止遞歸。更好地檢查字體大小,導致UIFont支持浮點大小,這種字體更適合。 另外使用標籤段落樣式來精確計算尺寸。

如果有人感興趣,你可以看看婁代碼:

static UIFont * ___suitableFontInRangePrivate(const CGSize labelSize, 
              NSParagraphStyle * paragraphStyle, 
              NSString * fontName, 
              NSString * text, 
              const CGFloat minSize, 
              const CGFloat maxSize) 
{ 
    // Font size in range, middle size between max & min. 
    const CGFloat currentSize = minSize + ((maxSize - minSize)/2); 

    // Font with middle size. 
    UIFont * currentFont = [UIFont fontWithName:fontName size:currentSize]; 

    // Calculate text height. 
    const CGFloat textHeight = [text boundingRectWithSize:CGSizeMake(labelSize.width, CGFLOAT_MAX) 
               options:NSStringDrawingUsesLineFragmentOrigin 
              attributes:@{ NSFontAttributeName : currentFont, NSParagraphStyleAttributeName : paragraphStyle } 
               context:nil].size.height; 
    CGFloat min, max; 
    if (textHeight > labelSize.height) 
    { 
     // Take left range part. 
     min = minSize; 
     max = currentSize; 
    } 
    else 
    { 
     // Take right range part. 
     min = currentSize; 
     max = maxSize; 
    } 

    // If font size in int range [0.0; 2.0] - got it, othervice continue search. 
    return ((max - min) <= 2.0) ? currentFont : ___suitableFontInRangePrivate(labelSize, paragraphStyle, fontName, text, min, max); 
} 

void UILabelAdjustsFontSizeToFrame(UILabel * label) 
{ 
    if (!label) return; 

    NSString * text = [label text]; 

    __block NSParagraphStyle * style = nil; 
    [[label attributedText] enumerateAttributesInRange:NSMakeRange(0, [text length]) 
              options:(NSAttributedStringEnumerationOptions)0 
              usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop){ 
               id paragraphStyle = [attrs objectForKey:@"NSParagraphStyle"]; 
               if (paragraphStyle) style = [paragraphStyle retain]; 
              }]; 

    if (!style) 
    { 
     NSMutableParagraphStyle * paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; 
     if (!paragraphStyle) paragraphStyle = [[NSMutableParagraphStyle alloc] init]; 
     if (paragraphStyle) 
     { 
      [paragraphStyle setLineBreakMode:[label lineBreakMode]]; 
      [paragraphStyle setAlignment:[label textAlignment]]; 
     } 
     style = paragraphStyle; 
    } 

    UIFont * suitableFont = ___suitableFontInRangePrivate([label frame].size, style, [[label font] fontName], text, 0, 500); 
    [label setFont:suitableFont]; 
    [style release]; 
} 
8

下面是根據@NielsCastle答案斯威夫特版,採用二進制搜索

extension UILabel{ 

    func adjustFontSizeToFitRect(rect : CGRect){ 

     if text == nil{ 
      return 
     } 

     frame = rect 

     let maxFontSize: CGFloat = 100.0 
     let minFontSize: CGFloat = 5.0 

     var q = Int(maxFontSize) 
     var p = Int(minFontSize) 

     let constraintSize = CGSize(width: rect.width, height: CGFloat.max) 

     while(p <= q){ 
      let currentSize = (p + q)/2 
      font = font.fontWithSize(CGFloat(currentSize)) 
      let text = NSAttributedString(string: self.text!, attributes: [NSFontAttributeName:font]) 
      let textRect = text.boundingRectWithSize(constraintSize, options: .UsesLineFragmentOrigin, context: nil) 

      let labelSize = textRect.size 

      if labelSize.height < frame.height && labelSize.height >= frame.height-10 && labelSize.width < frame.width && labelSize.width >= frame.width-10 { 
       break 
      }else if labelSize.height > frame.height || labelSize.width > frame.width{ 
       q = currentSize - 1 
      }else{ 
       p = currentSize + 1 
      } 
     } 

    } 
} 

使用

label.adjustFontSizeToFitRect(rect) 
經常只是

label.adjustFontSizeToFitRect(rect.frame) 
+0

不錯,謝謝:) - 我只會建議使用[guard statement](https://www.hackingwithswift.com/new-syntax-swift-2-guard)取代這個早期回報。 – Mikolaj 2016-05-30 17:19:57

+0

@nRewik沒有工作... – 2016-08-01 01:14:34

+0

@nRewik工作很好。只有部分,這是越野車是當你使'UILabel'相當高的高度,然後字體大小計算亂七八糟,它超出了寬度。將嘗試解決這個錯誤,並在這裏發佈。 – 2016-09-13 07:24:54

5

該解決方案(基於this answer)的作品,支持自動佈局和執行二進制搜索找到最好的字體大小。

我發現唯一的警告是你不能指定行數(因爲AFAIK你不能告訴boundingRectWithSize你想要多少行)。

AdjustableLabel.h

#import <UIKit/UIKit.h> 

@interface AdjustableLabel : UILabel 
/** 
    If set to YES, font size will be automatically adjusted to frame. 
    Note: numberOfLines can't be specified so it will be set to 0. 
*/ 
@property(nonatomic) BOOL adjustsFontSizeToFitFrame; 
@end 

AdjustableLabel.m

#import "AdjustableLabel.h" 

@interface AdjustableLabel() 
@property(nonatomic) BOOL fontSizeAdjusted; 
@end 

// The size found S satisfies: S fits in the frame and and S+DELTA doesn't. 
#define DELTA 0.5 

@implementation AdjustableLabel 

- (void)setAdjustsFontSizeToFitFrame:(BOOL)adjustsFontSizeToFitFrame 
{ 
    _adjustsFontSizeToFitFrame = adjustsFontSizeToFitFrame; 

    if (adjustsFontSizeToFitFrame) { 
     self.numberOfLines = 0; // because boundingRectWithSize works like this was 0 anyway 
    } 
} 

- (void)layoutSubviews 
{ 
    [super layoutSubviews]; 

    if (self.adjustsFontSizeToFitFrame && !self.fontSizeAdjusted) 
    { 
     self.fontSizeAdjusted = YES; // to avoid recursion, because adjustFontSizeToFrame will trigger this method again 

     [self adjustFontSizeToFrame]; 
    } 
} 

- (void) adjustFontSizeToFrame 
{ 
    UILabel* label = self; 

    if (label.text.length == 0) return; 

    // Necessary or single-char texts won't be correctly adjusted 
    BOOL checkWidth = label.text.length == 1; 

    CGSize labelSize = label.frame.size; 

    // Fit label width-wise 
    CGSize constraintSize = CGSizeMake(checkWidth ? MAXFLOAT : labelSize.width, MAXFLOAT); 

    // Try all font sizes from largest to smallest font size 
    CGFloat maxFontSize = 300; 
    CGFloat minFontSize = 5; 

    NSString* text = label.text; 
    UIFont* font = label.font; 

    while (true) 
    { 
     // Binary search between min and max 
     CGFloat fontSize = (maxFontSize + minFontSize)/2; 

     // Exit if approached minFontSize enough 
     if (fontSize - minFontSize < DELTA/2) { 
      font = [UIFont fontWithName:font.fontName size:minFontSize]; 
      break; // Exit because we reached the biggest font size that fits 
     } else { 
      font = [UIFont fontWithName:font.fontName size:fontSize]; 
     } 

     // Find label size for current font size 
     CGRect rect = [text boundingRectWithSize:constraintSize 
             options:NSStringDrawingUsesLineFragmentOrigin 
             attributes:@{NSFontAttributeName : font} 
             context:nil]; 

     // Now we discard a half 
     if(rect.size.height <= labelSize.height && (!checkWidth || rect.size.width <= labelSize.width)) { 
      minFontSize = fontSize; // the best size is in the bigger half 
     } else { 
      maxFontSize = fontSize; // the best size is in the smaller half 
     } 
    } 

    label.font = font; 
} 

@end 

使用

AdjustableLabel* label = [[AdjustableLabel alloc] init]; 
label.adjustsFontSizeToFitFrame = YES; 

// In case you change the font, the size you set doesn't matter 
label.font = [UIFont fontWithName:@"OpenSans-Light" size:20]; 
+0

這。作品。完美。 – Fattie 2016-05-17 03:02:53

+0

這非常優雅,而且效果很好。我有一個小小的改進,它允許標籤文本是單個單詞的情況。在這種情況下,你不希望它跨行分割,所以我補充說: '//如果只有一個單詞,那麼請降低高度,以便強制boundingRectWithSize適合它'//一個line.' ''//' 如果([label.text componentsSeparatedByString:@」「] .Count之間== 1){'' labelSize.height/= 2.0;'' }'剛過 聲明labelSize。 – PKCLsoft 2017-11-02 11:33:43

2

這裏有一個斯威夫特內線UILabel的外掛。它運行一個二進制搜索算法來調整標籤的字體和界限,並進行測試,以針對iOS 9.

USAGE工作:字體大小調整以適合尺寸的100×100(1.0字體點內準確)。

<label>.fitFontForSize(CGSizeMake(100.0, 100.0)) 

複製/粘貼以下到您的文件

extension UILabel { 

    func fitFontForSize(constrainedSize : CGSize, var maxFontSize : CGFloat = 300.0, var minFontSize : CGFloat = 5.0, accuracy : CGFloat = 1.0) { 
     assert(maxFontSize > minFontSize) 
     while maxFontSize - minFontSize > accuracy { 
      let midFontSize : CGFloat = ((minFontSize + maxFontSize)/2) 
      font = font.fontWithSize(midFontSize) 
      sizeToFit() 
      let checkSize : CGSize = bounds.size 
      if checkSize.height < constrainedSize.height && checkSize.width < constrainedSize.width { 
       minFontSize = midFontSize 
      } else { 
       maxFontSize = midFontSize 
      } 
     } 
     font = font.fontWithSize(minFontSize) 
     sizeToFit() 
    } 

} 

注: 標籤的寬度和高度的限制不應會爲了這個工作組。一個簡單的解決方法是將標籤嵌入到具有設置大小限制的UIView中,然後調用上面定義的函數(如下所示)。

<label>.fitFontForSize(<superview>.bounds.size) 
+1

不錯的擴展;)請記住函數聲明中的'var'參數實際上已被棄用,並將在Swift 3中刪除。 – rafalkitta 2016-05-24 14:50:41

1

所有這些,都是對原來的問題有趣的解決方案,但是所有的人都還缺少一個重要的事情:如果你僅僅依靠familyName得到下一個字體進行測試,你失去的權重信息和可能更高級的屬性,像小帽子,人物風格等

更好的辦法是,而不是通過周圍的字體名稱和做[UIFont fontWithName:someFontName size:someFontSize],傳遞UIFontDescriptor對象一起,然後做 [UIFont fontWithDescriptor:someFontDescriptor size:someFontSize]

0

因爲我沒有找到工作解決方案使用上述答案回答我所有的需求,我創建了我自己的組件提供以下功能:使用時FittableFontLabel

  • 調整字體以適應高度和寬度使用單線標籤時標籤的多線
  • 調整字體,以適應寬度,高度標籤將調整本身
  • NSAttributedStrings支持以及基本的字符串
  • 自動改變一拉時調整大小BEL文/幀
  • ...

如果你們中的任何有趣的是,它的使用 的CocoaPods全迅速可用庫:https://github.com/tbaranes/FittableFontLabel

0

斯威夫特3「二進制搜索解決方案」的基礎上this與答案小的改進。樣本是在UITextView子類中的:

func binarySearchOptimalFontSize(min: Int, max: Int) -> Int { 
    let middleSize = (min + max)/2 

    if min > max { 
     return middleSize 
    } 

    let middleFont = UIFont(name: font!.fontName, size: CGFloat(middleSize))! 

    let attributes = [NSFontAttributeName : middleFont] 
    let attributedString = NSAttributedString(string: text, attributes: attributes) 

    let size = CGSize(width: bounds.width, height: .greatestFiniteMagnitude) 
    let options: NSStringDrawingOptions = [.usesLineFragmentOrigin, .usesFontLeading] 
    let textSize = attributedString.boundingRect(with: size, options: options, context: nil) 

    if textSize.size.equalTo(bounds.size) { 
     return middleSize 
    } else if (textSize.height > bounds.size.height || textSize.width > bounds.size.width) { 
     return binarySearchOptimalFontSize(min: min, max: middleSize - 1) 
    } else { 
     return binarySearchOptimalFontSize(min: middleSize + 1, max: max) 
    } 
} 

我希望能幫助別人。

+0

這不起作用。將'greatestFiniteMagnitude'交換爲寬度,如'CGSize(width:bounds.width,height:.greatestFiniteMagnitude)' – zrubenst 2017-07-01 21:15:07

0

@ agarcian的回答很接近,但它並沒有完全爲我工作,因爲有人在評論別人提到的,它總是返回0。

這裏是我的嘗試。

乾杯!

/** 
* Returns the font size required in order to fit the specified text in the specified area. 
* NB! When drawing, be sure to pass in the same options that we pass to boundingRectWithSize:options:attributes:context: 
* Heavily modified form of: http://stackoverflow.com/a/14662750/1027452 
*/ 
+(NSInteger)fontSizeForText:(NSString *)text withFont:(UIFont *)font inArea:(CGSize)areaSize minFontSize:(NSInteger)minFontSize maxFontSize:(NSInteger)maxFontSize 
{ 
// If the sizes are incorrect, return 0, or error, or an assertion. 
    if (maxFontSize < minFontSize) { 
     return 0; 
    } 

    // Find the middle 
    NSInteger fontSize = (minFontSize + maxFontSize)/2; 
    // Create the font 
    UIFont *f = [UIFont fontWithName:font.fontName size:fontSize]; 
    // Create a constraint size with max height 
    CGSize constraintSize = CGSizeMake(areaSize.width, MAXFLOAT); 
    // Find label size for current font size 
    CGRect rect = [text boundingRectWithSize:constraintSize 
              options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading) 
             attributes:@{NSFontAttributeName : f} 
              context:nil]; 
    CGSize labelSize = rect.size; 

    if (labelSize.height <= areaSize.height && labelSize.width <= areaSize.width) 
    { 
     return fontSize; 
    } 
    else if (labelSize.height > areaSize.height || labelSize.width > areaSize.width) 
    { 
     return [self fontSizeForText:text withFont:f inArea:areaSize minFontSize:minFontSize maxFontSize:maxFontSize -1];; 
    } 
    else 
    { 
     return [self fontSizeForText:text withFont:f inArea:areaSize minFontSize:minFontSize+1 maxFontSize:maxFontSize];; 
    } 
} 
相關問題