简体   繁体   English

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

[英].onAppear() doesn't update Shapes that use @resultBuilder

I was playing around with @resultBuilder and made a simple result builder called SumBuilder which takes a bunch of integers and adds them all up.我在玩弄@resultBuilder并制作了一个名为SumBuilder的简单结果生成器,它接受一堆整数并将它们全部相加。

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

Since I was also playing around with shapes the other day I thought about creating a custom shape which can use this new result builder in its implementation.因为前几天我也在玩弄形状,所以我想创建一个自定义形状,它可以在其实现中使用这个新的结果生成器。 The custom shape just draws a line from the origin of the container to the point specified.自定义形状只是从容器的原点到指定点画一条线。

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

When I hooked up sum to a @State variable in my ContentView it works perfectly.当我将sum连接到我的ContentView中的@State变量时,它工作得很好。 However, when I change sum using .onAppear() , the value of sum will change under the hood but the View doesn't update to reflect that:但是,当我使用.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
        }
    }
}

I tried moving the .onAppear() to the Text thinking the .onAppear() wasn't being called since the CustomShape is initially a dot, but that didn't change anything.我尝试将.onAppear()移动到Text ,认为.onAppear()没有被调用,因为CustomShape最初是一个点,但这并没有改变任何东西。 What's even stranger is that when I transform the CustomShape to a regular View , the .onAppear() works as intended.更奇怪的是,当我将CustomShape转换为常规View时, .onAppear()按预期工作。

Does anyone know how to make a Shape which utilises @resultBuilder update even when a value is changed using .onAppear() ?有谁知道如何制作一个使用@resultBuilder更新的Shape ,即使使用.onAppear()更改值也是如此? If you can help in any shape or form I'd be very grateful.如果您能以任何形式提供帮助,我将不胜感激。

Thanks in advance!提前致谢!

PS This is only the least amount of code required to recreate the problem. PS 这只是重现问题所需的最少代码。 In my real app I'm initialising a variable called endPoint to be the centre using a GeometryProxy .在我的真实应用程序中,我使用GeometryProxy将一个名为endPoint的变量初始化为中心。 That's why I can't initialise distance directly when I declare it as it would be conceptually different than what I'm actually trying to achieve.这就是为什么我不能在声明distance时直接初始化它,因为它在概念上与我实际尝试实现的不同。

As i mentioned, i just tried your code but using a ViewModel, which worked as expected.正如我所提到的,我只是尝试了您的代码,但使用的是按预期工作的 ViewModel。 In production you probably want to use input-ouput-pattern but this is the least amount of code to explain what i meant by using a 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
                }
        }
    }
}

In SwiftUI, the .onAppear() modifier is used to perform some action when a view appears on the screen.在 SwiftUI 中, .onAppear()修饰符用于在视图出现在屏幕上时执行某些操作。 However, this modifier does not trigger a re-render of the view, so any changes made within the.onAppear() closure will not be reflected in the view's appearance.但是,此修饰符不会触发视图的重新渲染,因此在 .onAppear() 闭包中所做的任何更改都不会反映在视图的外观中。

This applies to shapes that use @resultBuilder as well.这也适用于使用 @resultBuilder 的形状。 If a shape is created using @resultBuilder and you make changes to it within the .onAppear() closure, the changes will not be visible in the view.如果形状是使用 @resultBuilder 创建的,并且您在.onAppear()闭包中对其进行了更改,则更改将不会在视图中可见。 To update a shape that uses @resultBuilder you need to use .onChange() or .onReceive() instead of .onAppear() to update the shape which will trigger the view to re-render.要更新使用 @resultBuilder 的形状,您需要使用.onChange().onReceive()而不是.onAppear()来更新将触发视图重新渲染的形状。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM