Swift 3 - Adjust Font Size to Fit Width, Multiple Lines
Asked Answered
N

5

12

I have a UILabel and it is set to 42.0 pt font, and the width of the label is set using autoconstraints based on factors other than the label itself (aka the things to the right and left of the label determine the label's width).

I would like to auto-adjust the font size to fit the width of the label, however also break to two lines when it can. Similar to this:

enter image description here

I know you can adjust the font size to fit the width of the label, but only when the number of lines is set to 1.

How would I accomplish this?

Nephrosis answered 4/3, 2017 at 3:21 Comment(2)
What happens if you set the number of lines to 0?Carburize
The text still stays on one line, and the font size doesn't shrinkNephrosis
P
6

This will work..

  • Set minimum scale factor for your label. as shown in this image.
  • Set adjustsFontSizeToFitWidth to true
  • Set number of lines = 2 // or zero (0) if you want more number of lines
  • Set line breaking mode to '.byTruncatingTail' for 2 lines

Swift 5
Set number of lines zero for dynamic text information, it will be useful for varying text.

var label = UILabel()
let stringValue = "A label\nwith\nmultiline text."
label.text = stringValue
label.numberOfLines = 2 // 0
label.lineBreakMode = .byTruncatingTail // or .byWrappingWord
label.minimumScaleFactor = 0.5 // It is not required but nice to have a minimum scale factor to fit text into label frame
label.adjustsFontSizeToFitWidth = true //needed in Swift 5

enter image description here

Also, don't set height constraint for your label more than 2 lines.

Populace answered 4/3, 2017 at 5:5 Comment(3)
I tried this but the font size doesn't scale down when you set numberOfLines to anything besides 1Nephrosis
Please share me your code, that you've tried, I've tested this and working for your examples very well.Populace
It is important to use .byTruncatingTail which is also the default. This solution have a comment saying or .byWrappingWord and that will not work. See also https://mcmap.net/q/1010879/-autoresize-multiline-uilabel-in-swift/833197.Monograph
E
2

Interesting question. Here's my solution:

let labelText = self.mylabel.text //where mylabel is the label
let labelSeperated = self.labelText.components(seperatedBy: " ")
if labelSeperated.count > 1 {
    myLabel.lineBreakMode = .byWordWrapping
    myLabel.numberOfLines = 0 
} else {
    myLabel.numberOfLines = 1
    myLabel.adjustsFontSizeToFitWidth = true 
}

Put this code where the label will be changed. It sets the line number to 0 if there are two or more numbers, otherwise set to 1 line only.

If you want to resize multi-line labels, check out this blog post.

Electrotherapeutics answered 4/3, 2017 at 4:32 Comment(2)
The only problem with this solution is that the font size won't change when there are more than one line of textNephrosis
@ChrisSchlitt have you looked at the blog post?Electrotherapeutics
U
1

Swift 5

func setFontForLabel(label:UILabel, maxFontSize:CGFloat, minFontSize:CGFloat, maxLines:Int) {

    var numLines: Int = 1
    var textSize: CGSize = CGSize.zero
    var frameSize: CGSize = CGSize.zero
    let font: UIFont = label.font.withSize(maxFontSize)

    frameSize = label.frame.size

    textSize = (label.text! as NSString).size(withAttributes: [NSAttributedString.Key.font: font])

    // Determine number of lines
    while ((textSize.width/CGFloat(numLines)) / (textSize.height * CGFloat(numLines)) > frameSize.width / frameSize.height) && numLines < maxLines {
        numLines += 1
    }

    label.font = font
    label.adjustsFontSizeToFitWidth = true
    label.numberOfLines = numLines
    label.minimumScaleFactor = minFontSize/maxFontSize
}

Swift 3

I looked at the post that paper111 posted. Unfortunately it's in Obj-C and the sizeWithFont: ,constrainedToSize: , lineBreakMode: method has been deprecated. (- - );

His answer was good, but still didn't provide a fixed size. What I did was to start with a UILabel that had everything but the height (this is probably the same for most people).

let myFrame = CGRect(x: 0, y:0, width: 200, height: self.view.height)
let myLbl = UILabel(frame: myFrame)
let finalHeight:CGFloat = 300

myLbl.font = UIFont(name: "Chalkduster", size: 16.0)
myLbl.lineBreakMode = .byWordWrapping
myLbl.numberOfLines = 0
myLbl.text = "Imagine your long line of text here"
addSubview(myLbl)
myLbl.sizeToFit()

guard myLbl.frame.height > finalHeight else { return }
var fSize:CGFloat = 16 //start with the default font size
    repeat {
        fSize -= 2
        myLbl.font = UIFont(name: "Chalkduster", size: fSize)
        myLbl.sizeToFit()
    } while myLbl.frame.height > finalHeight

You can see that there's a guard blocking the resize if it's not needed. Also, calling sizeToFit() many times isn't ideal, but I can't think of another way around it. I tried to use myLbl.font.withSize(fSize) in the loop but it wouldn't work, so I used the full method instead.

Hope it works for you!

Uniliteral answered 24/3, 2017 at 5:19 Comment(0)
U
1

Swift 5

extension UILabel{
func adjustsFontSizeToFit(maxFontSize:CGFloat,width:CGFloat,height:CGFloat) {
    self.numberOfLines = 0
    var fontSize:CGFloat = maxFontSize
    if self.sizeThatFits(CGSize(width: width, height: .infinity)).height > height{
        while self.sizeThatFits(CGSize(width: width, height: .infinity)).height > height{
            fontSize -= 1
            self.font = self.font.withSize(fontSize)
        }
    }
}

}

Unready answered 22/7, 2020 at 19:1 Comment(0)
E
0

@Krunal's answer helped me but it doesn't work when you have unknown number of lines so here's the solution I came up with. You can also set the maximum and minimum font size. Hope this helps someone!

Swift 2.2 - Sorry, haven't migrated to Swift 3 yet.

func setFontForLabel(label:UILabel, maxFontSize:CGFloat, minFontSize:CGFloat, maxLines:Int) {

    var numLines: Int = 1
    var textSize: CGSize = CGSizeZero
    var frameSize: CGSize = CGSizeZero
    var font: UIFont = UIFont.systemFontOfSize(maxFontSize)

    frameSize = label.frame.size

    textSize = (label.text! as NSString).sizeWithAttributes([NSFontAttributeName: font])

    // Determine number of lines
    while ((textSize.width/CGFloat(numLines)) / (textSize.height * CGFloat(numLines)) > frameSize.width / frameSize.height) && numLines < maxLines {
        numLines += 1
    }

    label.font = font
    label.adjustsFontSizeToFitWidth = true
    label.numberOfLines = numLines
    label.minimumScaleFactor = minFontSize/maxFontSize
}
Ensilage answered 22/5, 2017 at 2:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.