SwiftUI如何实现多个Text视图动态调整字号时保持大小一致
原因说明
这是SwiftUI的默认布局逻辑,不属于系统Bug。每个Text视图会独立计算自身内容适配可用空间所需的缩放比例,仅保证自身内容可以完整展示,不会主动和同容器内的其他Text做字号同步,因此文本内容变化后容易出现三个文本字号不一致的情况。
解决方法
方案1:iOS 16及以上版本
直接给整个VStack添加uniformTypeScaling()修饰符,强制容器内所有文本使用统一的缩放比例即可:
VStack(alignment: .leading) { Text(text) Spacer(minLength: 20) Text(text) Spacer(minLength: 20) Text(text) Spacer(minLength: 20) } .lineLimit(7) .font(.system(size: 100)) .minimumScaleFactor(0.01) // 新增下面这行 .uniformTypeScaling()
方案2:兼容iOS 15及更早版本
通过PreferenceKey监听所有文本的实际适配字号,取最小值统一应用到所有文本上即可:
- 先定义用于传递最小字号的PreferenceKey
struct MinFontSizePreferenceKey: PreferenceKey { static var defaultValue: CGFloat = 100 static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { value = min(value, nextValue()) } }
- 改造原有页面代码,同步字号
struct ContentView: View { @State private var text = "Tap the screen!" @State private var syncFontSize: CGFloat = 100 private let maxFontSize: CGFloat = 100 private let minScaleFactor: CGFloat = 0.01 var body: some View { HStack { VStack(alignment: .leading) { Text(text) .background( GeometryReader { proxy in // 计算当前Text适配后的实际字号 let availableWidth = proxy.size.width let actualFontSize = calculateFitFontSize(for: text, width: availableWidth, maxSize: maxFontSize) Color.clear.preference(key: MinFontSizePreferenceKey.self, value: actualFontSize) } ) .font(.system(size: syncFontSize)) Spacer(minLength: 20) Text(text) .background( GeometryReader { proxy in let availableWidth = proxy.size.width let actualFontSize = calculateFitFontSize(for: text, width: availableWidth, maxSize: maxFontSize) Color.clear.preference(key: MinFontSizePreferenceKey.self, value: actualFontSize) } ) .font(.system(size: syncFontSize)) Spacer(minLength: 20) Text(text) .background( GeometryReader { proxy in let availableWidth = proxy.size.width let actualFontSize = calculateFitFontSize(for: text, width: availableWidth, maxSize: maxFontSize) Color.clear.preference(key: MinFontSizePreferenceKey.self, value: actualFontSize) } ) .font(.system(size: syncFontSize)) Spacer(minLength: 20) } .lineLimit(7) .minimumScaleFactor(minScaleFactor) .onPreferenceChange(MinFontSizePreferenceKey.self) { minSize in syncFontSize = max(minSize, maxFontSize * minScaleFactor) } Spacer(minLength: 0) } .padding() .contentShape(Rectangle()) .frame(width: 1600/2.5, height: 900/2.5) .onTapGesture { generateNewText() } } // 计算文本适配的最大字号 private func calculateFitFontSize(for text: String, width: CGFloat, maxSize: CGFloat) -> CGFloat { var fontSize = maxSize let font = UIFont.systemFont(ofSize: fontSize) let constraintSize = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude) while fontSize > 1 { let textSize = (text as NSString).boundingRect( with: constraintSize, options: .usesLineFragmentOrigin, attributes: [.font: UIFont.systemFont(ofSize: fontSize)], context: nil ) if textSize.height <= 7 * font.lineHeight { // 7为你设置的lineLimit return fontSize } fontSize -= 1 } return 1 } private func generateNewText() { var verse = "" for _ in 0..<Int.random(in: 20...50) { verse+="example " } text = verse } }
临时快速修复
如果暂时不想大改代码,可以给所有Text添加相同的layoutPriority(1)修饰符,强制布局系统给三个文本分配相同的布局优先级,大部分场景下可以缓解字号不一致的问题:
Text(text) .layoutPriority(1)
内容的提问来源于stack exchange,提问作者Denny L.
相关产品推荐
相关产品推荐

