已发布的作品适用于单个对象,但不适用于对象数组
Published works for single object but not for array of objects
我正在尝试制作可单独移动的物体。我能够为一个对象成功地做到这一点,但是一旦我将它放入一个数组中,对象就不能再移动了。
型号:
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)
}
}
}
多图(图片不跟随拖动):
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)
}
}
}
}
单张图片(图片跟随手势):
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)
}
}
}
我希望单个项目能够自由移动。我看到坐标正在更新,但每个图像的位置都没有。
有两种 ObservableObject
类型,您感兴趣的是 Combine.ObservableObject
。它需要一个类型为 ObservableObjectPublisher
的 objectWillChange
变量,SwiftUI 正是使用它来触发新的渲染。我不确定 Foundation.ObservableObject
的用途,但它令人困惑。
@Published
创建一个 PassthroughSubject
发布者,它可以连接到其他地方的接收器,但对 SwiftUI 没有用,当然 .onReceive()
除外。
首先,免责声明:下面的代码并非复制粘贴解决方案。它的唯一目标是帮助您了解挑战。可能有更有效的方法来解决它,所以在您了解问题后花点时间考虑您的实现。
为什么视图不更新?: SocialStore
中的 @Publisher
只会在数组更改时发出更新。由于没有从数组中添加或删除任何内容,因此不会发生任何事情。此外,因为数组元素是对象(而不是值),所以当它们改变位置时,数组保持不变,因为对对象的引用保持不变。请记住:类 创建 对象 ,结构创建 值 .
我们需要一种方法来制作商店,以便在其元素中的某些内容发生变化时发出变化。在下面的示例中,您的商店将订阅其每个元素绑定。现在,所有已发布的项目更新都将转发给您的商店发布者,您将获得所需的结果。
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 = [=10=].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)
}
}
}
}
你需要实施
let objectWillChange = ObservableObjectPublisher()
在您的 ObservableObject 中 class
对于那些可能会觉得有用的人。这是对@kontiki 的回答的一种更通用的方法。
这样您就不必为不同的模型 class 类型重复自己。
import Foundation
import Combine
import SwiftUI
class ObservableArray<T>: ObservableObject {
@Published var array:[T] = []
var cancellables = [AnyCancellable]()
init(array: [T]) {
self.array = array
}
func observeChildrenChanges<K>(_ type:K.Type) throws ->ObservableArray<T> where K : ObservableObject{
let array2 = array as! [K]
array2.forEach({
let c = [=10=].objectWillChange.sink(receiveValue: { _ in self.objectWillChange.send() })
// Important: You have to keep the returned value allocated,
// otherwise the sink subscription gets cancelled
self.cancellables.append(c)
})
return self
}
}
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 {
//For observing changes to the array only.
//No need for model class(in this case Social) to conform to ObservabeObject protocol
@ObservedObject var socialObject: ObservableArray<Social> = ObservableArray(array: testData)
//For observing changes to the array and changes inside its children
//Note: The model class(in this case Social) must conform to ObservableObject protocol
@ObservedObject var socialObject: ObservableArray<Social> = try! ObservableArray(array: testData).observeChildrenChanges(Social.self)
var body: some View {
VStack {
ForEach(socialObject.array, id: \.id) { social in
Image(social.imageName)
.position(social.pos)
.gesture(social.dragGesture)
}
}
}
}
我正在尝试制作可单独移动的物体。我能够为一个对象成功地做到这一点,但是一旦我将它放入一个数组中,对象就不能再移动了。
型号:
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)
}
}
}
多图(图片不跟随拖动):
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)
}
}
}
}
单张图片(图片跟随手势):
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)
}
}
}
我希望单个项目能够自由移动。我看到坐标正在更新,但每个图像的位置都没有。
有两种 ObservableObject
类型,您感兴趣的是 Combine.ObservableObject
。它需要一个类型为 ObservableObjectPublisher
的 objectWillChange
变量,SwiftUI 正是使用它来触发新的渲染。我不确定 Foundation.ObservableObject
的用途,但它令人困惑。
@Published
创建一个 PassthroughSubject
发布者,它可以连接到其他地方的接收器,但对 SwiftUI 没有用,当然 .onReceive()
除外。
首先,免责声明:下面的代码并非复制粘贴解决方案。它的唯一目标是帮助您了解挑战。可能有更有效的方法来解决它,所以在您了解问题后花点时间考虑您的实现。
为什么视图不更新?: SocialStore
中的 @Publisher
只会在数组更改时发出更新。由于没有从数组中添加或删除任何内容,因此不会发生任何事情。此外,因为数组元素是对象(而不是值),所以当它们改变位置时,数组保持不变,因为对对象的引用保持不变。请记住:类 创建 对象 ,结构创建 值 .
我们需要一种方法来制作商店,以便在其元素中的某些内容发生变化时发出变化。在下面的示例中,您的商店将订阅其每个元素绑定。现在,所有已发布的项目更新都将转发给您的商店发布者,您将获得所需的结果。
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 = [=10=].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)
}
}
}
}
你需要实施
let objectWillChange = ObservableObjectPublisher()
在您的 ObservableObject 中 class
对于那些可能会觉得有用的人。这是对@kontiki 的回答的一种更通用的方法。
这样您就不必为不同的模型 class 类型重复自己。
import Foundation
import Combine
import SwiftUI
class ObservableArray<T>: ObservableObject {
@Published var array:[T] = []
var cancellables = [AnyCancellable]()
init(array: [T]) {
self.array = array
}
func observeChildrenChanges<K>(_ type:K.Type) throws ->ObservableArray<T> where K : ObservableObject{
let array2 = array as! [K]
array2.forEach({
let c = [=10=].objectWillChange.sink(receiveValue: { _ in self.objectWillChange.send() })
// Important: You have to keep the returned value allocated,
// otherwise the sink subscription gets cancelled
self.cancellables.append(c)
})
return self
}
}
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 {
//For observing changes to the array only.
//No need for model class(in this case Social) to conform to ObservabeObject protocol
@ObservedObject var socialObject: ObservableArray<Social> = ObservableArray(array: testData)
//For observing changes to the array and changes inside its children
//Note: The model class(in this case Social) must conform to ObservableObject protocol
@ObservedObject var socialObject: ObservableArray<Social> = try! ObservableArray(array: testData).observeChildrenChanges(Social.self)
var body: some View {
VStack {
ForEach(socialObject.array, id: \.id) { social in
Image(social.imageName)
.position(social.pos)
.gesture(social.dragGesture)
}
}
}
}