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

多行TextField动态字体大小调整未按预期生效

问题分析与解决方案

你的代码存在几个核心问题,导致字号未自动调整、内容滚动消失:

  • 文本尺寸计算未考虑TextField的内边距和外部frame偏移,判断条件失效
  • 仅监听文本变化,未处理视图尺寸变化(如屏幕旋转)的场景
  • lineLimit(nil)允许文本滚动,掩盖了字号调整的效果
  • 计算文本尺寸时的可用空间参数不准确

修正后的代码

@State private var fontSize: CGFloat = 16
@State private var text = ""
// 固定padding值,方便精准计算文本可用空间
private let textPadding: CGFloat = 8

var body: some View {
    GeometryReader { geometry in
        TextField("Notiz", text: $text, axis: .vertical)
            .font(.system(size: fontSize))
            .padding(textPadding)
            .background(Color.clear)
            // 计算TextField的可用frame:减去预留的20边距
            .frame(
                width: geometry.size.width - 20,
                height: geometry.size.height - 20,
                alignment: .leading
            )
            .multilineTextAlignment(.leading)
            // 设置足够大的行数限制,避免自动滚动
            .lineLimit(100)
            .disabled(false)
            .onAppear {
                // 视图初始化时计算一次字号
                adjustFontSizeToFit(in: geometry.size)
            }
            .onChange(of: text) { _ in
                adjustFontSizeToFit(in: geometry.size)
            }
            .onChange(of: geometry.size) { newSize in
                // 视图尺寸变化时重新计算字号
                adjustFontSizeToFit(in: newSize)
            }
    }
}

private func adjustFontSizeToFit(in availableSize: CGSize) {
    // 计算文本实际可用空间:TextField的frame尺寸减去上下左右padding
    let textAvailableWidth = (availableSize.width - 20) - 2 * textPadding
    let textAvailableHeight = (availableSize.height - 20) - 2 * textPadding
    // 文本最大允许的尺寸(宽度固定为可用宽度,高度不设限用于计算实际高度)
    let maxTextSize = CGSize(width: textAvailableWidth, height: .infinity)
    
    guard !text.isEmpty else {
        // 空文本时恢复默认字号
        fontSize = 16
        return
    }
    
    // 从当前字号开始调整,先尝试缩小到合适尺寸
    var currentSize = fontSize
    var textSize = calculateTextSize(for: text, fontSize: currentSize, maxSize: maxTextSize)
    
    // 缩小字号直到文本高度不超过可用高度,且不小于最小字号
    while textSize.height > textAvailableHeight && currentSize > 10 {
        currentSize -= 1
        textSize = calculateTextSize(for: text, fontSize: currentSize, maxSize: maxTextSize)
    }
    
    // 尝试增大字号到最大允许值,同时保证文本高度不超过可用高度
    while textSize.height < textAvailableHeight && currentSize < 16 {
        let testSize = currentSize + 1
        let testTextSize = calculateTextSize(for: text, fontSize: testSize, maxSize: maxTextSize)
        if testTextSize.height <= textAvailableHeight {
            currentSize = testSize
            textSize = testTextSize
        } else {
            break
        }
    }
    
    fontSize = currentSize
}

// 封装文本尺寸计算逻辑,避免重复代码
private func calculateTextSize(for text: String, fontSize: CGFloat, maxSize: CGSize) -> CGSize {
    (text as NSString).boundingRect(
        with: maxSize,
        options: [.usesLineFragmentOrigin, .usesFontLeading],
        attributes: [.font: UIFont.systemFont(ofSize: fontSize)],
        context: nil
    ).size
}

关键修改点说明

  1. 固定padding值:将.padding()改为固定的.padding(textPadding),确保文本可用空间计算精准
  2. 补充监听时机:新增onAppear和onChange(of: geometry.size)监听,覆盖视图初始化和尺寸变化的场景
  3. 修正可用空间计算:计算文本实际可用宽高时,减去TextField的内边距和外部frame偏移量
  4. 优化字号调整逻辑:封装文本尺寸计算函数,增大字号时先测试再调整,防止过度增大
  5. 限制滚动行为:将lineLimit(nil)改为.lineLimit(100),用足够大的行数限制避免文本自动滚动,确保全部内容可见

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 12:57:13