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

如何在Xcode 16中动态调整UITextView的字体大小?

如何在Xcode 16中动态调整UITextView的字体大小?

嘿,这个需求我太懂了——医疗类APP既要保证合规文本完整展示,又要在不同尺寸的iPhone上看起来舒服,确实挺折腾的。从你提供的截图就能看出核心问题:

  • iPhone SE3 上文本需要缩小才能避免滚动
  • iPhone 16 Pro Max 上有大量留白,需要放大字号填充空间

iPhone SE3 模拟器效果
iPhone 16 Pro Max 模拟器效果

我帮你梳理几个适配Xcode 16和最新Swift语法的实用方案,完美解决你的问题:

方案一:动态计算字号,让文本刚好适配TextView高度

这个思路是先设置基础字号,然后通过计算文本高度逐步调整(缩小或放大),直到文本刚好填满TextView的可用空间,同时限制最大/最小字号避免字体极端化。

首先,我们需要一个辅助方法来计算富文本在特定字号下的高度(保留你设置的不同字体样式,只修改字号):

func calculateTextHeight(for attributedText: NSAttributedString, in textView: UITextView, fontSize: CGFloat) -> CGFloat {
    // 复制原富文本,批量修改所有字体的字号
    let mutableText = NSMutableAttributedString(attributedString: attributedText)
    mutableText.enumerateAttribute(.font, in: NSRange(location: 0, length: mutableText.length)) { value, range, _ in
        if let font = value as? UIFont {
            let scaledFont = font.withSize(fontSize)
            mutableText.addAttribute(.font, value: scaledFont, range: range)
        }
    }
    
    // 计算文本在TextView宽度下的高度
    let availableWidth = textView.bounds.width - textView.textContainerInset.left - textView.textContainerInset.right
    let textRect = mutableText.boundingRect(
        with: CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude),
        options: [.usesLineFragmentOrigin, .usesFontLeading],
        context: nil
    )
    return textRect.height
}

然后在你的ViewController中,设置完富文本后添加这段调整逻辑:

// 你的原有代码:设置consentTextView属性和contentString...
consentTextView.attributedText = contentString

// 开始动态调整字号
let minFontSize: CGFloat = 12   // 最小字号,避免过小无法阅读
let maxFontSize: CGFloat = 18   // 最大字号,避免过大溢出
var targetFontSize: CGFloat = 15 // 初始基准字号

// 获取TextView的可用高度(去掉内边距)
let availableHeight = consentTextView.bounds.height - consentTextView.textContainerInset.top - consentTextView.textContainerInset.bottom

// 迭代调整字号,直到文本刚好适配
while true {
    let currentTextHeight = calculateTextHeight(for: contentString, in: consentTextView, fontSize: targetFontSize)
    
    if currentTextHeight <= availableHeight && targetFontSize < maxFontSize {
        // 文本能放下,尝试放大字号
        targetFontSize += 0.5
    } else if currentTextHeight > availableHeight && targetFontSize > minFontSize {
        // 文本放不下,缩小字号
        targetFontSize -= 0.5
    } else {
        break // 找到最优字号,退出循环
    }
}

// 将最优字号应用到富文本
let finalAttributedText = NSMutableAttributedString(attributedString: contentString)
finalAttributedText.enumerateAttribute(.font, in: NSRange(location: 0, length: finalAttributedText.length)) { value, range, _ in
    if let font = value as? UIFont {
        let scaledFont = font.withSize(targetFontSize)
        finalAttributedText.addAttribute(.font, value: scaledFont, range: range)
    }
}
consentTextView.attributedText = finalAttributedText
consentTextView.isScrollEnabled = false // 文本已适配,关闭滚动

方案二:自定义UITextView子类,封装自动缩放逻辑

如果你的APP多个页面需要这个功能,自定义子类会更便于复用,而且能自动响应视图布局变化(比如旋转屏幕):

class AutoScalingTextView: UITextView {
    // 可配置的字号范围
    var minFontSize: CGFloat = 12
    var maxFontSize: CGFloat = 18
    
    override func layoutSubviews() {
        super.layoutSubviews()
        adjustFontSizeToFit()
    }
    
    private func adjustFontSizeToFit() {
        guard let attributedText = attributedText else { return }
        let availableHeight = bounds.height - textContainerInset.top - textContainerInset.bottom
        
        // 用二分法查找最优字号,比循环更高效
        var low = minFontSize
        var high = maxFontSize
        var bestSize = minFontSize
        
        for _ in 0..<10 { // 迭代10次足够达到精确效果
            let midSize = (low + high) / 2
            let textHeight = calculateTextHeight(for: attributedText, fontSize: midSize)
            
            if textHeight <= availableHeight {
                bestSize = midSize
                low = midSize
            } else {
                high = midSize
            }
        }
        
        // 应用最优字号
        applyFontSize(bestSize)
        // 极端情况下如果还是放不下,自动开启滚动
        isScrollEnabled = calculateTextHeight(for: attributedText, fontSize: bestSize) > availableHeight
    }
    
    private func calculateTextHeight(for attributedText: NSAttributedString, fontSize: CGFloat) -> CGFloat {
        let mutableText = NSMutableAttributedString(attributedString: attributedText)
        mutableText.enumerateAttribute(.font, in: NSRange(location: 0, length: mutableText.length)) { value, range, _ in
            if let font = value as? UIFont {
                let scaledFont = font.withSize(fontSize)
                mutableText.addAttribute(.font, value: scaledFont, range: range)
            }
        }
        
        let availableWidth = bounds.width - textContainerInset.left - textContainerInset.right
        let textRect = mutableText.boundingRect(
            with: CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude),
            options: [.usesLineFragmentOrigin, .usesFontLeading],
            context: nil
        )
        return textRect.height
    }
    
    private func applyFontSize(_ size: CGFloat) {
        guard let attributedText = attributedText else { return }
        let mutableText = NSMutableAttributedString(attributedString: attributedText)
        mutableText.enumerateAttribute(.font, in: NSRange(location: 0, length: mutableText.length)) { value, range, _ in
            if let font = value as? UIFont {
                let scaledFont = font.withSize(size)
                mutableText.addAttribute(.font, value: scaledFont, range: range)
            }
        }
        self.attributedText = mutableText
    }
}

使用时只需将原有的UITextView替换为AutoScalingTextView,并设置字号范围:

let consentTextView = AutoScalingTextView()
consentTextView.minFontSize = 12
consentTextView.maxFontSize = 18
// 其他原有设置保持不变...

关于UILabel vs UITextView的选择

你说得很对,UITextView更适合展示大段文本——尤其是医疗类场景,用户可能需要选中复制文本(即使你禁用了编辑,TextView默认支持选中)。UILabel虽然也能做自动缩放,但处理复杂富文本样式时,TextView的兼容性和扩展性更好;而且如果未来遇到极端小屏(比如旧款SE),TextView可以无缝开启滚动作为 fallback,体验更流畅。

最后提醒一句:方案一需要在viewDidLayoutSubviews中手动调用调整逻辑,因为视图的实际 bounds 要在布局完成后才会确定,避免初始计算错误;方案二已经在layoutSubviews中自动处理了这个问题。

备注:内容来源于stack exchange,提问作者Infantools

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.15 08:53:04