square/Blueprint

AttributedLabel measuring is wrong sometimes

watt opened this issue · 0 comments

watt commented

Problems

There are a couple of related problems with the AttributedLabel element (and by extension, Label).

  1. Measurement is wrong when numberOfLines is set to a value other than 0.

    The current measurement behavior uses NSAttributedString.boundingRect(with:options:context:), which has no affordance for line limits.

  2. Rounding assumes the main screen's scale by default, and will be wrong if rendered on a different screen.

    This one is definitely an edge case, but we should at least consider solving this if it's feasible.

Potential solutions

Static UILabel for measuring

We solved problem 1 internally by using a static prototype instance of UILabel. During measuring we apply the view description to the label and then call sizeThatFits. This solution operates on the assumption that measuring is always done on the main thread, so two labels cannot be measured concurrently.

This does not solve problem 2: if UILabel is not in a window, it returns results rounded to the main screen's scale.

TextKit & UITextView

TextKit measuring methods allow us to do line limits, but in experimentation I have found that TextKit measuring doesn't match UILabel. In fact, it's quite difficult (maybe impossible, without knowing its internals) to accurately reproduce its measuring behavior across every combination of line break mode and line limit.

It's important to get those values right — if they're off slightly, it can cause text to be truncated in places where it should have fit. If we want to use TextKit we may have to switch to UITextView or do our own string rendering to ensure it matches our measuring.

We could also potentially switch between using a UILabel for single-line text and a UITextView for multi-line text, or offer these as separate elements.

Since TextKit does not automatically do any rounding, we'd be free to do this ourselves (perhaps by passing the screen scale down through the Environment).

Custom rendering (hard mode)

As mentioned above, we could side-step UIKit entirely and use TextKit/CoreText to measure and then render strings.

This seems like overkill but it's an option.