ios-多行UILabel上的自动收缩

是否可以在UILabel的多行上结合使用autoshrink属性? 例如,在2条可用行上可能会出现较大的文本。

jfisk asked 2019-11-07T20:22:18Z
16个解决方案
46 votes

我对上面的代码做了一些修改,使其成为UILabel上的一个类别:

头文件:

#import <UIKit/UIKit.h>
@interface UILabel (MultiLineAutoSize)
    - (void)adjustFontSizeToFit;
@end

和执行文件:

@implementation UILabel (MultiLineAutoSize)

- (void)adjustFontSizeToFit
{
    UIFont *font = self.font;
    CGSize size = self.frame.size;

    for (CGFloat maxSize = self.font.pointSize; maxSize >= self.minimumFontSize; maxSize -= 1.f)
    {
        font = [font fontWithSize:maxSize];
        CGSize constraintSize = CGSizeMake(size.width, MAXFLOAT);
        CGSize labelSize = [self.text sizeWithFont:font constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];
        if(labelSize.height <= size.height)
        {
            self.font = font;
            [self setNeedsLayout];
            break;
        }
    }
    // set the font to the minimum size anyway
    self.font = font;
    [self setNeedsLayout];
}

@end
DaGaMs answered 2019-11-07T20:23:37Z
39 votes

这些人找到了解决方案:

[http://www.11pixel.com/blog/28/resize-multi-line-text-to-fit-uilabel-on-iphone/]

他们的解决方案如下:

int maxDesiredFontSize = 28;
int minFontSize = 10;
CGFloat labelWidth = 260.0f;
CGFloat labelRequiredHeight = 180.0f;
//Create a string with the text we want to display.
self.ourText = @"This is your variable-length string. Assign it any way you want!";

/* This is where we define the ideal font that the Label wants to use.
   Use the font you want to use and the largest font size you want to use. */
UIFont *font = [UIFont fontWithName:@"Marker Felt" size:maxDesiredFontSize];

int i;
/* Time to calculate the needed font size.
   This for loop starts at the largest font size, and decreases by two point sizes (i=i-2)
   Until it either hits a size that will fit or hits the minimum size we want to allow (i > 10) */
for(i = maxDesiredFontSize; i > minFontSize; i=i-2)
{
    // Set the new font size.
    font = [font fontWithSize:i];
    // You can log the size you're trying: NSLog(@"Trying size: %u", i);

    /* This step is important: We make a constraint box 
       using only the fixed WIDTH of the UILabel. The height will
       be checked later. */ 
    CGSize constraintSize = CGSizeMake(labelWidth, MAXFLOAT);

    // This step checks how tall the label would be with the desired font.
    CGSize labelSize = [self.ourText sizeWithFont:font constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];

    /* Here is where you use the height requirement!
       Set the value in the if statement to the height of your UILabel
       If the label fits into your required height, it will break the loop
       and use that font size. */
    if(labelSize.height <= labelRequiredHeight)
        break;
}
// You can see what size the function is using by outputting: NSLog(@"Best size is: %u", i);

// Set the UILabel's font to the newly adjusted font.
msg.font = font;

// Put the text into the UILabel outlet variable.
msg.text = self.ourText;

为了使此工作正常,必须在接口构建器中将IBOutlet分配给UILabel。

“ IBOutlet UILabel * msg;”

所有优点都是11像素像素的人的优点。

The dude answered 2019-11-07T20:23:01Z
39 votes

我找到了此链接[http://beckyhansmeyer.com/2015/04/09/autoshrinking-text-in-a-multiline-uilabel/]

可以通过3个简单的步骤使用Interface Builder解决此问题:

  1. 将“自动收缩”设置为“最小字体大小”。
  2. 将字体设置为您的所需的最大字体大小(20)并将“行数”设置为10,在我的情况下为以该字体大小适合标签的行数。
  3. 然后,更改“行从“自动换行”到“截尾”。

希望能帮助到你!

MontiRabbit answered 2019-11-07T20:24:28Z
24 votes

这是基于itecedor的iOS 6更新而更新到iOS 7的类别解决方案。

头文件:

#import <UIKit/UIKit.h>
@interface UILabel (MultiLineAutoSize)
    - (void)adjustFontSizeToFit;
@end

和执行文件:

@implementation UILabel (MultiLineAutoSize)


- (void)adjustFontSizeToFit {
    UIFont *font = self.font;
    CGSize size = self.frame.size;

    for (CGFloat maxSize = self.font.pointSize; maxSize >= self.minimumScaleFactor * self.font.pointSize; maxSize -= 1.f)
    {
        font = [font fontWithSize:maxSize];
        CGSize constraintSize = CGSizeMake(size.width, MAXFLOAT);

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

        CGSize labelSize = textRect.size;


        if(labelSize.height <= size.height)
        {
            self.font = font;
            [self setNeedsLayout];
            break;
        }
    }
    // set the font to the minimum size anyway
    self.font = font;
    [self setNeedsLayout]; }


@end
stevenpaulr answered 2019-11-07T20:24:55Z
12 votes

标为解决方案的答案是不可靠的。 如果正确设置以下属性,UILabel将自动处理它:

numberOfLines必须为非零

lineBreakMode必须为NSLineBreakByCharWrapping

lineBreakMode不得为NSLineBreakByCharWrappingNSLineBreakByWordWrapping

GoldenJoe answered 2019-11-07T20:25:39Z
8 votes

来自@DaGaMs的快速版本。

SWIFT 2:

extension UILabel {
    func adjustFontSizeToFit(minimumFontSize: CGFloat, maximumFontSize: CGFloat? = nil) {
        let maxFontSize = maximumFontSize ?? font.pointSize
        for size in stride(from: maxFontSize, to: minimumFontSize, by: -CGFloat(0.1)) {
            let proposedFont = font.fontWithSize(size)
            let constraintSize = CGSizeMake(bounds.size.width, CGFloat(MAXFLOAT))
            let labelSize = ((text ?? "") as NSString).boundingRectWithSize(constraintSize,
                options: .UsesLineFragmentOrigin,
                attributes: [NSFontAttributeName: proposedFont],
                context: nil)
            if labelSize.height <= bounds.size.height {
                font = proposedFont
                setNeedsLayout()
                break;
            }
        }
    }
}

SWIFT 3:

extension UILabel {
    func adjustFontSizeToFit(minimumFontSize: CGFloat, maximumFontSize: CGFloat? = nil) {
        let maxFontSize = maximumFontSize ?? font.pointSize
        for size in stride(from: maxFontSize, to: minimumFontSize, by: -CGFloat(0.1)) {
            let proposedFont = font.withSize(size)
            let constraintSize = CGSize(width: bounds.size.width, height: CGFloat(MAXFLOAT))
            let labelSize = ((text ?? "") as NSString).boundingRect(with: constraintSize,
                                                                            options: .usesLineFragmentOrigin,
                                                                            attributes: [NSFontAttributeName: proposedFont],
                                                                            context: nil)
            if labelSize.height <= bounds.size.height {
                font = proposedFont
                setNeedsLayout()
                break;
            }
        }
    }
}
wfbarksdale answered 2019-11-07T20:26:11Z
7 votes

由于缺乏声誉,我无法评论MontiRabbit的帖子,因此我将给出一个新的答案。 他(和她的推荐人)提出的解决方案在Xcode 7.3或更高版本上不起作用,这是不精确的。为了使其工作,在情节提要中,我必须:

  1. 设置宽度约束(纯宽度或尾部和引线)
  2. 设置高度约束(这非常重要,通常使用自动调整大小功能不会设置标签高度)
  3. 将“自动收缩”属性设置为“最小字体大小”或“最小字体大小”(在两种情况下均有效)
  4. 将“换行符”属性设置为“截尾”
  5. 将“行”属性设置为非零值

希望能帮助到你! ;)

Nem answered 2019-11-07T20:27:14Z
6 votes

我喜欢DaGaMs的答案,但是在使用可以在dequeueReusableCell:中返回的UITableViewCells之类的标签时,即使某些文本较少且可以利用的tableView单元格仍需要原始字体大小,常规字体大小也会继续缩小。 原始标签的原始字体大小。

因此,我以DaGaMs的类别作为起点,我创建了一个单独的类而不是一个单独的类别,并且确保我的情节提要中的UILabel使用了这个新类:

#import "MultiLineAutoShrinkLabel.h"

@interface MultiLineAutoShrinkLabel ()
@property (readonly, nonatomic) UIFont* originalFont;
@end

@implementation MultiLineAutoShrinkLabel

@synthesize originalFont = _originalFont;

- (UIFont*)originalFont { return _originalFont ? _originalFont : (_originalFont = self.font); }

- (void)quoteAutoshrinkUnquote
{
    UIFont* font = self.originalFont;
    CGSize frameSize = self.frame.size;

    CGFloat testFontSize = _originalFont.pointSize;
    for (; testFontSize >= self.minimumFontSize; testFontSize -= 0.5)
    {
        CGSize constraintSize = CGSizeMake(frameSize.width, MAXFLOAT);
        CGSize testFrameSize = [self.text sizeWithFont:(font = [font fontWithSize:testFontSize])
                                     constrainedToSize:constraintSize
                                         lineBreakMode:self.lineBreakMode];
        // the ratio of testFontSize to original font-size sort of accounts for number of lines
        if (testFrameSize.height <= frameSize.height * (testFontSize/_originalFont.pointSize))
            break;
    }

    self.font = font;
    [self setNeedsLayout];
}

@end
john.k.doe answered 2019-11-07T20:27:46Z
6 votes

itedcedor的答案有pwightman指出的一个问题。 同样,也不需要修剪空格。 这是修改后的版本:

- (void)adjustFontSizeToFit {
    UIFont *font = self.font;
    CGSize size = self.frame.size;

    for (CGFloat maxSize = self.font.pointSize; maxSize >= self.minimumScaleFactor * self.font.pointSize; maxSize -= 1.f) {
        font = [font fontWithSize:maxSize];
        CGSize constraintSize = CGSizeMake(size.width, MAXFLOAT);
        CGSize labelSize = [self.text sizeWithFont:font constrainedToSize:constraintSize lineBreakMode:NSLineBreakByWordWrapping];

        if(labelSize.height <= size.height) {
            self.font = font;
            [self setNeedsLayout];
            break;
        }
    }

    // set the font to the minimum size anyway
    self.font = font;
    [self setNeedsLayout];
}
Abras answered 2019-11-07T20:28:11Z
4 votes

感谢DaGaMs提供此解决方案。

我将其更新如下:

1-与iOS 6一起使用(因为不建议使用minimumFontSize和UILineBreakModeWordWrap)2-从标签的文本中去除空格,因为这将导致调整大小失败(您不希望我发现该错误花了多长时间)

-(void)adjustFontSizeToFit 
{
    self.text = [self.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];

    UIFont *font = self.font;
    CGSize size = self.frame.size;

    for (CGFloat maxSize = self.font.pointSize; maxSize >= self.minimumScaleFactor; maxSize -= 1.f)
    {
        font = [font fontWithSize:maxSize];
        CGSize constraintSize = CGSizeMake(size.width, MAXFLOAT);
        CGSize labelSize = [self.text sizeWithFont:font constrainedToSize:constraintSize lineBreakMode:NSLineBreakByWordWrapping];
        if(labelSize.height <= size.height)
        {
            self.font = font;
            [self setNeedsLayout];
            break;
        }
    }
    // set the font to the minimum size anyway
    self.font = font;
    [self setNeedsLayout];
}
itecedor answered 2019-11-07T20:28:49Z
2 votes

对于UIButton,仅以下几行对我有用:

self.centerBtn.titleLabel.numberOfLines = 2;
self.centerBtn.titleLabel.textAlignment = NSTextAlignmentCenter;
self.centerBtn.titleLabel.adjustsFontSizeToFitWidth = YES;
Geman Wu answered 2019-11-07T20:29:13Z
2 votes

我使用了@wbarksdale的Swift 3解决方案,但发现中间的长单词被截断了。 为了保持单词完整,我必须进行如下修改:

extension UILabel {
    func adjustFontSizeToFit(minimumFontSize: CGFloat, maximumFontSize: CGFloat? = nil) {
        let maxFontSize = maximumFontSize ?? font.pointSize
        let words = self.text?.components(separatedBy: " ")
        var longestWord: String?
        if let max = words?.max(by: {$1.characters.count > $0.characters.count}) {
            longestWord = max
        }
        for size in stride(from: maxFontSize, to: minimumFontSize, by: -CGFloat(0.1)) {
            let proposedFont = font.withSize(size)
            let constraintSize = CGSize(width: bounds.size.width, height: CGFloat(MAXFLOAT))
            let labelSize = ((text ?? "") as NSString).boundingRect(with: constraintSize,
                                                                    options: .usesLineFragmentOrigin,
                                                                    attributes: [NSFontAttributeName: proposedFont],
                                                                    context: nil)

            let wordConstraintSize = CGSize(width: CGFloat(MAXFLOAT), height: CGFloat(MAXFLOAT))
            let longestWordSize = ((longestWord ?? "") as NSString).boundingRect(with: wordConstraintSize,
                                                                    options: .usesFontLeading,
                                                                    attributes: [NSFontAttributeName: proposedFont],
                                                                    context: nil)

            if labelSize.height <= bounds.size.height && longestWordSize.width < constraintSize.width {
                font = proposedFont
                setNeedsLayout()
                break
            }
        }
    }
}
Daniel McLean answered 2019-11-07T20:29:38Z
1 votes

我已经根据上面的“ The Dude's”答案在UILabel上写了一个小类别,以实现此功能。

[https://gist.github.com/ayushn21/d87b835b2efc756e859f]

ayushn21 answered 2019-11-07T20:30:09Z
0 votes

自iOS 2.0以来,在NSString-sizeWithFont:minFontSize:actualFontSize:forWidth:lineBreakMode:上显然存在一种方法,但是不幸的是,在iOS 7中已弃用该方法,而没有建议的替代方法,因为不鼓励自动减小字体大小。 我在苹果在主题演讲等中使用苹果时并没有真正理解苹果的立场,我认为如果字体大小在很小范围内就可以了。 这是Swift使用此方法的实现。

var newFontSize: CGFloat = 30
    let font = UIFont.systemFontOfSize(newFontSize)
    (self.label.text as NSString).sizeWithFont(font, minFontSize: 20, actualFontSize: &newFontSize, forWidth: self.label.frame.size.width, lineBreakMode: NSLineBreakMode.ByWordWrapping)
    self.label.font = font.fontWithSize(newFontSize)

我不知道不使用不推荐使用的方法就可以实现的方法。

Nick answered 2019-11-07T20:30:42Z
0 votes

试试这个:

在标签上设置text属性后,可以使用子类UILabel或调用AdjustFontSize方法

override var text : String? { didSet { self.adjustFontSize() } }

func adjustFontSize()
{
    var lineCount = self.string.components(separatedBy: "\n").count - 1
    var textArray = self.string.components(separatedBy: " ")
    var wordsToCompare = 1
    while(textArray.count > 0)
    {
        let words = textArray.first(n: wordsToCompare).joined(separator: " ")
        let wordsWidth = words.widthForHeight(0, font: self.font)
        if(wordsWidth > self.frame.width)
        {
            textArray.removeFirst(wordsToCompare)
            lineCount += 1
            wordsToCompare = 1
        }
        else if(wordsToCompare > textArray.count)
        {
            break
        }
        else
        {
            wordsToCompare += 1
        }
    }
    self.numberOfLines = lineCount + 1
}
Rendel answered 2019-11-07T20:31:09Z
0 votes
extension UILabel{

func adjustFont(minSize:Int, maxSize:Int){
    var newFont = self.font
    for index in stride(from: maxSize, to: minSize, by: -1) {
        newFont = UIFont.systemFont(ofSize: CGFloat(index))
        let size = CGSize(width: self.frame.width, height: CGFloat(Int.max))
        let size2 = (self.text! as NSString).boundingRect(with: size, options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: [NSAttributedStringKey.font:newFont!], context: nil).size
        if size2.height < self.frame.size.height{
            break
        }
    }
    self.font = newFont
}

}

您还需要为UILabel的numberOfLines属性分配值。

cnu answered 2019-11-07T20:31:35Z
translate from https://stackoverflow.com:/questions/9059631/autoshrink-on-a-uilabel-with-multiple-lines