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:
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?
This will work..
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
Also, don't set height constraint for your label more than 2 lines.
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)
}
}
}
}
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.
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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With