swift .onAppear()不更新使用@resultBuilder的形状

3gtaxfhh  于 2023-01-25  发布在  Swift
关注(0)|答案(2)|浏览(131)

我在玩@resultBuilder,做了一个简单的结果构建器,叫做SumBuilder,它接受一堆整数,然后把它们加起来。

@resultBuilder
struct SumBuilder {
    static func buildBlock(_ components: Int...) -> Int {
        components.reduce(0, +)
    }
}

因为前几天我也在玩形状,所以我想创建一个自定义形状,它可以在实现中使用这个新的结果生成器。自定义形状只是从容器的原点到指定的点画一条线。

struct CustomShape: Shape {
    @SumBuilder let sum: () -> Int
    func path(in rect: CGRect) -> Path {
        Path { path in
            path.move(to: .zero)
            path.addLine(to: CGPoint(x: sum(), y: sum()))
        }
    }
}

当我把sum连接到ContentView中的@State变量时,它运行得很好,但是当我用.onAppear()修改sum时,sum的值会在幕后发生变化,但是View并没有相应地更新:

struct ContentView: View {
    @State private var distance = 0
    var body: some View {
        VStack {
            Button("Increase distance") {
                distance += 100
            }
            
            CustomShape { distance }
                .stroke()
                .onAppear { distance = 100 }  // << Updates distance, but the View still doesn't show
                                              // the line until I press the Button to make distance = 200
        }
    }
}

我试着把.onAppear()移到Text,以为.onAppear()没有被调用,因为CustomShape最初是一个点,但这没有改变任何东西,更奇怪的是,当我把CustomShape转换成一个普通的View时,.onAppear()按预期工作。
有没有人知道如何制作一个Shape,即使使用.onAppear()改变了一个值,也能使用@resultBuilder更新?如果你能以任何形式提供帮助,我将非常感激。
先谢了!

mxg2im7a

mxg2im7a1#

正如我提到的,我只是尝试了你的代码,但使用了一个ViewModel,它的工作与预期的一样。在生产中,你可能想使用输入-输出-模式,但这是最少的代码量来解释我使用ViewModel的意思。

extension ContentView {
    final class ViewModel: ObservableObject {
        @Published var distance = 0
    }
    
}
struct ContentView: View {
    @StateObject var viewModel = ViewModel()
    var body: some View {
        VStack {
            Button("Increase distance") {
                viewModel.distance += 100
            }
            
            CustomShape { viewModel.distance }
                .stroke()
                .onAppear {
                    viewModel.distance = 100
                }
        }
    }
}
6ie5vjzr

6ie5vjzr2#

在SwiftUI中,.onAppear()修饰符用于在视图出现在屏幕上时执行某些操作。但是,此修饰符不会触发视图的重新呈现,因此在.onAppear()闭包中所做的任何更改都不会反映在视图的外观中。
这也适用于使用@resultBuilder的形状。如果形状是使用@resultBuilder创建的,并且您在.onAppear()闭包中对其进行了更改,则这些更改在视图中不可见。要更新使用@resultBuilder的形状,您需要使用.onChange().onReceive()而不是.onAppear()来更新形状,这将触发视图重新呈现。

相关问题