I have a study project also you can call it testing/ learning project! Which has a goal to recreate the data source and trying get sinked with latest updates to data source!
In this project I was successful to create the same data source without sending or showing data source to the Custom View! like this example project in down, so I want keep the duplicated created data source with original data source updated!
For example: I am deleting an element in original data source and I am trying read the id of the View that get disappeared! for deleting the same one in duplicated one! but it does not work well!
The Goal: As you can see in my project and my codes! I want create and keep the duplicated data source sinked and equal to original without sending the data source directly to custom View! or even sending any kind of notification! the goal is that original data source should put zero work to make itself known to duplicated data source and the all work is belong to custom View to figure out the deleted item.
PS: please do not ask about use case or something like that! I am experimenting this method to see if it would work or where it could be helpful!
struct ContentView: View {
@State private var array: [Int] = Array(0...3)
var body: some View {
VStack(alignment: .leading, spacing: 10.0) {
ForEach(array.indices, id:\.self) { index in
CustomView(id: index) {
HStack {
Text(String(describing: array[index]))
.frame(width: 50.0)
Image(systemName: "trash")
.foregroundColor(.red)
.onTapGesture { array.remove(at: index) }
}
}
}
}
.font(Font.body.bold())
}
}
struct CustomView<Content: View>: View {
let id: Int
let content: () -> Content
@StateObject private var customModel: CustomModel = CustomModel.shared
var body: some View {
content()
.preference(key: IntPreferenceKey.self, value: id)
.onPreferenceChange(IntPreferenceKey.self) { newValue in
if !customModel.array.contains(newValue) { customModel.array.append(newValue) }
}
.onDisappear(perform: {
print("id:", id, "is going get removed!")
customModel.array.remove(at: id)
})
}
}
class CustomModel: ObservableObject {
static let shared: CustomModel = CustomModel()
@Published var array: Array<Int> = Array<Int>() {
didSet {
print(array.sorted())
}
}
}
struct IntPreferenceKey: PreferenceKey {
static var defaultValue: Int { get { return Int() } }
static func reduce(value: inout Int, nextValue: () -> Int) { value = nextValue() }
}
CodePudding user response:
Relying on array indexes for ForEach
will be unreliable for this sort of work, since the ID you're using is self
-- ie the index
of the item. This will result in unreliable recalculations of the items in ForEach
since they're not actually identifiable by their index. For example, if item 0 gets removed, then what was item 1 now becomes item 0, making using an index as the identifier relatively useless.
Instead, use actual unique IDs to describe your models and everything works as expected:
struct Item: Identifiable {
let id = UUID()
var label : Int
}
struct ContentView: View {
@State private var array: [Item] = [.init(label: 0),.init(label: 1),.init(label: 2),.init(label: 3)]
var body: some View {
VStack(alignment: .leading, spacing: 10.0) {
ForEach(array) { item in
CustomView(id: item.id) {
HStack {
Text("\(item.label) - \(item.id)")
.frame(width: 50.0)
Image(systemName: "trash")
.foregroundColor(.red)
.onTapGesture { array.removeAll(where: { $0.id == item.id }) }
}
}
}
}
.font(.body.bold())
}
}
struct CustomView<Content: View>: View {
let id: UUID
let content: () -> Content
@StateObject private var customModel: CustomModel = CustomModel.shared
var body: some View {
content()
.preference(key: UUIDPreferenceKey.self, value: id)
.onPreferenceChange(UUIDPreferenceKey.self) { newValue in
if !customModel.array.contains(where: { $0 == id }) {
customModel.array.append(id)
}
}
.onDisappear(perform: {
print("id:", id, "is going to get removed!")
customModel.array.removeAll(where: { $0 == id })
})
}
}
class CustomModel: ObservableObject {
static let shared: CustomModel = CustomModel()
@Published var array: Array<UUID> = Array<UUID>() {
didSet {
print(array)
}
}
}
struct UUIDPreferenceKey: PreferenceKey {
static var defaultValue: UUID { get { return UUID() } }
static func reduce(value: inout UUID, nextValue: () -> UUID) { value = nextValue() }
}