I have a single-line UILabel with the following configuration:
let label = UILabel()
label.text = "Lorem ipsum dolor sit amet"
label.numberOfLines = 1
When I call sizeThatFits(size)
on this label with a small size width, the result is always a size with a bigger width.
let labelSize = label.sizeThatFits(CGSize(width: 30, height: UILabel.noIntrinsicMetric))
// labelSize.width > 30
Shouldn’t sizeThatFits()
return a size that is smaller or equal than the fitting size?
EDIT:
Neither of the suggested answers clarify this question.
Calling textRect(forBounds:limitedToNumberOfLines:)
returns a zero width and systemLayoutSizeFitting()
returns the same size as sizeThatFits()
. There is no other workaround that can be used in any of the suggested answers.
2
Answers
From Apple’s docs:
When we do this:
UIKit says: "hey view (label), what is your size if I want a width of 30?" And the view (label) says: "I can’t fit in a width of 30, so, here is my sizeThatFits".
In this case, on an iPhone 15 Pro, that is:
(207.0, 20.333333333333332)
If we call it like this:
The label says: "I can fit in a width of 1000, so here is my sizeThatFits"* — and we get the same result:
(207.0, 20.333333333333332)
So, what’s the point of the width/height values on
.sizeThatFits()
?Well, these days, we’re almost certainly using auto-layout / constraints, and we wouldn’t call that. However, there are valid use-cases to set the
.frame
of a label.Suppose I want a 30-point width? I do this:
What value do I use for the height?
(personally, I would use
ceil(sz.height)
)Note that if
.numberOfLines = 0
we get a very different return:Now, UIKit says: "hey view (label), what is your size if I want a width of 30?" And the view (label) says: "I can wrap my text, so here is my sizeThatFits for a width of 30".
we get this output:
The documentation says:
The whole point of
sizeThatFits(_:)
is “tell me what size I need to show all of this text.” The width parameter is essentially ignored ifnumberOfLines
is1
.The width supplied to
sizeThatFits(_:)
only comes into play when usingnumberOfLines
of0
(or greater than1
). In that scenario,sizeThatFits(_:)
will now let us know the label’s height (and width) that will fit the text wrapped within the originally supplied width.I understand from your comments that you were expecting it to never return a value whose width that exceeded
30
even ifnumberOfLines
is1
. Right or wrong, that simply is not how it works. If you want to clamp the width to not exceed30
, you would do this yourself: