SwiftUI如何让所有视图统一使用最长文本所需的最小缩放因子?
统一HStack中所有Text的缩放比例
要实现所有Text使用最长文本的缩放比例,你需要先让系统计算出最长文本所需的缩放因子,再将该因子同步应用到所有文本上。以下是两种可行的实现方案:
方案一:基于文本原始宽度计算缩放比例
通过测量所有文本的原始宽度,结合HStack的可用空间,计算出统一的缩放比例:
import SwiftUI // 用于传递文本原始宽度的PreferenceKey struct TextWidthPreferenceKey: PreferenceKey { static var defaultValue: [CGFloat] = [] static func reduce(value: inout [CGFloat], nextValue: () -> [CGFloat]) { value.append(contentsOf: nextValue()) } } // 测量文本原始宽度的Modifier struct MeasureTextWidth: ViewModifier { func body(content: Content) -> some View { content .background( GeometryReader { proxy in Color.clear .preference(key: TextWidthPreferenceKey.self, value: [proxy.size.width]) } ) } } struct UniformScaledTextHStack: View { let texts = ["Hello", "Different length string", "Third String"] let hStackSpacing: CGFloat = 8 let baseFont = Font.title let minScale: CGFloat = 0.5 @State private var maxOriginalTextWidth: CGFloat = 0 @State private var uniformScale: CGFloat = 1.0 var body: some View { HStack(spacing: hStackSpacing) { ForEach(texts, id: \.self) { text in Text(text) .font(baseFont) .lineLimit(1) .allowsTightening(true) .scaleEffect(uniformScale) .minimumScaleFactor(minScale) // 保底缩放比例 .frame(maxWidth: .infinity) } } // 隐藏的HStack,用于测量所有文本的原始宽度 .background( HStack(spacing: hStackSpacing) { ForEach(texts, id: \.self) { text in Text(text) .font(baseFont) .lineLimit(1) .modifier(MeasureTextWidth()) } } .hidden() ) // 获取所有文本的原始宽度,记录最大值 .onPreferenceChange(TextWidthPreferenceKey.self) { widths in maxOriginalTextWidth = widths.max() ?? 0 } // 测量HStack的总宽度,计算统一缩放比例 .overlay( GeometryReader { hStackProxy in Color.clear .onAppear { calculateScale(with: hStackProxy.size.width) } .onChange(of: hStackProxy.size.width) { newWidth in calculateScale(with: newWidth) } } ) } private func calculateScale(with totalWidth: CGFloat) { // 计算每个文本的可用宽度(扣除总间距) let totalSpacing = hStackSpacing * CGFloat(texts.count - 1) let availableWidthPerText = (totalWidth - totalSpacing) / CGFloat(texts.count) // 计算缩放比例,限制在1.0(不缩放)到minScale之间 uniformScale = min(1.0, availableWidthPerText / maxOriginalTextWidth) uniformScale = max(uniformScale, minScale) } }
方案说明:
- 用隐藏的HStack测量所有文本在原始字体大小下的宽度,找到最长文本的宽度。
- 通过GeometryReader获取HStack的总宽度,计算每个文本能分配到的可用空间。
- 基于最长文本的原始宽度和可用空间,计算出统一的缩放比例,同步应用到所有文本。
- 适配布局变化(如屏幕旋转),在宽度改变时重新计算缩放比例。
方案二:参考最长文本的自动缩放比例
让最长文本自动计算所需缩放比例,再将该比例同步给其他文本:
import SwiftUI // 传递缩放后文本尺寸的PreferenceKey struct ScaledTextSizePreferenceKey: PreferenceKey { static var defaultValue: CGSize = .zero static func reduce(value: inout CGSize, nextValue: () -> CGSize) { value = nextValue() } } struct UniformScaledTextHStack: View { let texts = ["Hello", "Different length string", "Third String"] let hStackSpacing: CGFloat = 8 let baseFont = Font.title let minScale: CGFloat = 0.5 @State private var uniformScale: CGFloat = 1.0 // 获取最长文本 private var longestText: String { texts.max(by: { $0.count < $1.count }) ?? "" } var body: some View { HStack(spacing: hStackSpacing) { ForEach(texts, id: \.self) { text in Text(text) .font(baseFont) .lineLimit(1) .allowsTightening(true) .scaleEffect(uniformScale) .minimumScaleFactor(minScale) .frame(maxWidth: .infinity) } } // 隐藏的最长文本,让系统自动计算缩放比例 .overlay( Text(longestText) .font(baseFont) .lineLimit(1) .allowsTightening(true) .minimumScaleFactor(minScale) .frame(maxWidth: .infinity) .hidden() // 测量缩放后的文本尺寸 .background( GeometryReader { proxy in Color.clear .preference(key: ScaledTextSizePreferenceKey.self, value: proxy.size) } ) ) // 根据最长文本的缩放前后尺寸,计算统一缩放比例 .onPreferenceChange(ScaledTextSizePreferenceKey.self) { scaledSize in guard let uiFont = UIFont(baseFont) else { return } let originalSize = longestText.size(withAttributes: [.font: uiFont]) uniformScale = scaledSize.width / originalSize.width // 确保不低于最小缩放比例 uniformScale = max(uniformScale, minScale) } } } // 辅助扩展:将SwiftUI Font转换为UIFont extension UIFont { init?(_ font: Font) { guard let descriptor = font.fontDescriptor else { return nil } self.init(descriptor: descriptor, size: descriptor.pointSize) } }
方案说明:
- 在overlay中放置隐藏的最长文本,让系统自动应用
minimumScaleFactor适配宽度。 - 测量该最长文本缩放后的宽度,结合其原始宽度计算出缩放比例。
- 将计算出的比例应用到所有文本上,实现统一缩放效果。
两种方案都能实现你的需求,可根据实际布局复杂度选择。方案二更简洁,适合文本数量不多的场景;方案一的计算逻辑更透明,便于自定义调整布局规则。
内容的提问来源于stack exchange,提问作者Iain Coleman
相关产品推荐
相关产品推荐

