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.
For those who might find it helpful. This is a more generic approach to @kontiki 's answer.
This way you will not have to be repeating yourself for different model class types.
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
inSocialStore
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.
You need to implement
in your ObservableObject class
There are two
ObservableObject
types and the one that you are interested in isCombine.ObservableObject
. It requires anobjectWillChange
variable of typeObservableObjectPublisher
and it is this that SwiftUI uses to trigger a new rendering. I am not sure whatFoundation.ObservableObject
is used for but it is confusing.@Published
creates aPassthroughSubject
publisher that can be connected to a sink somewhere else but which isn't useful to SwiftUI, except for.onReceive()
of course.