如何计算特定字体和字体大小的文本字符串的宽度?

问题描述 投票:0回答:16

我有一个显示一些字符的 UILabel。如“x”、“y”或“rpm”。如何计算标签中文本的宽度(它不使用整个可用空间)?这是用于自动布局,如果 UILabel 中的文本较小,则另一个视图将具有更大的框架矩形。指定 UIFont 和字体大小时,是否有方法计算文本的宽度?也没有换行符,只有一行。

iphone cocoa-touch uikit
16个回答
101
投票

由于

sizeWithFont
已被弃用,我将更新我原来的答案以使用 Swift 4 和
.size

//: Playground - noun: a place where people can play
    
import UIKit
           
if let font = UIFont(name: "Helvetica", size: 24) {
   let fontAttributes = [NSAttributedString.Key.font.font: font]
   let text = "Your Text Here"
   let size = (text as NSString).size(withAttributes: fontAttributes)
}

大小应该是“您的文本在这里”的屏幕大小,以磅为单位。


81
投票

sizeWithFont:
现已弃用,请改用
sizeWithAttributes:

UIFont *font = [UIFont fontWithName:@"Helvetica" size:30];
NSDictionary *userAttributes = @{NSFontAttributeName: font,
                                 NSForegroundColorAttributeName: [UIColor blackColor]};
NSString *text = @"hello";
...
const CGSize textSize = [text sizeWithAttributes: userAttributes];

76
投票

您可以通过

NSString UIKit Additions
中的各种 sizeWithFont: 方法来做到这一点。在您的情况下,最简单的变体就足够了(因为您没有多行标签):

NSString *someString = @"Hello World";
UIFont *yourFont = // [UIFont ...]
CGSize stringBoundingBox = [someString sizeWithFont:yourFont];

此方法有多种变体,例如。有些人考虑换行模式或最大尺寸。


72
投票

2019 年 9 月更新

这个答案 是一种使用新语法的更简洁的方法。

原答案

基于 Glenn Howes 的出色回答,我创建了一个扩展来计算字符串的宽度。如果你正在做一些事情,比如设置

UISegmentedControl
的宽度,这可以根据片段的标题字符串设置宽度。

extension String {

    func widthOfString(usingFont font: UIFont) -> CGFloat {
        let fontAttributes = [NSAttributedString.Key.font: font]
        let size = self.size(withAttributes: fontAttributes)
        return size.width
    }

    func heightOfString(usingFont font: UIFont) -> CGFloat {
        let fontAttributes = [NSAttributedString.Key.font: font]
        let size = self.size(withAttributes: fontAttributes)
        return size.height
    }

    func sizeOfString(usingFont font: UIFont) -> CGSize {
        let fontAttributes = [NSAttributedString.Key.font: font]
        return self.size(withAttributes: fontAttributes)
    }
}

用法:

    // Set width of segmentedControl
    let starString = "⭐️"
    let starWidth = starString.widthOfString(usingFont: UIFont.systemFont(ofSize: 14)) + 16
    segmentedController.setWidth(starWidth, forSegmentAt: 3)

52
投票

Swift 4.2 中的 Oneliner 🔸

let size = "abc".size(withAttributes:[.font: UIFont.systemFont(ofSize: 18.0)])

50
投票

Swift-5

使用 intrinsicContentSize 找到文本的高度和宽度。

yourLabel.intrinsicContentSize.width

即使您的字符串之间有自定义间距,如 "T E X T"

,这也会起作用

10
投票

iOS 16 更新:

UILabel.textRect(forBounds: CGRect, limitedToNumberOfLines: Int)

如果您正在努力通过多行支持获取文本width,那么您可以使用下一个代码(Swift 5):

func width(text: String, height: CGFloat) -> CGFloat {
    let attributes: [NSAttributedString.Key: Any] = [
        .font: UIFont.systemFont(ofSize: 17)
    ]
    let attributedText = NSAttributedString(string: text, attributes: attributes)
    let constraintBox = CGSize(width: .greatestFiniteMagnitude, height: height)
    let textWidth = attributedText.boundingRect(with: constraintBox, options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil).width.rounded(.up)
    
    return textWidth
}

如果需要,您可以使用相同的方式找到文本高度(只需切换 constraintBox 实现):

let constraintBox = CGSize(width: maxWidth, height: .greatestFiniteMagnitude)

或者这里有一个统一函数来获取多行支持的文本大小:

func labelSize(for text: String, maxWidth: CGFloat, maxHeight: CGFloat) -> CGSize {
    let attributes: [NSAttributedString.Key: Any] = [
        .font: UIFont.systemFont(ofSize: 17)
    ]
    
    let attributedText = NSAttributedString(string: text, attributes: attributes)
    
    let constraintBox = CGSize(width: maxWidth, height: maxHeight)
    let rect = attributedText.boundingRect(with: constraintBox, options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil).integral
    
    return rect.size
}

用法:

let textSize = labelSize(for: "SomeText", maxWidth: contentView.bounds.width, maxHeight: .greatestFiniteMagnitude)
let textHeight = textSize.height.rounded(.up)
let textWidth = textSize.width.rounded(.up)

8
投票

Swift 中的这个简单扩展效果很好。

extension String {
    func size(OfFont font: UIFont) -> CGSize {
        return (self as NSString).size(attributes: [NSFontAttributeName: font])
    }
}

用法:

let string = "hello world!"
let font = UIFont.systemFont(ofSize: 12)
let width = string.size(OfFont: font).width // size: {w: 98.912 h: 14.32}

6
投票

对于Swift 5.4

extension String {
    func SizeOf_String( font: UIFont) -> CGSize {
        let fontAttribute = [NSAttributedString.Key.font: font]
        let size = self.size(withAttributes: fontAttribute)  // for Single Line
       return size;
    }
}

像...一样使用它

        let Str = "ABCDEF"
        let Font =  UIFont.systemFontOfSize(19.0)
        let SizeOfString = Str.SizeOfString(font: Font!)

3
投票

斯威夫特 4

extension String {
    func SizeOf(_ font: UIFont) -> CGSize {
        return self.size(withAttributes: [NSAttributedStringKey.font: font])
    }
}

2
投票

这是针对 swift 2.3 版本的。你可以得到字符串的宽度。

var sizeOfString = CGSize()
if let font = UIFont(name: "Helvetica", size: 14.0)
    {
        let finalDate = "Your Text Here"
        let fontAttributes = [NSFontAttributeName: font] // it says name, but a UIFont works
        sizeOfString = (finalDate as NSString).sizeWithAttributes(fontAttributes)
    }

1
投票

不确定这有多有效,但我写了这个函数来返回适合给定宽度的字符串的点大小:

func fontSizeThatFits(targetWidth: CGFloat, maxFontSize: CGFloat, font: UIFont) -> CGFloat {
    var variableFont = font.withSize(maxFontSize)
    var currentWidth = self.size(withAttributes: [NSAttributedString.Key.font:variableFont]).width

    while currentWidth > targetWidth {
        variableFont = variableFont.withSize(variableFont.pointSize - 1)
        currentWidth = self.size(withAttributes: [NSAttributedString.Key.font:variableFont]).width
    }

    return variableFont.pointSize
}

它会像这样使用:

textView.font = textView.font!.withSize(textView.text!.fontSizeThatFits(targetWidth: view.frame.width, maxFontSize: 50, font: textView.font!))


0
投票

我这样做的方式我的代码是扩展 UIFont:(这是 Swift 4.1)

extension UIFont {


    public func textWidth(s: String) -> CGFloat
    {
        return s.size(withAttributes: [NSAttributedString.Key.font: self]).width
    }

} // extension UIFont

0
投票

SwiftUI 与 Swift5

在 SwiftUI 中,您找不到将 UIFont 转换为 Font 的简单方法。所以以前的答案可能不起作用。 您可以在 overlay() 修饰符中使用 GeometryReader{ geometryProxy in } 来获取文本大小。请注意,如果您不在 overlay() 内部使用它,View 将尽可能地扩展。

如果你想把变量传递出去,你可能需要写一个视图扩展函数来做。

Text("Lat.: \(latitude), Lon.: \(longitude) ")
    .font(Font.custom("ChalkboardSE-Light",size: 20))
    .overlay(
    GeometryReader{ geometryProxy in
        Image("button_img")
             .resizable()
             .frame(width: 10 , height: 10)
             .offset(x: geometryProxy.size.width)
             .extensionFunction{ //... }
              })
    

几天前我从另一个线程复制了扩展功能。

extension View{
    func extensionFunction(_ closure:() -> Void) -> Self{
        closure()
        return self
    }
    
}

0
投票

2022 年,已更新。

对于这个非常古老的问题/答案,我只是采用当前的方式来做到这一点always有效,这是下面答案的组合。

By always 我的意思是最常见的情况 - 不幸的是这是最棘手的情况 - 当你从字面上修改

drawText#in rect
.

fileprivate extension String {
    func realSize(font: UIFont) -> CGSize {
        var s = self.size(withAttributes: [NSAttributedString.Key.font: font])
        s.width = size.width.rounded(.up)
        s.height = size.height.rounded(.up)
        return s
    }
}

请注意,它四舍五入大小(具体来说,向上)。为什么?在大多数情况下你会想要这个,因为通常布局有均匀大小的点,这是“你需要的”来匹配其他渲染。

(请注意,无论如何差异绝对很小)。两种方式都试试看,如你所愿。

因此例如像

class StableLabel: UILabel {

    override var intrinsicContentSize: CGSize {
        // we arrange to return some fixed-always size ...
    }

    override func drawText(in rect: CGRect) {
        let painful = text!.realSize(font: font)

        // Assuming the size of this label is absolutely fixed by the layout,
        // we want to ALWAYS draw the text sitting at the BOTTOM CENTER
        // of the fixed size of this label, even as the size is changed.
        // For example a focus or such may be changing the point size.

        let left = knownSize.width - painful.width) / 2.0
        let down = knownSize.height - painful.height)
        let r = CGRect(origin: CGPoint(x: left, y: down), size: painful)
        super.drawText(in: r)
    }

0
投票

对于 SwiftUI,您不能使用 Font,因为它没有大小(widthAttributes: 但是如果你定义相同的 UIFont 那么你可以获得这个文本标签的大小。 然后这些方法作为字符串扩展工作:

func widthOfString(usingUIFont font: UIFont) -> CGFloat {
    let fontAttributes = [NSAttributedString.Key.font: font]
    //Only works with UIFont not Font
    let size = self.size(withAttributes: fontAttributes)
    return size.width
}

func heightOfString(usingUIFont font: UIFont) -> CGFloat {
    let fontAttributes = [NSAttributedString.Key.font: font]
    //Only works with UIFont not Font
    let size = self.size(withAttributes: fontAttributes)
    return size.height
}

func heightOfStringBasedOnWidth(usingUIFont font: UIFont, width:CGFloat) -> CGFloat {
    let fontAttributes = [NSAttributedString.Key.font: font]
    //Only works with UIFont not Font, very nice method since you can limit the width
    //The below method just returns the height of the font regardless of width so you need to iterate over it
    let size = self.size(withAttributes: fontAttributes)
    if(size.width < width)
    {
        return size.height
    }
    else
    {
        var desiredWidth = size.width
        var lines:Int = 1
        while desiredWidth > width {
            desiredWidth -= width
            lines += 1
        }
        return size.height * CGFloat(lines)
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.