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

如何消除多行富文本Label中的空白区域?

我刚复现了你的问题,确实挺闹心的——当多行富文本Label设为居中对齐时,背景色会铺满每行的整个可用宽度,看起来就像空白区域也被涂黑了。这其实是因为UIKit绘制富文本背景色时,是按每行的排版边界来填充的,而不是只包围文字本身。

下面给你两种实用的解决方案,按需选择:

方案1:让Label宽度适配文字实际宽度,Label整体居中

这是最简单的解决思路:不让文字在Label内部居中,而是让Label本身在父视图里居中,同时Label的宽度刚好匹配文字实际占据的宽度,这样背景色就只会覆盖文字部分。

修改你的lbl()方法即可:

func lbl() {
    let text = "the fox is foxy and likes to run fast through the forest"
    let attributes: [NSAttributedString.Key: Any] = [
        .foregroundColor: UIColor.yellow,
        .backgroundColor: UIColor.black,
        .font: UIFont.boldSystemFont(ofSize: 20.0)
    ]
    let attributedText = NSMutableAttributedString(string: text, attributes: attributes)
    
    // 计算富文本在最大宽度限制下的实际尺寸
    let maxWidth = view.bounds.width - 32 // 对应你原来的左右间距
    let textSize = attributedText.boundingRect(
        with: CGSize(width: maxWidth, height: .greatestFiniteMagnitude),
        options: [.usesLineFragmentOrigin, .usesFontLeading],
        context: nil
    ).size
    
    itemLabel.attributedText = attributedText
    itemLabel.font = UIFont.boldSystemFont(ofSize: 20.0)
    itemLabel.textAlignment = .left // 改成左对齐,Label本身会居中
    itemLabel.numberOfLines = 2
    itemLabel.lineBreakMode = .byTruncatingTail
    itemLabel.isUserInteractionEnabled = false
    itemLabel.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(itemLabel)
    
    // 设置约束:Label居中,尺寸匹配文字实际大小
    NSLayoutConstraint.activate([
        itemLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
        itemLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor),
        itemLabel.widthAnchor.constraint(equalToConstant: ceil(textSize.width)),
        itemLabel.heightAnchor.constraint(equalToConstant: ceil(textSize.height))
    ])
}

方案2:自定义富文本背景绘制(更灵活)

如果需要保留Label的宽范围限制(比如允许Label宽度大于文字宽度,但背景色只包裹文字),可以用Core Text手动绘制富文本,精确控制背景色的范围。

先创建一个自定义View:

class CustomTextLabel: UIView {
    var attributedText: NSAttributedString? {
        didSet { setNeedsDisplay() }
    }
    var numberOfLines = 2
    
    override func draw(_ rect: CGRect) {
        super.draw(rect)
        guard let attributedText = attributedText else { return }
        
        let frameSetter = CTFramesetterCreateWithAttributedString(attributedText as CFAttributedString)
        let path = CGMutablePath()
        path.addRect(bounds)
        
        let frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, attributedText.length), path, nil)
        let lines = CTFrameGetLines(frame) as [AnyObject]
        
        var lineOrigins = [CGPoint](repeating: .zero, count: lines.count)
        CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), &lineOrigins)
        
        // 遍历每行,先画背景色再画文字
        for (index, line) in lines.enumerated() {
            if index >= numberOfLines { break }
            
            let line = line as! CTLine
            let lineRect = CTLineGetImageBounds(line, UIGraphicsGetCurrentContext())
            let origin = lineOrigins[index]
            
            // 绘制背景色:只覆盖文字实际宽度
            let bgRect = CGRect(
                x: origin.x + lineRect.origin.x,
                y: origin.y + lineRect.origin.y,
                width: lineRect.width,
                height: lineRect.height
            )
            UIColor.black.setFill()
            bgRect.fill()
            
            // 绘制文字
            UIGraphicsGetCurrentContext()?.saveGState()
            UIGraphicsGetCurrentContext()?.textMatrix = .identity
            UIGraphicsGetCurrentContext()?.translateBy(x: origin.x, y: origin.y + bounds.height - lineRect.origin.y - lineRect.height)
            UIGraphicsGetCurrentContext()?.scaleBy(x: 1, y: -1)
            CTLineDraw(line, UIGraphicsGetCurrentContext()!)
            UIGraphicsGetCurrentContext()?.restoreGState()
        }
    }
}

然后在ViewController里替换原来的UILabel:

class ViewController: UIViewController {
    let itemLabel = CustomTextLabel()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = UIColor.white
        lbl()
    }
    
    func lbl() {
        let text = "the fox is foxy and likes to run fast through the forest"
        let attributes: [NSAttributedString.Key: Any] = [
            .foregroundColor: UIColor.yellow,
            .font: UIFont.boldSystemFont(ofSize: 20.0)
        ]
        itemLabel.attributedText = NSMutableAttributedString(string: text, attributes: attributes)
        itemLabel.numberOfLines = 2
        itemLabel.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(itemLabel)
        
        NSLayoutConstraint.activate([
            itemLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            itemLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            itemLabel.widthAnchor.constraint(lessThanOrEqualTo: view.widthAnchor, constant: -32)
        ])
    }
}

简单总结:如果只是解决当前的空白问题,方案1足够高效;如果需要更灵活的排版控制(比如复杂的文字高亮、自定义行间距),方案2更合适。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 07:28:14