Sti Sti - 6 months ago 21
Swift Question

How to get UILabel to resize better?

I am trying to make my multiline

UILabel
as small as possible with given max-width, but the
sizeToFit()
and
sizeThatFits(_,_)
methods aren't giving the results I want.

Take a look at this image:
enter image description here
The red rectangle represents the width that I pass in
sizeThatFits
(while height being Max). Obviously, as you can see, this method does in fact return a size that fits, but it does not give me the smallest size possible, which I want.

Let's say I specified
max-width: 300
. This actual result is giving me a size of ca.
280*50
.
As you can see in the image, the text is now written like this:

Here is some text that is supposed to
align nicely


What I want to achieve is this:

Here is some text that is
supposed to align nicely


This result would've had the same height, but a much smaller width, e.g
200*50

I realize that it's difficult to define "smallest size possible", as it could return this:

Here
is
some
text
that
...


Or even just a single letter per line. But given that
sizeThatFits
returns this, with a given width and height, why doesn't it return my wanted result, which is the same height, but with smaller width. Why isn't the smallest fit returned? Does a function like this exist?

Sti Sti
Answer

I made my own solution, which works pretty well:

private func calculateWantedSize(label:UILabel)->CGSize{
    var lastAcceptableWidth = label.bounds.width
    let currentHeight = label.sizeThatFits(CGSize(width: label.bounds.width, height: CGFloat.max)).height

    var tempHeight = currentHeight
    while(tempHeight == currentHeight){
        let newWidth = lastAcceptableWidth - 1
        tempHeight = label.sizeThatFits(CGSize(width: newWidth, height: CGFloat.max)).height
        if tempHeight == currentHeight{
            lastAcceptableWidth = newWidth
        }
    }
    return CGSize(width: lastAcceptableWidth, height: label.bounds.height)
}

Here I'm sending my label to a function which first calculates the current width and height of the label, then loops through width-1 and checks the resulting height until the height is changed. The height will change when the width is so low that it needs a new line. When this happens, I return the last valid width which still needed the same height as original.

This is perfect for my problem, using attributed text etc.

Of course, when using this, be sure to do some checking before calling. I don't know what will happen if you use this function when the label has a single word, or a single letter, or no text at all. I have only tested this for the cases I know will happen with this app, and it works well.