SWIFTUI:用数组 属性 创建 Class - 符合 'ObservableObject'。 Class!不是结构

SWIFTUI: Create Class with Array property - witch conform to 'ObservableObject'. Class! NOT struct

我需要创建数组 属性 of class 符合 ObservableObject 协议。因为我要将它与过滤器、排序等一起使用-将 table 中的 select 记录显示在列表视图中

class SQS_Record: NSObject, ObservableObject {
    @Published final var __idx: Int = -1 
}

class Project: SQS_Record {
    @objc dynamic var name: String = ""         { willSet { objectWillChange.send() } }
}

class SQS_Table<RecordType: SQS_Record>: ObservableObject {
    @Published var records = [RecordType]()
    var rcount: Int {
        return records.count
    }
    subscript (index: Int) -> RecordType {
        get {
            return records[index]
        }
        set(item) {
            records[index] = item
        }
    }


Let projectsTbl = SQS_Table<Project>(...)

struct ProjectsListView: View {
    // This is ERROR line - below
    @StateObject private var itemsList = projectsTbl.records.sorted { [=15=].__idx < .__idx }        
        
    var body: some View {
        NavigationView {
            List() {
                // Список
                ForEach (itemsList, id: \.__id) { (proj: Project) in
                    ProjectElement(project: proj)
                }
                .onMove(perform: moveItem )
                .onDelete(perform: delItem )
            }
    …
}

错误:通用结构 'StateObject' 要求“[Project]”符合 'ObservableObject'

如果我没看错你的问题,你的array项是类。您可以将数组声明为 @State,但只有对数组本身的更改(添加、排序、删除)才会导致重新计算 body

让我们试试:

struct ArrayOfObservableObjects: View {
    @State private var store = (1 ... 5).map(Record.init)
    var body: some View {
        NavigationView {
            List(store) { record in
                Text(record.score.description)
            }
            .toolbar {
                HStack {
                    Button("Shuffle") {
                        store.shuffle()
                    }
                    Button("Change First") {
                        store.first?.score = 666
                    }
                    Button("Add") {
                        store.insert(Record(store.count + 1), at: 0)
                    }
                }
            }
        }
    }
}

如你所见,“Change First”按钮实际上改变了一条“Record”的“score”,但是这个修改并没有显示出来。

如果要“发布”数组中每个元素的更改,则必须分几步进行:

  1. 让您的 Record 可见:
class Record: ObservableObject, Identifiable {
    let id = UUID()
    @Published var score: Int
    init(_ score: Int) {
        self.score = score
    }
}
  1. 将您的数组包装在一个 ObservableObject 中:
class RecordStore: ObservableObject {
    @Published var records: [Record] = (1 ... 5).map(Record.init)

  1. 订阅数组每个元素的变化
    private var c: AnyCancellable?
    init() {
        subscribeToChanges()
    }

    func subscribeToChanges() {
        c = records
            .publisher
            .flatMap { record in record.objectWillChange }
            .sink { [weak self] in
                self?.objectWillChange.send()
            }
    }
}

有效。但是缺少了一些东西(如果我们添加一个新元素,它将不会被观察到)。所以要完成:

class RecordStore: ObservableObject {
    @Published var records: [Record] = (1 ... 5).map(Record.init) {
        didSet {
            subscribeToChanges()    ///<<< HERE
        }
    }

    private var c: AnyCancellable?
    init() {
        subscribeToChanges()
    }

    func subscribeToChanges() {
        c = records
            .publisher
            .flatMap { record in record.objectWillChange }
            .sink { [weak self] in
                self?.objectWillChange.send()
            }
    }
}

和视图:

import Combine
import SwiftUI

struct ArrayOfObservableObjects: View {
    @StateObject var store = RecordStore()
    var body: some View {
        NavigationView {
            List(store.records) { record in
                Text(record.score.description)
            }
            .toolbar {
                HStack {
                    Button("Shuffle") {
                        store.records.shuffle()
                    }
                    Button("Change First") {
                        store.records.first?.score = 666
                    }
                    Button("Add") {
                        store.records.insert(Record(store.records.count + 1), at: 0)
                    }
                }
            }
        }
    }
}