I have a UILabel that displays some chars. Like "x", "y" or "rpm". How can I calculate the width of the text in the label (it does not ues the whole available space)? This is for automatic layouting, where another view will have a bigger frame rectangle if that UILabel has a smaller text inside. Are there methods to calculate that width of the text when a UIFont and font size is specified? There's also no line-break and just one single line.
-
I don't know how you would do this with any font type, however, if you are using a fixed width font, you can calculate using the number of characters. I'm not entirely sure of the formula. – jgallant Aug 24 '09 at 19:57
16 Answers
Since sizeWithFont
is deprecated, I'm just going to update my original answer to using Swift 4 and .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]
let text = "Your Text Here"
let size = (text as NSString).size(withAttributes: fontAttributes)
}
The size should be the onscreen size of "Your Text Here" in points.

- 3,960
- 2
- 16
- 26

- 5,447
- 2
- 25
- 29
-
Thank you for posting this solution. I wrote an extension based on your answer. It's posted below. – Adrian Oct 06 '16 at 19:26
-
-
I'm using SwiftUI, but there are no suitable counterparts. I'm handling this if #if checks to import UIKit vs. AppKit. One problem I found, though, is that in the documentation, it says I need to pass a UIFont as an NSAttributedString.Key.font even in macOS (where UIFont, part of UIKit, doesn't exist). Link to documentation: https://developer.apple.com/documentation/foundation/nsattributedstring/key/1528839-font#:~:text=The%20value%20of%20this%20attribute%20is%20a%20UIFont%20object – Aadish Verma Aug 14 '23 at 16:29
sizeWithFont:
is now deprecated, use sizeWithAttributes:
instead:
UIFont *font = [UIFont fontWithName:@"Helvetica" size:30];
NSDictionary *userAttributes = @{NSFontAttributeName: font,
NSForegroundColorAttributeName: [UIColor blackColor]};
NSString *text = @"hello";
...
const CGSize textSize = [text sizeWithAttributes: userAttributes];
-
9Note: `sizeWithAttributes:` method returns fractional sizes; to use a returned size to size views, you must raise its value to the nearest higher integer using the ceil function. – Allen Apr 14 '15 at 04:13
You can do exactly that via the various sizeWithFont:
methods in NSString UIKit Additions. In your case the simplest variant should suffice (since you don't have multi-line labels):
NSString *someString = @"Hello World";
UIFont *yourFont = // [UIFont ...]
CGSize stringBoundingBox = [someString sizeWithFont:yourFont];
There are several variations of this method, eg. some consider line break modes or maximum sizes.

- 9,564
- 146
- 81
- 122

- 8,855
- 4
- 41
- 39
-
1
-
11"sizeWithFont:" is deprecated. Answer by wcochran should be the marked one. – Ferran Maylinch Mar 09 '14 at 22:02
-
5Since you have the green checkmark, you should change your answer to use sizeWithAttributes. – Glenn Howes Mar 14 '16 at 20:17
Update Sept 2019
This answer is a much cleaner way to do it using new syntax.
Original Answer
Based on Glenn Howes' excellent answer, I created an extension to calculate the width of a string. If you're doing something like setting the width of a UISegmentedControl
, this can set the width based on the segment's title string.
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)
}
}
usage:
// Set width of segmentedControl
let starString = "⭐️"
let starWidth = starString.widthOfString(usingFont: UIFont.systemFont(ofSize: 14)) + 16
segmentedController.setWidth(starWidth, forSegmentAt: 3)

- 16,233
- 18
- 112
- 180
-
1
-
1Why not just one function that returns size (CGSize)? Why do the work twice? – wcochran Jan 25 '18 at 17:28
-
-
1
-
Nevermind, this works but from my tests you need to add additional padding of _at least 15_ in order to prevent the text from being truncated. – Max Feb 20 '18 at 16:08
Oneliner in Swift 4.2
let size = "abc".size(withAttributes:[.font: UIFont.systemFont(ofSize: 18.0)])

- 5,355
- 43
- 38
Swift-5
Use intrinsicContentSize to find the text height and width.
yourLabel.intrinsicContentSize.width
This will work even you have custom spacing between your string like "T E X T"

- 24,880
- 6
- 40
- 67
iOS 16 update:
UILabel.textRect(forBounds: CGRect, limitedToNumberOfLines: Int)
If you're struggling to get text width with multiline support, so you can use the next code (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
}
And the same way you could find text height if you need to (just switch the constraintBox implementation):
let constraintBox = CGSize(width: maxWidth, height: .greatestFiniteMagnitude)
Or here's a unified function to get text size with multiline support:
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
}
Usage:
let textSize = labelSize(for: "SomeText", maxWidth: contentView.bounds.width, maxHeight: .greatestFiniteMagnitude)
let textHeight = textSize.height.rounded(.up)
let textWidth = textSize.width.rounded(.up)

- 4,311
- 1
- 38
- 49
-
This part of your code ".rounded(.up)" save me trouble, because in my code, the returned width was like 0.2-0.3 less that it should be, and I was not thinking of doing a round up. Thank you! – Alessandro Pace Aug 25 '22 at 08:06
-
This no longer works for me (iOS 16). I was able to get it to work about the same using `UILabel.textRect(forBounds: CGRect, limitedToNumberOfLines: Int)`. – Rolf Locher Dec 24 '22 at 00:38
-
This simple extension in Swift works well.
extension String {
func size(OfFont font: UIFont) -> CGSize {
return (self as NSString).size(attributes: [NSFontAttributeName: font])
}
}
Usage:
let string = "hello world!"
let font = UIFont.systemFont(ofSize: 12)
let width = string.size(OfFont: font).width // size: {w: 98.912 h: 14.32}

- 1,682
- 3
- 22
- 34
For 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;
}
}
Use it like...
let Str = "ABCDEF"
let Font = UIFont.systemFontOfSize(19.0)
let SizeOfString = Str.SizeOfString(font: Font!)

- 1,212
- 13
- 9
Swift 4
extension String {
func SizeOf(_ font: UIFont) -> CGSize {
return self.size(withAttributes: [NSAttributedStringKey.font: font])
}
}

- 887
- 1
- 12
- 26
This is for swift 2.3 Version. You can get the width of string.
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)
}

- 2,927
- 4
- 31
- 44

- 2,810
- 1
- 19
- 31
Not sure how efficient this is, but I wrote this function that returns the point size that will fit a string to a given width:
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
}
And it would be used like this:
textView.font = textView.font!.withSize(textView.text!.fontSizeThatFits(targetWidth: view.frame.width, maxFontSize: 50, font: textView.font!))

- 2,780
- 2
- 19
- 40
The way I am doing it my code is to make an extension of UIFont: (This is Swift 4.1)
extension UIFont {
public func textWidth(s: String) -> CGFloat
{
return s.size(withAttributes: [NSAttributedString.Key.font: self]).width
}
} // extension UIFont

- 1,203
- 1
- 14
- 27
SwiftUI with Swift5
In SwiftUI, you could not find an easy way to convert UIFont to Font. So the previous answers may not work. You could use GeometryReader{ geometryProxy in } inside the overlay() modifier to get the Text size. Be careful that if you don't use it inside overlay(), the View will expand to as much as it can.
If you want to pass the variable out, you may need to write a View extension function to do so.
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{ //... }
})
I copied the extension function from another thread a couple days ago.
extension View{
func extensionFunction(_ closure:() -> Void) -> Self{
closure()
return self
}
}

- 479
- 7
- 14
For 2022, updated.
For this very old question/answers, I'm just putting in the current way to do it that always works, which is a combo of answers below.
By always I mean the most common case - which is unfortunately the trickest case - when you're literally modifying something inside of 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
}
}
Note that it rounds the size (specifically, up). Why? In most cases you'll want this since typically layouts have even sized points and that's "what you'll need" to match other rendering.
(Note that the difference is absolutely tiny anyway). Try it both ways to see, as you wish.
Hence for example something like
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)
}

- 27,874
- 70
- 431
- 719
For SwiftUI you can't use Font since it has no size(widthAttributes: But if you define the same UIFont then you can get the size of this Text Label. Then these methods work as a String Extension:
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)
}
}

- 511
- 4
- 5