带内边距的自定义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
相关产品推荐
相关产品推荐

