使用GeometryReader与Padding时出现无限循环的原因排查
SwiftUI VStack内Text宽度匹配引发无限循环的问题分析与解决
问题成因
你用readSize获取整个VStack的宽度(由长文本Text决定),再把这个宽度赋值给短Text的frame(width:),但把padding加在了frame之后,这就触发了无限递归:
- 短Text设置
frame为VStack宽度后,加padding会让它的总宽度变成containerWidth + 5 - 这个宽度会撑大VStack,导致
readSize更新containerWidth为新的宽度 - 新的
containerWidth又被设置给短Text的frame,再加padding后宽度再次变大,循环往复
而你调换padding和frame顺序后,padding先增加了Text的原始宽度,再设置frame为containerWidth,会让Text被拉伸,没法实现“内容有内边距且背景宽度匹配长文本”的效果。
解决方案
方案1:单独读取长文本的宽度(推荐)
不要读取整个VStack的宽度,只读取长Text的宽度,这样短Text的padding不会影响宽度计算的源头:
import SwiftUI struct ContentView: View { @State private var longTextWidth: CGFloat? var body: some View { VStack(alignment: .center) { Text("Shorter Text") .padding(.leading, 5) .frame(width: longTextWidth, alignment: .leading) .background(Color.green) Text("Really really long text") .background(Color.yellow) .readSize { size in longTextWidth = size.width } } .background(Color.red) } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } extension View { func readSize(onChange: @escaping (CGSize) -> Void) -> some View { background( GeometryReader { geometryProxy in Color.clear .preference(key: SizePreferenceKey.self, value: geometryProxy.size) } ) .onPreferenceChange(SizePreferenceKey.self, perform: onChange) } } private struct SizePreferenceKey: PreferenceKey { static var defaultValue: CGSize = .zero static func reduce(value: inout CGSize, nextValue: () -> CGSize) {} }
方案2:调整padding与frame的顺序并设置对齐方式
如果不想改动宽度读取的目标,把padding移到frame前面,同时设置frame的对齐方式为.leading,让Text在frame内部保留padding的缩进,避免撑大VStack:
Text("Shorter Text") .padding(.leading, 5) .frame(width: containerWidth, alignment: .leading) .background(Color.green)
这样短Text的内容会有左边距,背景宽度也和长Text一致,不会触发无限循环。
内容的提问来源于stack exchange,提问作者meowmeowmeow
相关产品推荐
相关产品推荐

