Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Published works for single object but not for array of objects

I am trying to make individually moveable objects. I am able to successfully do it for one object but once I place it into an array, the objects are not able to move anymore.

Model:

class SocialStore: ObservableObject {
    @Published var socials : [Social]

    init(socials: [Social]){
        self.socials = socials
    }
}

class Social : ObservableObject{
    var id: Int
    var imageName: String
    var companyName: String
    @Published var pos: CGPoint

    init(id: Int, imageName: String, companyName: String, pos: CGPoint) {
        self.id = id
        self.imageName = imageName
        self.companyName = companyName
        self.pos = pos
    }

    var dragGesture : some Gesture {
        DragGesture()
            .onChanged { value in
                self.pos = value.location
                print(self.pos)
        }
    }
}

Multiple image (images not following drag):

struct ContentView : View {
    @ObservedObject var socialObject: SocialStore = SocialStore(socials: testData)

    @ObservedObject var images: Social = testData[2]

    var body: some View {
        VStack {
            ForEach(socialObject.socials, id: \.id) { social in
                Image(social.imageName)
                    .position(social.pos)
                    .gesture(social.dragGesture)
            }
        }
    }
}

Single image (image follow gesture):

struct ContentView : View {
    @ObservedObject var socialObject: SocialStore = SocialStore(socials: testData)

    @ObservedObject var images: Social = testData[2]

    var body: some View {
        VStack {
            Image(images.imageName)
                .position(images.pos)
                .gesture(images.dragGesture)
        }
    }
}

I expect the individual items to be able to move freely . I see that the coordinates are updating but the position of each image is not.

like image 381
sfung3 Avatar asked Aug 01 '19 04:08

sfung3


People also ask

How do you check if an object exists in an array of objects?

Using includes() Method: If array contains an object/element can be determined by using includes() method. This method returns true if the array contains the object/element else return false. Example: html.

How do you merge an array of objects into a single object?

assign() method to convert an array of objects to a single object. This merges each object into a single resultant object. The Object. assign() method also merges the properties of one or more objects into a single object.

How do you check if a value exists in an array of objects in JavaScript?

You can use the includes() method in JavaScript to check if an item exists in an array. You can also use it to check if a substring exists within a string. It returns true if the item is found in the array/string and false if the item doesn't exist.

Is array and object same in JavaScript?

Arrays are Objects Arrays are a special type of objects. The typeof operator in JavaScript returns "object" for arrays.


1 Answers

First, a disclaimer: The code below is not meant as a copy-and-paste solution. Its only goal is to help you understand the challenge. There may be more efficient ways of resolving it, so take your time to think of your implementation once you understand the problem.


Why the view does not update?: The @Publisher in SocialStore will only emit an update when the array changes. Since nothing is being added or removed from the array, nothing will happen. Additionally, because the array elements are objects (and not values), when they do change their position, the array remains unaltered, because the reference to the objects remains the same. Remember: Classes create objects, Structs create values.

We need a way of making the store, to emit a change when something in its element changes. In the example below, your store will subscribe to each of its elements bindings. Now, all published updates from your items, will be relayed to your store publisher, and you will obtain the desired result.

import SwiftUI
import Combine

class SocialStore: ObservableObject {
    @Published var socials : [Social]
    var cancellables = [AnyCancellable]()

    init(socials: [Social]){
        self.socials = socials

        self.socials.forEach({
            let c = $0.objectWillChange.sink(receiveValue: { self.objectWillChange.send() })

            // Important: You have to keep the returned value allocated,
            // otherwise the sink subscription gets cancelled
            self.cancellables.append(c)
        })
    }
}

class Social : ObservableObject{
    var id: Int
    var imageName: String
    var companyName: String

    @Published var pos: CGPoint

    init(id: Int, imageName: String, companyName: String, pos: CGPoint) {
        self.id = id
        self.imageName = imageName
        self.companyName = companyName
        self.pos = pos
    }

    var dragGesture : some Gesture {
        DragGesture()
            .onChanged { value in
                self.pos = value.location
                print(self.pos)
        }
    }
}

struct ContentView : View {
    @ObservedObject var socialObject: SocialStore = SocialStore(socials: testData)

    var body: some View {
        VStack {
            ForEach(socialObject.socials, id: \.id) { social in
                Image(social.imageName)
                    .position(social.pos)
                    .gesture(social.dragGesture)
            }
        }
    }
}
like image 111
kontiki Avatar answered Sep 24 '22 00:09

kontiki