核心数据不会触发 SwiftUI 的立即刷新

Core data not triggering immediate refresh with SwiftUI

我对这个问题一头雾水,过去几天我一直在解决这个问题。

我正在绘制自定义形状,用户可以通过拖动手势移动其中一个点。我希望在移动点时重新绘制形状。

这是一些使用 CGPoint 的示例代码。

import SwiftUI

struct ShapeTest: Shape {
    
    @State var points: [CGPoint]
    @State var closed: Bool = true
    
    func path(in rect: CGRect) -> Path {
        var path = Path()
        if (points.count > 0) {
            path.move(to: points.first!)
            path.addLines(points)
            if closed { path.closeSubpath() }
        }
        return path
    }
}

struct RedrawEdgeTestCGPoint: View {
    
    @State var points = [CGPoint]()
    @State var originalPosition: CGPoint? = nil
    
    private var movePointDragGesture: some Gesture {
        DragGesture(minimumDistance: 0, coordinateSpace: .local)
            .onChanged { value in
                if points.isEmpty {
                    return
                }
                
                if originalPosition == nil {
                    originalPosition = CGPoint(x: points.last!.x, y: points.last!.y)
                }
                
                let lastIndex = points.count - 1
                points[lastIndex].x = originalPosition!.x + value.translation.width
                points[lastIndex].y = originalPosition!.y + value.translation.height
            }
            .onEnded { value in
                originalPosition = nil
            }
    }
    
    private var addNewPointGesture: some Gesture {
        TapGesture(count: 2)
            .onEnded {
                points.append(CGPoint())
            }
    }
    
    var body: some View {
        GeometryReader { geometry in
            Group {
                ShapeTest(points: points)
                    .stroke()
            }
            .contentShape(Rectangle())
            .gesture(movePointDragGesture.simultaneously(with: addNewPointGesture))
        }
    }
}

struct RedrawEdgeTestCGPoint_Previews: PreviewProvider {
    static var previews: some View {
        RedrawEdgeTestCGPoint()
    }
}

现在,这是另一个代码示例。这个例子大体上是一样的,但这是使用我在 Core Data 中定义的 Point 实体而不是 CGPoint。

import SwiftUI

struct RedrawEdgeTestCoreData: View {
    
    @Environment(\.managedObjectContext) private var viewContext
    
    @State var points: [Point] = []
    @State var originalPosition: CGPoint? = nil
    
    private var movePointDragGesture: some Gesture {
        DragGesture(minimumDistance: 0, coordinateSpace: .local)
            .onChanged { value in
                if points.isEmpty {
                    return
                }
                
                if originalPosition == nil {
                    originalPosition = CGPoint(x: points.last!.x, y: points.last!.y)
                }
                
                let lastIndex = points.count - 1
                points[lastIndex].x = originalPosition!.x + value.translation.width
                points[lastIndex].y = originalPosition!.y + value.translation.height
            }
            .onEnded { value in
                originalPosition = nil
            }
    }
    
    private var addNewPointGesture: some Gesture {
        TapGesture(count: 2)
            .onEnded {
                points.append(Point(context: viewContext))
            }
    }
    
    var body: some View {
        GeometryReader { geometry in
            Group {
                ShapeTest(points: points.map { CGPoint(x: [=11=].x, y: [=11=].y) })
                    .stroke()
            }
            .contentShape(Rectangle())
            .gesture(movePointDragGesture.simultaneously(with: addNewPointGesture))
        }
    }
}

struct RedrawEdgeTestCoreData_Previews: PreviewProvider {
    static var previews: some View {
        RedrawEdgeTestCoreData()
            .environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
    }
}

使用核心数据的版本在拖动时不会更新。当我双击添加另一个点时它会重绘。

关于发生这种情况的原因有什么建议吗?

我使用了 jnpdx 建议的方法并且有效。

import SwiftUI

class PointsViewModel: ObservableObject {
    @Published var points = [Point]()
    
    func movePoint(index: Int, x: CGFloat, y: CGFloat) {
        points[index].x = x
        points[index].y = y
        objectWillChange.send()
    }
}

struct RedrawEdgeTestCoreData: View {
    
    @Environment(\.managedObjectContext) private var viewContext
    
    @ObservedObject var viewModel: PointsViewModel
    @State var originalPosition: CGPoint? = nil
    
    private var movePointDragGesture: some Gesture {
        DragGesture(minimumDistance: 0, coordinateSpace: .local)
            .onChanged { value in
                if viewModel.points.isEmpty {
                    return
                }
                
                if originalPosition == nil {
                    originalPosition = CGPoint(x: viewModel.points.last!.x, y: viewModel.points.last!.y)
                }
                
                let lastIndex = viewModel.points.count - 1
                viewModel.movePoint(index: lastIndex, x: originalPosition!.x + value.translation.width, y: originalPosition!.y + value.translation.height)
            }
            .onEnded { value in
                originalPosition = nil
            }
    }
    
    private var addNewPointGesture: some Gesture {
        TapGesture(count: 2)
            .onEnded {
                viewModel.points.append(Point(context: viewContext))
            }
    }
    
    var body: some View {
        GeometryReader { geometry in
            ZStack {
                ShapeTest(points: viewModel.points.map { CGPoint(x: [=10=].x, y: [=10=].y) })
                    .stroke()
            }
            .contentShape(Rectangle())
            .gesture(movePointDragGesture.simultaneously(with: addNewPointGesture))
        }
    }
}

struct RedrawEdgeTestCoreData_Previews: PreviewProvider {
    static var previews: some View {
        RedrawEdgeTestCoreData(viewModel: PointsViewModel())
            .environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
    }
}