多行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 }
关键修改点说明
- 固定padding值:将
.padding()改为固定的.padding(textPadding),确保文本可用空间计算精准 - 补充监听时机:新增
onAppear和onChange(of: geometry.size)监听,覆盖视图初始化和尺寸变化的场景 - 修正可用空间计算:计算文本实际可用宽高时,减去TextField的内边距和外部frame偏移量
- 优化字号调整逻辑:封装文本尺寸计算函数,增大字号时先测试再调整,防止过度增大
- 限制滚动行为:将
lineLimit(nil)改为.lineLimit(100),用足够大的行数限制避免文本自动滚动,确保全部内容可见
内容的提问来源于stack exchange,提问作者Menslo
相关产品推荐
相关产品推荐

