当成员具有 @State 修饰符时,不会呈现 swiftui 中的 PieChart

PieChart in swiftui is not rendered when a member has @State modifier

我正在创建一个应用程序并一起学习 swift/swiftui。我的应用程序使用带有 ForEach 循环的 Path 对象显示饼图。 当我将 _percentages 成员从普通成员更改为 @State 成员时,我基于带有路径的 ForEach 循环的饼图没有出现。

class 成员:

    @State var data: [Float]
    @State var colors: [Color]
    @Binding var slicePressedNo: Int
    var angles: [Float] = []
    var offsets: [Float] = []
    var total: Float = 0
    private var _sortedData: [Float]
    @State private var _percentages: [Float] = [] // Removing @State fixes the issue and everything works as expected

初始化函数:

init (data: [Float], colors: [Color], slicePressedNo: Binding<Int>) {
        self._data = State(initialValue: data)
        self._colors = State(initialValue: colors)
        self._slicePressedNo = slicePressedNo
        self._sortedData = data

        var sum: Float = 0.0
        for value in self.data {
            sum += value
        }

        self._percentages.removeAll()
        for value in self.data {
            self._percentages.append((value / sum))
            print("sum = \(sum)")
            print("value = \(value)")
        }

        angles.removeAll()
        offsets.removeAll()
        offsets.append(0)
        for value in self._percentages {
            let angle = value * 360
            angles.append(angle)
            offsets.append(angle + offsets.last!)
        }

        for i in self._percentages.indices {
            self._percentages[i] = self._percentages[i] * 100.0
            print("% = \(self._percentages[i])")
        }

        total = sum
    }

负责构建饼图切片的代码:

GeometryReader { geometry in
                var offset: Float = 0.0

                let width: CGFloat = min(geometry.size.width, geometry.size.height)
                let height = width
                let center = CGPoint(x: width * 0.5, y: height * 0.5)


                ForEach (angles.indices, id: \.self) { i in
                    withAnimation(.easeIn) {
                        Path { path in
                            path.move(to: center)
                            path.addArc(
                                center: center,
                                radius: width * 0.4,
                                startAngle: Angle(degrees:  Double(offsets[i])),
                                endAngle: Angle(degrees: Double(offsets[i] + angles[i])),
                                clockwise: false)
//                            print("i = \(angles[i])")
                            offset += angles[i]
                        }
                    }
                    .fill(slicePressedNo != i ? colors[i] : .yellow)
                }
...

_percantages 成员用在 class 但低于 PieChart 构建部分:

ForEach (angles.indices, id: \.self) { i in
                    if (_percentages[i] >= 2) {
                        PieChartSliceValueView(label: /*String(format: "%.2f",*/ $_percentages[i], x: center.x, y: center.y, angle: Double(offsets[i] + (angles[i] / 2.0)), radius: width * 0.32)
                    }
                }

一切正常,直到我将 _percentages(或其他成员)从普通成员更改为@State 成员。如果我更改它,ForEach 循环将不起作用 - 没有任何触发来呈现它?或者更改使 angles table 为空?可能我在@State 变量理解中遗漏了一些东西。

提前致谢 茹卡斯

首先不要在变量名中使用_。您只需要他们访问基础值,例如在 init.

其次,我对你提到的 class 感到困惑,因为我在你的代码中没有看到任何内容。

我会将 PieView 放在一个额外的视图中,该视图只会执行以下操作:根据移交的数据显示饼图。
此数据和颜色不必在视图中声明。只有可以在视图内更改的内容必须是 @State。您的 slicePressedNo 可能必须是 @Binding,因为您会在点击视图中检测到它。所有其他的都可以是简单的 vars 甚至 lets。

因此 PieView 可能如下所示:

struct PieView: View {
    
    var data: [Float]
    var colors: [Color]
    @Binding var slicePressedNo: Int
    
    private var angles: [Float] = []
    private var offsets: [Float] = []
    var total: Float = 0
    private var sortedData: [Float]
    private var percentages: [Float] = []
    
    init (data: [Float], colors: [Color], slicePressedNo: Binding<Int>) {
        self.data = data
        self.colors = colors
        self._slicePressedNo = slicePressedNo // only _ here
        self.sortedData = data
        
        // ... your code following

然后 调用 视图可以使用@State 来更改值,它向下传递给 PieView。一旦这些值发生变化,PieView 将被重新绘制。

这是一个在按下按钮时添加新值的简单示例:

struct ContentView: View {
    
    @State private var pressed = 1
    @State private var data: [Float] = [1, 5, 3, 8]
    @State private var colors: [Color] = [.blue, .red, .green, .orange, .teal]
    
    var body: some View {
        VStack {
            PieView(data: data,
                    colors: colors,
                    slicePressedNo: $pressed)
            
            Button("Add slice") {
                colors.append(colors.randomElement() ?? .red)
                data.append(Float.random(in: 0..<15))
            }
        }
    }
}

结果如下: