UILabel subclass - text cut off in bottom despite label being correct height
Asked Answered
P

8

33

I have a problem with UILabel subclass cutting off text in the bottom. Label is of proper height to fit the text, there is some space left in the bottom, but the text is still being cut off.

The label

The red stripes are border added to label's layer.

I subclass the label to add edge insets.

override func sizeThatFits(size: CGSize) -> CGSize {
    var size = super.sizeThatFits(size)
    size.width += insets.left + insets.right
    size.height += insets.top + insets.bottom
    return size
}

override func drawTextInRect(rect: CGRect) {
    super.drawTextInRect(UIEdgeInsetsInsetRect(rect, insets))
}

However, in this particular case the insets are zero.

Parmenides answered 23/3, 2016 at 11:2 Comment(4)
try to increase UILabel height because the height of label is less than the font size or decrease font size.Ludovika
As you can see, the text height is lower than label's height. Also, if I use regular label instead of this subclass, the text is not cut off.Parmenides
@Parmenides which custom font are you using.Promiscuous
What worked for me was allowing the containing view to become taller, as in Height >=Pauwles
P
22

Turns out the problem was with

self.lineBreakMode = .ByClipping

changing it to

self.lineBreakMode = .ByCharWrapping

Solved the problem

Parmenides answered 23/3, 2016 at 11:26 Comment(0)
R
12

My problem was that the label's (vertical) content compression resistance priority was not high enough; setting it to required (1000) fixed it.

It looks like the other non-OP answers may be some sort of workaround for this same underlying issue.

Raleigh answered 5/7, 2018 at 23:8 Comment(0)
E
12

I was facing the same issue with Helvetica Neue Condensed Bold font. Changing label's Baseline property from Align Baselines to Align Centers did the trick for me. You can change this easily in storyboard by selecting your label.

Eyesight answered 22/8, 2019 at 12:20 Comment(2)
Same here. Using Helvetica Neue Condensed Bold for a text label. Setting the property helped.Imminence
If it didn't work, try ".none". I submit an answer explaining this slight different approach and why ".alignCenters" solution should work only sometimes.Cally
C
8

TL'DR

Probably the property you are looking for is UILabel's baselineAdjustment.

It is needed because of an old UILabel's known bug. Try it:

label.baselineAdjustment = .none

Also it could be changed through interface builder. This property could be found under UILabel's Attributes inspector with the name "Baseline".

Baseline property on Interface Builder


Explanation

It's a bug

There is some discussions like this one about a bug on UILabel's text bounding box. What we observe here in our case is some version of this bug. It looks like the bounding box grows in height when we shrink the text through AutoShrink .minimumFontScale or .minimumFontSize.

As a consequence, the bounding box grows bigger than the line height and the visible portion of UILabel's height. That said, with baselineAdjustment property set to it's default state, .alignBaselines, text aligns to the cropped bottom and we could observe line clipping.

Understanding this behaviour is crucial to explain why set .alignCenters solve some problems but not others. Just center text on the bigger bounding box could still clip it.


Solution

So the best approach is to set

label.baselineAdjustment = .none

The documentation for the .none case said:

Adjust text relative to the top-left corner of the bounding box. This is the default adjustment.

Since bonding box origin matches the label's frame, it should fix any problem for a one-lined label with AutoShrink enabled.

Also it could be changed through interface builder. This property could be found under UILabel's Attributes inspector with the name "Baseline".


Documentation

You could read more here about UILabel's baselineAdjustmenton official documentation.

Cally answered 13/6, 2020 at 19:32 Comment(1)
Your explanation makes sense but doesn't work on my case. I don't know why.Stephainestephan
V
3

Happened for me when providing topAnchor and centerYAnchor for label at the same time. Leaving just one anchor fixed the problem.

Varanasi answered 13/3, 2017 at 19:11 Comment(0)
U
3

I ran into this too, but wanted to avoid adding a height constraint. I'd already created a UILabel subclass that allowed me to add content insets (but for the purpose of setting tableHeaderView straight to a label without having to contain it in another view). Using the class I could set the bottom inset to solve the issue with the font clipping.

import UIKit

@IBDesignable class InsetLabel: UILabel {

    @IBInspectable var topInset: CGFloat = 16
    @IBInspectable var bottomInset: CGFloat = 16
    @IBInspectable var leftInset: CGFloat = 16
    @IBInspectable var rightInset: CGFloat = 16
    var insets: UIEdgeInsets {
        get {
            return UIEdgeInsets(
                top: topInset,
                left: leftInset,
                bottom: bottomInset,
                right: rightInset
            )
        }
    }

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

    override var intrinsicContentSize: CGSize {
        return addInsetsTo(size: super.intrinsicContentSize)
    }

    override func sizeThatFits(_ size: CGSize) -> CGSize {
        return addInsetsTo(size: super.sizeThatFits(size))
    }

    func addInsetsTo(size: CGSize) -> CGSize {
        return CGSize(
            width: size.width + leftInset + rightInset,
            height: size.height + topInset + bottomInset
        )
    }

}

This could be simplified just for the font clipping to:

import UIKit

class FontFittingLabel: UILabel {

    var inset: CGFloat = 16 // Adjust this

    override func drawText(in rect: CGRect) {
        super.drawText(in: rect.inset(by: UIEdgeInsets(
            top: 0,
            left: 0,
            bottom: inset,
            right: 0
        )))
    }

    override var intrinsicContentSize: CGSize {
        let size = super.intrinsicContentSize
        return CGSize(
            width: size.width,
            height: size.height + inset
        )
    }

}
Uremia answered 20/2, 2019 at 15:24 Comment(3)
I suspect some fonts to have a configuration problem which leads to this bug. Very nice solution to get around it.Nucleotide
This is not working for very long text. Last 2 words are getting cut always.Countrywoman
UILabel seems to cut off no matter what anyone tries, it's maddening. The solution is to just shift to a textView-- wish I had done that a long time ago because UILabel is a complete and utter Apple nightmare wrapped in a tornado.Megagamete
A
2

Other answers didn't help me, but what did was constraining the height of the label to whatever height it needed, like so:

let unconstrainedSize = CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)
label.heightAnchor.constraint(equalToConstant: label.sizeThatFits(unconstrainedSize).height).isActive = true

Also, sizeThatFits(_:) will return a 0 by 0 size if your label's text field is nil or equal to ""

Adar answered 29/11, 2017 at 19:33 Comment(0)
U
1

I had a vertical UIStackView with a UILabel at the bottom. This UILabel was cutting off the letters that go below the baseline (q, g, y, etc), but only when nested inside a horizontal UIStackView. The fix was to add the .lastBaseline alignment modifier to the outer stack view.

    lazy var stackView: UIStackView = {
        let stackView = UIStackView(arrangedSubviews: [
            aVerticalStackWithUILabelAtBottom, // <-- bottom UILabel was cutoff
            UIView(),
            someOtherView 
        ])
        stackView.axis = .horizontal
        stackView.spacing = Spacing.one
        stackView.alignment = .lastBaseline // <-- BOOM fixed it
        stackView.isUserInteractionEnabled = true
        return stackView
    }()
Unidirectional answered 30/9, 2021 at 14:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.