如何设置字体大小以填充UILabel高度?

way*_*neh 55 iphone xcode objective-c ios

我看过很多改变UILabel大小的例子.

这是我想要做的:更改字体大小,以便文本在新高度内尽可能大.

有线索吗?

bac*_*h-f 41

我有同样的问题,由于这个线程和Joel的算法,我可以解决它.:-)

下面是我在Swift中的代码.我在iOS 8 + Autolayout.

问题:

  1. 用户输入费用:

123 app

  1. 当用户点击"检查"按钮时,会从底部显示一个菜单,将所有内容推送到屏幕顶部(缩小内容,包括标签):

123 app

修复后:

123 app

这正是设计师的想法... :)

xScope应用:)

我将UILabel子类化并覆盖layoutSubviews.然后每次UILabel更改其大小时,都会重新计算字体大小:

//
//  LabelWithAdaptiveTextHeight.swift
//  123
//
//  Created by https://github.com/backslash-f on 12/19/14.
//

/*
 Designed with single-line UILabels in mind, this subclass 'resizes' the label's text (it changes the label's font size)
 everytime its size (frame) is changed. This 'fits' the text to the new height, avoiding undesired text cropping.
 Kudos to this Stack Overflow thread: bit.ly/setFontSizeToFillUILabelHeight
*/

import Foundation
import UIKit

class LabelWithAdaptiveTextHeight: UILabel {

    override func layoutSubviews() {
        super.layoutSubviews()
        font = fontToFitHeight()
    }

    // Returns an UIFont that fits the new label's height.
    private func fontToFitHeight() -> UIFont {

        var minFontSize: CGFloat = DISPLAY_FONT_MINIMUM // CGFloat 18
        var maxFontSize: CGFloat = DISPLAY_FONT_BIG     // CGFloat 67
        var fontSizeAverage: CGFloat = 0
        var textAndLabelHeightDiff: CGFloat = 0

        while (minFontSize <= maxFontSize) {

            fontSizeAverage = minFontSize + (maxFontSize - minFontSize) / 2

            // Abort if text happens to be nil
            guard text?.characters.count > 0 else {
              break
            }

            if let labelText: NSString = text {
                let labelHeight = frame.size.height

                let testStringHeight = labelText.sizeWithAttributes(
                    [NSFontAttributeName: font.fontWithSize(fontSizeAverage)]
                ).height

                textAndLabelHeightDiff = labelHeight - testStringHeight

                if (fontSizeAverage == minFontSize || fontSizeAverage == maxFontSize) {
                    if (textAndLabelHeightDiff < 0) {
                        return font.fontWithSize(fontSizeAverage - 1)
                    }
                    return font.fontWithSize(fontSizeAverage)
                }

                if (textAndLabelHeightDiff < 0) {
                    maxFontSize = fontSizeAverage - 1

                } else if (textAndLabelHeightDiff > 0) {
                    minFontSize = fontSizeAverage + 1

                } else {
                    return font.fontWithSize(fontSizeAverage)
                }
            }
        }
        return font.fontWithSize(fontSizeAverage)
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这很好,但是如果你的标签的文字恰好是零,你就会陷入while循环.我试图编辑帖子,但它被拒绝,所以我会留给作者. (2认同)
  • 这让我很快乐。我不明白为什么苹果首先不这样做。 (2认同)

Kas*_*hif 32

有一个更简单的解决方案.只需添加以下线条并且神奇地,标签也会调整其字体大小以适应标签的高度:

SWIFT 3:

label.minimumScaleFactor = 0.1    //or whatever suits your need
label.adjustsFontSizeToFitWidth = true    
label.lineBreakMode = .byClipping
label.numberOfLines = 0
Run Code Online (Sandbox Code Playgroud)

  • 这是唯一正确的答案. (7认同)

Joe*_*her 31

这就是我如何做到的,因为DGund的答案对我来说不起作用,它适合宽度,但我希望它适合高度.

+ (UIFont *)findAdaptiveFontWithName:(NSString *)fontName forUILabelSize:(CGSize)labelSize withMinimumSize:(NSInteger)minSize
{
    UIFont *tempFont = nil;
    NSString *testString = @"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

    NSInteger tempMin = minSize;
    NSInteger tempMax = 256;
    NSInteger mid = 0;
    NSInteger difference = 0;

    while (tempMin <= tempMax) {
        mid = tempMin + (tempMax - tempMin) / 2;
        tempFont = [UIFont fontWithName:fontName size:mid];
        difference = labelSize.height - [testString sizeWithFont:tempFont].height;

        if (mid == tempMin || mid == tempMax) {
            if (difference < 0) {
                return [UIFont fontWithName:fontName size:(mid - 1)];
            }

            return [UIFont fontWithName:fontName size:mid];
        }

        if (difference < 0) {
            tempMax = mid - 1;
        } else if (difference > 0) {
            tempMin = mid + 1;
        } else {
            return [UIFont fontWithName:fontName size:mid];
        }
    }

    return [UIFont fontWithName:fontName size:mid];
}
Run Code Online (Sandbox Code Playgroud)

这将采用字体名称,大小(理论上它不一定是UILabel,但我总是用UILabel)和最小尺寸(你也可以使用最大尺寸,只需用256替换最大尺寸参数).这将主要测试最小和最大字体大小之间的每个字体大小,并返回目标高度处或之下的字体大小.

用法是自解释的,但看起来像这样:

self.myLabel.font = [self findAdaptiveFontWithName:@"HelveticaNeue-UltraLight" forUILabelSize:self.myLabel.frame.size withMinimumSize:30];
Run Code Online (Sandbox Code Playgroud)

您也可以在UIFont上创建一个类方法类别(这就是我所做的).

编辑:根据建议,我删除了for循环并花了一点时间使用二进制搜索例程提高效率.我做了几次检查,以确保字体最终适合标签.在初始测试中它似乎工作.

  • 您可能希望使用二进制搜索例程来提高此算法的速度. (4认同)

DGu*_*und 21

编辑:查看Joel Fischer的最佳答案,以编程方式获得正确的大小!

您可以将字体设置为自动填充标签的大小,也可以选择不低于最小字体大小.如果您需要更多信息adjustsFontSizeToFitWidth,YES.请设置为查看UILabel类参考.

虽然布尔值被称为"adjustsFontSizeToFitWidth",但它实际上意味着标签高度的最大尺寸,它将保留在标签的一行(或者指定的行数).

  • 它确实适合我的宽度,但绝对不是高度. (76认同)
  • 我必须设置numberOfLines = 0以使其正常工作. (30认同)
  • 如果没有任何反应,你的字体大小不够大(或者,它已经在边界内得到的大小). (3认同)
  • 设置一个非常大的字体,此属性将比代码容易得多。 (2认同)

Fjo*_*ohn 13

根据我的标签高度调整文本我已经适应了Joel方法

func optimisedfindAdaptiveFontWithName(fontName:String, label:UILabel!, minSize:CGFloat,maxSize:CGFloat) -> UIFont!
{

    var tempFont:UIFont
    var tempHeight:CGFloat
    var tempMax:CGFloat = maxSize
    var tempMin:CGFloat = minSize

    while (ceil(tempMin) != ceil(tempMax)){
        let testedSize = (tempMax + tempMin) / 2


        tempFont = UIFont(name:fontName, size:testedSize)
        let attributedString = NSAttributedString(string: label.text!, attributes: [NSFontAttributeName : tempFont])

        let textFrame = attributedString.boundingRectWithSize(CGSize(width: label.bounds.size.width, height: CGFloat.max), options: NSStringDrawingOptions.UsesLineFragmentOrigin , context: nil)

        let difference = label.frame.height - textFrame.height
        println("\(tempMin)-\(tempMax) - tested : \(testedSize) --> difference : \(difference)")
        if(difference > 0){
            tempMin = testedSize
        }else{
            tempMax = testedSize
        }
    }


    //returning the size -1 (to have enought space right and left)
    return UIFont(name: fontName, size: tempMin - 1)
}
Run Code Online (Sandbox Code Playgroud)

我这样使用它:

myLabel.font = optimisedfindAdaptiveFontWithName("Helvetica", label: myLabel, minSize: 10, maxSize: 38)
    println("\(myLabel.font)")
Run Code Online (Sandbox Code Playgroud)


Fat*_*tie 8

好消息,

执行二进制搜索是完全没必要的!

您只需要使用比率搜索进行迭代(几次).

        guess = guess * ( desiredHeight / guessHeight )
Run Code Online (Sandbox Code Playgroud)

这是一个完整的整体IBDesignable解决方案.

注意:与设计师或印刷师合作时,您需要设置字体的跟踪/拉伸.(这是荒谬的苹果不包括这个.)StyledLabel 还包括跟踪/拉伸.

StyledLabel.swift

它设置跟踪,拉伸和设置点大小以匹配所有设备上的视图帧高度.

在故事板中:只需制作UILabel的框架,即文本所需的高度 - 故事的结尾!

// the call fontToFitHeight FINDS THE POINT SIZE TO "FILL TO HEIGHT".
// Just use autolayout to make the frame THE ACTUAL HEIGHT
// you want the type ON ANY DEVICE

// ADDITIONALLY you can set:
// the tracking (that's the overall amount of space between all letters)
// and streching (actually squeeze or stretch the letters horizontally)

// Note: tracking and stretching IS SHOWN IN STORYBOARD LIVE
// WTT crazyrems http://stackoverflow.com/a/37300130/294884

import UIKit

@IBDesignable
class StyledLabel: UILabel
    {
    @IBInspectable var tracking:CGFloat = 0.8
    // values between about 0.7 to 1.3.  one means normal.

    @IBInspectable var stretching:CGFloat = -0.1
    // values between about -.5 to .5.  zero means normal.

    override func awakeFromNib()
        {
        tweak()
        }

    override func prepareForInterfaceBuilder()
        {
        tweak()
        }

    override func layoutSubviews()
        {
        super.layoutSubviews()
        font = fontToFitHeight()
        }

    private func fontToFitHeight() -> UIFont
        {
/* Apple have failed to include a basic thing needed in handling text: fitting the text to the height. Here's the simplest and fastest way to do that:

        guess = guess * ( desiredHeight / guessHeight )

That's really all there is to it. The rest of the code in this routine is safeguards. Further, the routine iterates a couple of times, which is harmless, to take care of any theoretical bizarre nonlinear sizing issues with strange typefaces. */

        guard text?.characters.count > 0 else { return font }
        let desiredHeight:CGFloat = frame.size.height
        guard desiredHeight>1 else { return font }
        var guess:CGFloat
        var guessHeight:CGFloat

        print("searching for... ", desiredHeight)

        guess = font.pointSize
        if (guess>1&&guess<1000) { guess = 50 }

        guessHeight = sizeIf(guess)

        if (guessHeight==desiredHeight)
            {
            print("fluke, exact match within float math limits, up front")
            return font.fontWithSize(guess)
            }

        var iterations:Int = 4

/* It is incredibly unlikely you would need more than four iterations, "two" would rarely be needed. You could imagine some very strange glyph handling where the relationship is non-linear (or something weird): That is the only theoretical reason you'd ever need more than one or two iterations. Note that when you watch the output of the iterations, you'll sometimes/often see same or identical values for the result: this is correct and expected in a float iteration. */

        while(iterations>0)
            {
            guess = guess * ( desiredHeight / guessHeight )
            guessHeight = sizeIf(guess)

            if (guessHeight==desiredHeight)
                {
                print("unbelievable fluke, exact match within float math limits while iterating")
                return font.fontWithSize(guess)
                }

            iterations -= 1
            }

        print("done. Shame Apple doesn't do this for us!")
        return font.fontWithSize(guess)
        }

    private func sizeIf(pointSizeToTry:CGFloat)->(CGFloat)
        {
        let s:CGFloat = text!.sizeWithAttributes(
            [NSFontAttributeName: font.fontWithSize(pointSizeToTry)] )
            .height

        print("guessing .. ", pointSizeToTry, " .. " , s)
        return s
        }

    private func tweak()
        {
        let ats = NSMutableAttributedString(string: self.text!)
        let rg = NSRange(location: 0, length: self.text!.characters.count)

        ats.addAttribute(
            NSKernAttributeName, value:CGFloat(tracking), range:rg )

        ats.addAttribute(
            NSExpansionAttributeName, value:CGFloat(stretching), range:rg )

        self.attributedText = ats
        }
    }
Run Code Online (Sandbox Code Playgroud)


Ant*_*ine 6

基于@ Conaaando的出色答案,我已将其更新为包含IBDesignable参数的版本,这使得可以在整个"界面"构建器中对其进行编辑:

在此输入图像描述

和代码:

//
//  TIFFitToHeightLabel.swift
//

import Foundation
import UIKit

@IBDesignable class TIFFitToHeightLabel: UILabel {

    @IBInspectable var minFontSize:CGFloat = 12 {
        didSet {
            font = fontToFitHeight()
        }
    }

    @IBInspectable var maxFontSize:CGFloat = 30 {
        didSet {
            font = fontToFitHeight()
        }
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        font = fontToFitHeight()
    }

    // Returns an UIFont that fits the new label's height.
    private func fontToFitHeight() -> UIFont {

        var minFontSize: CGFloat = self.minFontSize
        var maxFontSize: CGFloat = self.maxFontSize
        var fontSizeAverage: CGFloat = 0
        var textAndLabelHeightDiff: CGFloat = 0

        while (minFontSize <= maxFontSize) {
            fontSizeAverage = minFontSize + (maxFontSize - minFontSize) / 2

            if let labelText: NSString = text {
                let labelHeight = frame.size.height

                let testStringHeight = labelText.sizeWithAttributes(
                    [NSFontAttributeName: font.fontWithSize(fontSizeAverage)]
                    ).height

                textAndLabelHeightDiff = labelHeight - testStringHeight

                if (fontSizeAverage == minFontSize || fontSizeAverage == maxFontSize) {
                    if (textAndLabelHeightDiff < 0) {
                        return font.fontWithSize(fontSizeAverage - 1)
                    }
                    return font.fontWithSize(fontSizeAverage)
                }

                if (textAndLabelHeightDiff < 0) {
                    maxFontSize = fontSizeAverage - 1

                } else if (textAndLabelHeightDiff > 0) {
                    minFontSize = fontSizeAverage + 1

                } else {
                    return font.fontWithSize(fontSizeAverage)
                }
            }
        }
        return font.fontWithSize(fontSizeAverage)
    }
}
Run Code Online (Sandbox Code Playgroud)


Edw*_*ski 6

在viewWillAppear中调用的一行可以解决这个问题:

testLabel.font = testLabel.font.fontWithSize(testLabel.frame.height * 2/3)
Run Code Online (Sandbox Code Playgroud)

在故事板中,我将所有标签高度设置为相对于视图的整体高度,这允许字体大小与它们动态缩放.

请注意,字体大小实际上是标签高度的2/3.如果您使用的字体的尾部低于线条(如y,g,q,p或j),则需要使字体大小为标签高度的比例,以使这些尾部不会被切断关闭.2/3适用于Helvetica Neue,但根据您使用的字体尝试其他比率.对于没有尾部,数字或全部大写文本的字体,1:1的比例可能就足够了.