Prevent UILabel from clipping it's text when the font size is larger than the label height
Asked Answered
G

4

7

I have a circumstance in my app whereby a label may be given a font size greater than it's height. This is to do with some rather complex architecture and layout code. Normally I would increase the label's height to accommodate the larger font but that is profoundly difficult to do in my scenario and I would rather avoid it.

The next logical thing is to turn clipsToBounds off, to allow the text sublayer to overflow the bounds of the label. Unfortunately this seems to have no effect in this case as the text is still clipped.

Am I missing something?

Gramme answered 18/10, 2017 at 8:43 Comment(9)
How about increasing the label's content compression resistance priority?Filmy
You may also need to check that label.layer.maskToBounds = false alsoStress
What is the desired behavior?Unweave
@Stress I can confirm that it is, additionally, the view debugger verifies that both are false at runtime.Gramme
@Unweave The goal is for the label not to clip the bottom of it's text off. Say for example I have a label of 15pt height, and a font of size 30pt. Only the top 15pts of the text would be visible as the label clips the bottom half. I would like this clipping to not occur, as I would have expected from masksToBounds = false.Gramme
@DariuszBukowski Unfortunately that doesn't have an effect.Gramme
@JacobKing do you want to chat about it?Stress
You could add another UILabel constrained to the centerY of the first with equalWidth and no height constraint. That way this label would be in the same position but would just expand to hold the text. You'd then just need a little logic to determine which label to show based on line height?Aeriell
Try with using Size To Fix Content of Label?Woosley
B
1

Looking at the documentation for UILabel:

https://developer.apple.com/documentation/uikit/uilabel/1620545-textrect

I think you need to override the method textRect(forBounds:limitedToNumberOfLines:) by explicitly increasing the rectangle returned by this method to the containing size of the label’s string rather than the bounds of the label.

(This solution does of course require you to subclass.)

Hope that helps.

Biquadratic answered 18/10, 2017 at 8:59 Comment(2)
That's an interesting idea, I'll give it a try!Gramme
Shoot, so annoyingly I don't have the facility to subclass the UILabel object due to the fact I am using AsyncDisplayKit and the label is wrapped in a ASTextNode with an abstract view.Gramme
C
0

You should be able to get the font height from font.lineHeight and then reduce the font size until the line height is less than the label height.

Cusick answered 18/10, 2017 at 8:53 Comment(2)
The issue is, Vince, that I don't want to reduce my font height. I want to font to be large it's more a case of I would want to increase the label height to accommodate it. As I said in my question, in my circumstance this is hard to do. Hence looking for a layer clipping based solution.Gramme
Ah, I saw that you didn't want to increase the label size but didn't see that you also didn't want to decrease the font size. One other possibility might be to have a second label centred over the existing label that takes the text and resizes appropriately. In that way, the original label just becomes a place holder in the screen layout. (As mentioned above.)Recency
G
0

The reason (need citation) is that UILabel which is embeeded in UIButton cares extra glyph information embedded in font whereas an independent UILabel doesn't.

Solution

You can nest a separate UILabel on top of your UIButton and it will solve the problem. It's ugly but it works. There are few workarounds that you ought to try.

Workarounds

Depending on the scenario here is a small checklist that I found as accepted answer or useful for someone.

1) If you're using a UIButton Make sure you're using this method

[button setTitle forState:]

otherwise you'd need to use the following code to refresh the state

[myButton setNeedsLayout];

2) You might need to adjust your font size to fit the width of the label.

[yourLabel setAdjustsFontSizeToFitWidth:YES];

3) Although setting clipToBounds works in consecutive hierarchy, You might want not want to set individually on either Button or Label.

[yourButton setClipsToBounds:NO];
[yourButton.titleLabel setClipToBounds:NO];

There are few solutions that are pointing UIButton subclassing method which are essentially trying to add UIEdgeInset to button.

Ginelle answered 13/9, 2019 at 10:46 Comment(0)
T
0

I encountered a similar situation when I was animating a UILabel's height change inside of a TableView cell. The top of the Label's text was being clipped as the label grew in size. I understand you cannot subclass the the label for your situation, but posting my solution in case it helps others.

I utilized a subclass of UILabel that adds an inset to the content of the label. Also uses a UIEdgeInset extension which I will provide:

class InsetLabel: UILabel {
    var inset = UIEdgeInsets.zero {
        didSet {
            self.invalidateIntrinsicContentSize()
        }
    }

    override func drawText(in rect: CGRect) {
        super.drawText(in: rect.inset(by: self.inset))
    }

    override open func layoutSubviews() {
        super.layoutSubviews()
        self.preferredMaxLayoutWidth = self.frame.width - self.inset.width
    }

    override var intrinsicContentSize: CGSize {
        let superSize = super.intrinsicContentSize
        let width = superSize.width + self.inset.width
        let height = superSize.height + self.inset.height
        return CGSize(width: width, height: height)
    }
}
extension UIEdgeInsets {
    init(top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) {
        self.init()
        self.top = top
        self.left = left
        self.right = right
        self.bottom = bottom
    }

    init(value: CGFloat) {
        self.init()
        self.top = value
        self.right = value
        self.bottom = value
        self.left = value
    }

    var width: CGFloat { self.left + self.right }
    var height: CGFloat { self.top + self.bottom }
}
Tsushima answered 24/9 at 18:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.