0

I make an array in ContentView and I can change the context of it when someone dragging the object. In ContentView I have an array nodes.n_array that is an array of Node objects. In ForEach(in ContentView) I try to detect dragging the object and change coordinates of it. It doesn't work.

Node class:

import Foundation
import SwiftUI

class Node: Decodable, ObservableObject {
    enum CodingKeys: CodingKey {
        case id, x, y, text, red, green, blue, shape
    }
    
    
    public let id: Int
    @Published var x: Double
    @Published var y: Double
    public let text: String
    public var red: Double
    public var green: Double
    public var blue: Double
    public var shape: Int
    
    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decode(Int.self, forKey: .id)
        x = try container.decode(Double.self, forKey: .x)
        y = try container.decode(Double.self, forKey: .y)
        text = try container.decode(String.self, forKey: .text)
        red = try container.decode(Double.self, forKey: .red)
        green = try container.decode(Double.self, forKey: .green)
        blue = try container.decode(Double.self, forKey: .blue)
        shape = try container.decode(Int.self, forKey: .shape)
    }
}
    
extension Node {
    func circle() -> some View {
        Circle()
            .fill(Color(red: red, green: green, blue: blue))
            .frame(width: 64, height: 64, alignment: .center)
            .position(x: x, y: y)
    }
    
    func square() -> some View {
        Rectangle()
            .fill(Color(red: red, green: green, blue: blue))
            .frame(width: 64, height: 64, alignment: .center)
            .position(x: x, y: y)
    }
    
    @ViewBuilder func drawShape() -> some View {
        switch shape{
        case 1: circle()
        case 2: square()
            default: Text("Failed")
        }
    }
    
    func label() -> some View {
        return Text(text)
            .font(.system(size: 30))
            .bold()
            .position(x: x, y: y)
            .foregroundColor(Color(red: 0.25, green: 0.25, blue: 0.25))
    }
}

Nodes class:

import Foundation

class Nodes: ObservableObject {
   @Published var n_array: [Node]

    init() {
        let url = Bundle.main.url(forResource: "nodes", withExtension: "json")!
        let data = try! Data(contentsOf: url)
        n_array = try! JSONDecoder().decode([Node].self, from: data)
    }
}

Content View:

import SwiftUI

struct ContentView: View {
    @ObservedObject var nodes = Nodes()
    
    var body: some View {
        
        ZStack {
            
            ForEach(0..<nodes.n_array.count) { index in
                nodes.n_array[index].drawShape()
                    .gesture(DragGesture()
                        .onChanged { value in
                            nodes.n_array[index].x = value.translation.width
                            nodes.n_array[index].y = value.translation.height
                        }
                        .onEnded { value in
                            
                        }
                    )
                nodes.n_array[index].label()
            }
        }
    }
}

1 Answer 1

0

Nested ObservableObjects don't work in SwiftUI unless you manually propagate the state back up to the parent object by calling objectWillChange.

Generally, the standard route is to use a struct for your model and then have the array managed by the ObservableObject parent.

If you change your model to the following, you'll see the drag start to update:

struct Node: Decodable {
    public let id: Int
    var x: Double
    var y: Double
    public let text: String
    public var red: Double
    public var green: Double
    public var blue: Double
    public var shape: Int
}

Note: you'll also need a little more logic for your dragging, but that's beyond the direct scope of this question -- you'll see the item jump at the beginning of the drag because you need to store the initial translation

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.