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

Swift4中如何通过代码设置字体大小以适配UILabel的行数限制

解决多行UILabel自动适配字体大小的问题

从你提供的设备显示效果可以看到,需要让不同长度的文本在指定行数内自动调整字体大小,刚好填满设置的行数限制,下面是具体的解决方案:

iPhone 5s 文本显示效果
iPhone X 文本显示效果

核心思路

当前你的代码仅设置了固定字体大小和sizeToFit(),无法实现动态适配。我们可以通过从大到小尝试字体尺寸的方式,利用boundingRect(with:options:attributes:context:)计算文本在特定字体下的渲染尺寸,直到文本行数刚好符合设置的限制值。

具体实现步骤

1. 给UILabel添加字体适配扩展

写一个通用的扩展方法,方便所有UILabel复用这个逻辑:

extension UILabel {
    /// 调整字体大小以适配指定的最大行数
    /// - Parameters:
    ///   - maxLines: 允许的最大行数
    ///   - maxFontSize: 初始尝试的最大字体大小
    ///   - minFontSize: 允许的最小字体大小,防止字体过小
    func adjustFontSizeToFit(maxLines: Int, maxFontSize: CGFloat, minFontSize: CGFloat = 10) {
        guard let text = text, !text.isEmpty, let baseFont = font else { return }
        
        // 获取Label的可用宽度(去除内边距)
        let availableWidth = bounds.width - contentEdgeInsets.left - contentEdgeInsets.right
        guard availableWidth > 0 else { return }
        
        var currentFontSize = maxFontSize
        var fits = false
        
        repeat {
            let testFont = baseFont.withSize(currentFontSize)
            let textAttributes: [NSAttributedString.Key: Any] = [.font: testFont]
            
            // 计算文本在当前字体下的完整尺寸
            let textSize = text.boundingRect(
                with: CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude),
                options: .usesLineFragmentOrigin,
                attributes: textAttributes,
                context: nil
            ).size
            
            // 计算当前文本的行数
            let calculatedLines = ceil(textSize.height / testFont.lineHeight)
            
            if calculatedLines <= CGFloat(maxLines) {
                fits = true
                break
            }
            
            // 每次缩小0.5pt,保证精度和效率
            currentFontSize -= 0.5
        } while currentFontSize >= minFontSize
        
        // 设置最终适配的字体
        font = baseFont.withSize(currentFontSize)
    }
}

2. 优化你的Label初始化代码

去掉sizeToFit(),因为我们将通过Auto Layout确定Label宽度后再动态调整字体:

let headingLabel : UILabel = {
    let hl = UILabel()
    hl.translatesAutoresizingMaskIntoConstraints = false
    hl.textColor = .black
    hl.lineBreakMode = .byWordWrapping
    hl.numberOfLines = 3
    hl.clipsToBounds = true
    // 设置初始最大字体,后续会被适配方法调整
    hl.font = UIFont.systemFont(ofSize: 20)
    return hl
}()

let descriptionLabel : UILabel = {
    let dl = UILabel()
    dl.translatesAutoresizingMaskIntoConstraints = false
    dl.textColor = .black
    dl.lineBreakMode = .byWordWrapping
    dl.numberOfLines = 5
    dl.clipsToBounds = true
    dl.alpha = 0.8
    // 设置初始最大字体,后续会被适配方法调整
    dl.font = UIFont.systemFont(ofSize: 18)
    return dl
}()

3. 在合适的时机调用适配方法

必须确保Label的宽度已经确定(比如Auto Layout完成布局)后再调用,否则尺寸计算会不准确。推荐在viewDidLayoutSubviews中调用,或者在网络请求拿到文本并设置后调用:

// 假设从网络JSON获取到文本
func setupLabelsWithNetworkData() {
    let fetchedHeading = "从网络获取的标题内容"
    let fetchedDescription = "从网络获取的长描述内容,可能长度变化不定"
    
    headingLabel.text = fetchedHeading
    descriptionLabel.text = fetchedDescription
    
    // 调用适配方法,传入各自的行数限制和初始最大字体
    headingLabel.adjustFontSizeToFit(maxLines: 3, maxFontSize: 20)
    descriptionLabel.adjustFontSizeToFit(maxLines: 5, maxFontSize: 18)
}

// 在布局完成后调用,确保宽度准确
override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    setupLabelsWithNetworkData()
}

关键注意事项

  • 确保Label宽度固定:如果Label的宽度还未确定(比如Auto Layout还未完成),boundingRect计算出的尺寸会偏差,导致适配效果不佳。
  • 合理设置字体范围:minFontSize可以根据UI需求调整,避免字体过小影响可读性;maxFontSize建议设为你期望的最大显示字体。
  • 保持换行模式一致:lineBreakMode要设置为.byWordWrapping,和计算尺寸时的选项匹配,确保行数计算准确。

这样处理后,不管文本长度如何变化,两个Label都会自动调整字体大小,刚好填满你设置的行数限制,在不同尺寸的设备上都能保持一致的显示效果~

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:49:17