0

I have a fairly simple piece of code that I am trying to write containing a displayed array and a draggable object inside that array. The problem though is that it compiles and displays the array, but does not update the view when I tap and drag the object, despite the terminal showing that the tOffset variable is getting constantly updated.

I am 90% sure I am just not getting the property wrapper right, maybe @ObservedObject doesn't work for arrays? And feedback is appreciated . Here is my class declaration:

class Token: Identifiable, ObservableObject{
    var id: UUID
    var name: String
    var scale: CGFloat
    
    @Published var sOffset: CGSize
    @Published var cOffset: CGSize
    @Published var tOffset: CGSize

    init(n: String) {
        id = UUID()
        name = n
        scale = 2.0

        sOffset = .zero
        cOffset = .zero
        tOffset = .zero
        
        updateTOffset()
    }
    
    public func updateTOffset() {
        tOffset.height = sOffset.height + cOffset.height
        tOffset.width = sOffset.width + cOffset.width
        
        print("Updated T Offset , x: \(tOffset.width), y:\(tOffset.height) ")
    }
}

class varToken: Token {
    var exp: expToken?
    
    init(name: String, exp: expToken?) {
        super.init(n: name)
        
        self.exp = exp
    }
}

class expToken: Token {
    @Published var bOffset: CGSize = .zero
    
    init(name: String) {
        super.init(n: name)
        sOffset.height = -10
        scale = 1.0
        
        updateTOffset()
    }
}

class tokenArray: Identifiable, ObservableObject {
    var id: UUID = UUID()
    
    @Published var tokens: [varToken] = [varToken(name: "1 +", exp: nil), varToken(name: "x", exp: expToken(name: "2"))]

}

I created it intending for it to conform to Observable Object. The view is very simple, with all elements in one view struct:

struct ViewDemo2: View {
    @ObservedObject var t: tokenArray = tokenArray()
    
    var body: some View {
        HStack(alignment: .bottom) {
            ForEach(t.tokens) { token in
                Text(token.name)
                    .scaleEffect(token.scale, anchor: .center)
                    .offset(token.tOffset)
                    .padding(.leading, 10)
                if token.exp != nil {
                    Text(token.exp!.name)
                            .scaleEffect(token.exp!.scale)
                            .offset(token.exp!.tOffset)
                            .gesture(
                                DragGesture()
                                    .onChanged() { value in
                                        withAnimation(.spring()) {
                                            token.exp!.cOffset = value.translation
                                            token.exp!.updateTOffset()
                                        }
                                    }
                                    .onEnded() { value in
                                        withAnimation(.spring()) {
                                            token.exp!.cOffset = .zero
                                            token.exp!.updateTOffset()
                                        }
                                    }
                            )
                }
            }
        }
    }
}
  • No, nested ObservableObjects don't work unless you explicitly call `objectWillChange()` on the parent. Instead of doing this, make `Token` a `struct`. `t` can just be `t: [Token]` -- no need for the multiple levels of ObservableObjects – jnpdx Sep 16 '22 at 21:05
  • Ok, so I tried creating token as a struct, and once initialized in a [var] array, trying to change the values within the struct are throwing errors that the struct object is a let instance... I tried removing those multiple levels of Observable Objects, still doesn't update but at least it doesn't throw errors. Considering that I plan on constantly mutating the values, shouldn't I leave it as a class? – Simeon Shaffar Sep 21 '22 at 21:22
  • No — you’re probably misusing `@State` somehow. Hard to tell how without seeing the code. But, in general, in SwiftUI, models should be structs. Check out the Apple and Hacking With Swift tutorials. – jnpdx Sep 21 '22 at 23:25

0 Answers0