swift - 使用 GeometryReader 和 Padding 时的无限循环

标签 swift swiftui geometryreader

我在 VStack 中有两个 Text 组件,我使用 GeometryReader 来匹配它们的宽度。

问题是,如果我在将 frame 设置为内容较短的 Text 后应用 padding,我会遇到无限循环。

我想了解为什么会发生这种情况。我的期望是它应该只采用较大的宽度,然后对其应用填充。


import SwiftUI

struct ContentView: View {
    @State private var containerWidth: CGFloat?

    var body: some View {
        VStack(alignment: .center) {
            Text("Shorter Text")
                .frame(width: containerWidth, alignment: .center)
                .background { Color.green }
                // this is `padding` is causing an infinite loop
                .padding(.leading, 5)

            Text("Really really long text")
                .background {
                    Color.yellow
                }
        }
        .background {
            Color.red
        }
        .readSize { size in
            containerWidth = size.width
        }
    }
}

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) {}
}

我尝试切换padding的应用程序。这可以防止宽度计算的无限递归,但不会添加填充。

最佳答案

让我们来分析一下它挂起的原因:

  1. 首先第一个文本宽度 = nil,这意味着它有自己的宽度
  2. 'readSize' 设置宽度,强制 VStack 调整大小
  3. VStack 已调整大小,因此“readSize”再次设置宽度

这就是我要做的:

struct ContentView: View {
    @State private var containerWidth: CGFloat?

    var body: some View {
        VStack {
            Text("Shorter Text")
                .readSize { storeWidth($0.width) }
                .frame(width: containerWidth)
                .background { Color.green }

            Text("Really really long text")
                .readSize { storeWidth($0.width) }
                .frame(width: containerWidth)
                .background { Color.yellow }
        }
        .background { Color.red }
    }

    func storeWidth(_ value: CGFloat) {
        containerWidth = max(value, containerWidth ?? -.infinity)
    }
}

像这样,“readSize”将执行一次,因为文本不会改变其宽度。修饰符的顺序很重要,这就是为什么将frame(width:)放在'readSize'之后

关于swift - 使用 GeometryReader 和 Padding 时的无限循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75527469/

相关文章:

swift - 许多不同的 ViewController 中的 Cell

ios - 快速获取可用 wifi 网络列表并连接到其中之一

mvvm - 将 @ObservableObject 传递给嵌套 View

swiftui - .navigation 标题总是会产生自动布局约束冲突

swift - GeometryReader 和 .frame 的对齐

swift - 如何对 CLKComplicationServerActiveComplicationsDidChangeNotification 作出 react

swift - 如何控制 SwiftUI 图像的无障碍画外音文本

swift - 如何在 SwiftUI 中的文件、编辑和查看之前将项目添加到应用程序菜单?

scroll - 如何正确地将 "cell item"从 SwiftUI LazyVGrid 传递给 .sheet?