Swift4中如何通过代码设置字体大小以适配UILabel的行数限制
解决多行UILabel自动适配字体大小的问题
从你提供的设备显示效果可以看到,需要让不同长度的文本在指定行数内自动调整字体大小,刚好填满设置的行数限制,下面是具体的解决方案:


核心思路
当前你的代码仅设置了固定字体大小和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
相关产品推荐
相关产品推荐

