cbpowell / MarqueeLabel

A drop-in replacement for UILabel, which automatically adds a scrolling marquee effect when the label's text does not fit inside the specified frame
MIT License
4.19k stars 561 forks source link

Support for automatic font size shrinking #264

Closed vladkorotnev closed 3 years ago

vladkorotnev commented 3 years ago

In my app's use-case, the scrolling label is used as a window title and is the only scrolling label in the whole view. However more often than not the text will be just a tiny bit too long (like, half a character too long) and make the label start scrolling.

To solve this, I've decided to allow the label to shrink the font until about 85% of it's original size where it becomes less easy to read, and only if it still won't fit, let iit scroll.

Thus with this change the component should now support the Minimum Font Scale / Minimum Font Size properties in Interface Builder to allow the label to shrink before it starts scrolling.

cbpowell commented 3 years ago

This is a great idea, thanks! I'm going to play around with the method of calculating the new size, because I think it would need to support attributed strings as well.

I'm not entirely sure how UILabel handles attributed strings when also setting minimumScaleFactor, like if it scales the font size for all font-size-ranges by the same factor (seems like that would be the simple way). I'm almost wondering if it would be easier and have low enough overhead to:

  1. just create a new, throwaway UILabel instance,
  2. apply the attributed string and minimumScaleFactor that have been set to MarqueeLabel,
  3. ask that throwaway label for it's sizeThatFits() and use that for the comparison, and
  4. discard the throwaway UILabel instance
cbpowell commented 3 years ago

To follow up: don't think that will work! Unless I'm just missing it, there doesn't seem to be a way to check the size of a UILabel at the scaled-down condition. sizeThatFits() seems to just return the "true" size at a scale factor of 1.0, so you can't use that to check.

Going to try adjusting your original approach to add a step of first enumerating all the font attributes to apply the scale factor to each.

vladkorotnev commented 3 years ago

If diving that deep I wonder what the Shrink Font Spacing also does

Anyway I assumed when attributedText is set text would be nil and the function won’t proceed so I didn’t consider that sadly :/ we didn’t use attributed strings in our app.

One approach would be to indeed iterate on all of the attributed ranges and ranges in between them and calculate the sizes using the specified font or default font. But I’m not sure how slow the measureString function is and the check function seems to be called quite often.

vladkorotnev commented 3 years ago

Or use a throwaway Label indeed but premultiplying the scale across the whole attributed string and default font, then using sizeThatFits as proposed :-)

cbpowell commented 3 years ago

@vladkorotnev I implemented the approach of scaling all the fonts in the string, and it seems reasonably performant? I did a quick time profiling and it didn't seem to hit too hard.

I pushed those edits directly to your branch, take a look and see if it works for you when you get a chance!

vladkorotnev commented 3 years ago

I'll have a look once I'm back in office! Would sublabel.attributedText contain the same string in case we only use sublabel.text?

cbpowell commented 3 years ago

Thanks!

Yep - if you set text on a UILabel, it replaces the attributedText with an attributed string with equivalent attributes to whatever font/color/size/etc. discrete properties you had set. And vice versa, setting attributedText replaces the text property with whatever the raw string is, and sets the discrete properties to whatever the attributed string has at the 1st character.

vladkorotnev commented 3 years ago

Hello,

I have just returned to the office now and your approach seems to work just as fine as the original one in our app.

cbpowell commented 3 years ago

Awesome! Thanks for checking it out - I'll merge it in.

vladkorotnev commented 3 years ago

Thank you very much!