You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

带内边距的自定义UILabel因intrinsicContentSize错误导致文本截断

问题

我实现了一个带内边距的AttributedLabel自定义UILabel类,重写了drawText方法与intrinsicContentSize属性。创建该类实例时设置左右内边距为20、行数为0,将其添加到UIStackView后,文本出现截断现象。相关代码及效果截图如下,请问问题原因是什么?

import UIKit

class AttributedLabel: UILabel {
    var cornerRadius: CGFloat = 3 {
        didSet {
            layer.cornerRadius = cornerRadius
        }
    }

    var borderColor: UIColor = .clear {
        didSet {
            layer.borderColor = borderColor.cgColor
            layer.borderWidth = 1
        }
    }

    var masksToBounds: Bool = false {
        didSet {
            layer.masksToBounds = masksToBounds
        }
    }

    var borderWidth: CGFloat = 1 {
        didSet {
            layer.borderWidth = borderWidth
        }
    }

    private var strikeThrough = false
    private var paddingTop: CGFloat = 0
    private var paddingBottom: CGFloat = 0
    private var paddingLeft: CGFloat = 0
    private var paddingRight: CGFloat = 0
    override var text: String? {
        didSet {
            if strikeThrough {
                attributedText = NSAttributedString(string: text ?? "", strikeThrough: true)
            }
        }
    }

    // MARK: - Initialization

    convenience init(
        paddingTop: CGFloat = 0,
        paddingBottom: CGFloat = 0,
        paddingLeft: CGFloat = 0,
        paddingRight: CGFloat = 0,
        strikeThrough: Bool = false
    ) {
        self.init()
        self.paddingTop = paddingTop
        self.paddingBottom = paddingBottom
        self.paddingLeft = paddingLeft
        self.paddingRight = paddingRight
        self.strikeThrough = strikeThrough
        backgroundColor = .yellow
    }

    // MARK: - Overriden

    override func drawText(in rect: CGRect) {
        let insets = UIEdgeInsets(top: paddingTop, left: paddingLeft, bottom: paddingBottom, right: paddingRight)
        super.drawText(in: rect.inset(by: insets))
    }

    override var intrinsicContentSize: CGSize {
        var contentSize = super.intrinsicContentSize
        contentSize.height += paddingTop
        contentSize.height += paddingBottom
        contentSize.width += paddingLeft
        contentSize.width += paddingRight
        return contentSize
    }
}
private let titleLabel: UILabel = {
    let label = AttributedLabel(paddingLeft: 20, paddingRight: 20)
    label.textColor = .mineShaft
    label.font = UIFont.poppinsMedium.withSize(18)
    label.textAlignment = .left
    label.numberOfLines = 0
    return label
}()

titleLabel.text = "Czy jesteś za wdrożeniem aplikacji PLZ?"
contentStackView.addArrangedSubview(titleLabel)

初始效果:初始效果
修改后效果:修改后效果


问题原因及解决方案

问题原因

  • 当numberOfLines = 0时,UILabel需要自适应多行文本,但当前仅重写了intrinsicContentSize,未处理sizeThatFits方法。UIStackView布局时会调用sizeThatFits获取视图合适尺寸,现有实现未在内边距影响下计算多行文本的实际宽度,导致尺寸计算不足,文本被截断。
  • intrinsicContentSize主要用于单行文本的固有尺寸计算,对于多行自适应场景,sizeThatFits才是决定视图最终尺寸的核心逻辑,当前实现缺失这部分内边距适配。

解决方案

重写sizeThatFits方法,将内边距纳入尺寸计算逻辑:

override func sizeThatFits(_ size: CGSize) -> CGSize {
    // 计算去除内边距后的可用布局尺寸
    let availableSize = CGSize(
        width: size.width - paddingLeft - paddingRight,
        height: size.height - paddingTop - paddingBottom
    )
    // 获取文本在可用尺寸下的实际适配大小
    var fittingSize = super.sizeThatFits(availableSize)
    // 重新加上内边距得到最终视图尺寸
    fittingSize.width += paddingLeft + paddingRight
    fittingSize.height += paddingTop + paddingBottom
    return fittingSize
}

同时优化intrinsicContentSize的计算逻辑,适配多行场景:

override var intrinsicContentSize: CGSize {
    let baseSize = super.intrinsicContentSize
    // 针对多行文本,用sizeThatFits修正固有尺寸的宽度计算
    let fittingSize = sizeThatFits(CGSize(width: baseSize.width, height: CGFloat.greatestFiniteMagnitude))
    return fittingSize
}

修改后,UIStackView将正确计算标签的自适应尺寸,文本不会再出现截断。

内容的提问来源于stack exchange,提问作者kunass2

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 23:47:47