UILabel 子类 - 尽管标签高度正确,但文本在底部被截断

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

我的 UILabel 子类在底部截断文本时遇到问题。标签的高度适合文本,底部还有一些 space,但文本仍被截断。

红色条纹是添加到标签层的边框。

我将标签子类化以添加边缘插入。

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))
}

但是,在这种特殊情况下,插入为零。

原来问题出在

self.lineBreakMode = .ByClipping

改为

self.lineBreakMode = .ByCharWrapping

解决了问题

同时为标签提供 topAnchor 和 centerYAnchor 时发生在我身上。 只留下一个锚点就解决了这个问题。

其他答案对我没有帮助,但是将标签的高度限制在它需要的任何高度,就像这样:

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

此外,如果标签的 text 字段是 nil 或等于 ""[=,sizeThatFits(_:) 将 return 尺寸 0 by 0 16=]

我的问题是标签的(垂直)内容抗压缩优先级不够高;将其设置为必需 (1000) 修复它。

看起来其他非 OP 答案可能是针对同一潜在问题的某种解决方法。

我 运行 也参与其中,但想避免添加高度限制。我已经创建了一个 UILabel subclass,它允许我添加内容插图(但目的是将 tableHeaderView 直接设置为标签,而不必将其包含在另一个视图中)。使用 class 我可以设置底部插图来解决字体剪裁问题。

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
        )
    }

}

这可以简化为字体剪辑:

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
        )
    }

}

我在使用 Helvetica Neue Condensed Bold 字体时遇到了同样的问题。将标签的 Baseline 属性 从 Align Baselines 更改为 Align Centers 对我有用。您可以通过选择标签在情节提要中轻松更改此设置。

TL'DR

你要找的属性可能是UILabelbaselineAdjustment.

由于旧的 UILabel 的已知错误,因此需要它。试一试:

label.baselineAdjustment = .none

也可以通过 interface builder 进行更改。这个 属性 可以在 UILabel 的属性检查器 下找到,名称为 "Baseline".


说明

这是一个错误

这样的讨论是关于 UILabel 的文本边界框上的错误。在我们的案例中,我们在这里观察到的是这个错误的某个版本。当我们通过 AutoShrink .minimumFontScale.minimumFontSize.

缩小文本 时,看起来 边界框的高度会增加

因此,边界框变得大于行高和 UILabel 高度的可见部分。也就是说,将 baselineAdjustment 属性 设置为默认状态 .alignBaselines,文本与裁剪后的底部对齐,我们可以观察到线条剪裁。

理解这种行为对于解释为什么 set .alignCenters 解决了一些问题而不是其他问题至关重要。只需将较大边界框上的文本居中,仍然可以将其剪裁掉。


解决方案

所以最好的方法是设置

label.baselineAdjustment = .none

.none 案例的文档说:

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

由于绑定框原点与标签的框架相匹配,它应该可以解决启用 AutoShrink 的单行标签的任何问题。

也可以通过 interface builder 进行更改。这个 属性 可以在 UILabel 的属性检查器 下找到,名称为 "Baseline".


文档

您可以在此处阅读有关 UILabel 的 baselineAdjustment官方 documentation 的更多信息。

我有一个垂直的 UIStackView,底部有一个 UILabel。此 UILabel 会切断基线以下的字母(qgy 等),但仅当嵌套在水平 UIStackView 内时.解决方法是将 .lastBaseline 对齐修饰符添加到外部堆栈视图。

    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
    }()