swift - 如何更新可观察对象中数组的元素

标签 swift swiftui dataflow observableobject property-wrapper

抱歉,如果我的问题很愚蠢,我是编程初学者。我有一个导航链接,指向从我的 View 模型数组生成的列表中的详细 View 。在详细 View 中,我希望能够改变点击元素的属性之一,但我似乎不知道如何做到这一点。我认为我解释得不是很好,所以这是代码。

// model
struct Activity: Identifiable {
    var id = UUID()
    var name: String
    var completeDescription: String
    var completions: Int = 0
}

// view model
class ActivityViewModel: ObservableObject {
    @Published var activities: [Activity] = []
}

// view
struct ActivityView: View {
    @StateObject var viewModel = ActivityViewModel()
    @State private var showingAddEditActivityView = false
    var body: some View {
        NavigationView {
            VStack {
                List {
                    ForEach(viewModel.activities, id: \.id) {
                        activity in
                        NavigationLink(destination: ActivityDetailView(activity: activity, viewModel: self.viewModel)) {
                            HStack {
                                VStack {
                                    Text(activity.name)
                                    Text(activity.miniDescription)
                                }
                                Text("\(activity.completions)")
                            }
                        }
                    }
                }
            }
            .navigationBarItems(trailing: Button("Add new"){
                self.showingAddEditActivityView.toggle()
            })
            .navigationTitle(Text("Activity List"))
        }
        .sheet(isPresented: $showingAddEditActivityView) {
            AddEditActivityView(copyViewModel: self.viewModel)
        }
    }
}

// detail view
struct ActivityDetailView: View {
    @State var activity: Activity
    @ObservedObject var viewModel: ActivityViewModel
    
    var body: some View {
        VStack {
            Text("Number of times completed: \(activity.completions)")
            Button("Increment completion count"){
                activity.completions += 1
                updateCompletionCount()
            }
            Text("\(activity.completeDescription)")
        }
    }
    func updateCompletionCount() {
         var tempActivity = viewModel.activities.first{ activity in activity.id == self.activity.id
         }!
        tempActivity.completions += 1
    }
}

// Add new activity view (doesn't have anything to do with question)

struct AddEditActivityView: View {
    @ObservedObject var copyViewModel : ActivityViewModel
    @State private var activityName: String = ""
    @State private var description: String = ""
    var body: some View {
        VStack {
            TextField("Enter an activity", text: $activityName)
            TextField("Enter an activity description", text: $description)
            Button("Save"){
                // I want this to be outside of my view
                saveActivity()
            }
        }
    }
    func saveActivity() {
        copyViewModel.activities.append(Activity(name: self.activityName, completeDescription: self.description))
        print(copyViewModel.activities)
    }
}

在详细信息 View 中,我尝试更新该特定事件的完成计数,并让它更新我的 View 模型。我上面尝试的方法可能没有意义,而且显然行不通。我只是留下它来展示我的尝试。

感谢您的帮助或见解。

最佳答案

问题出在这里:

struct ActivityDetailView: View {
    @State var activity: Activity
    ...

这需要是一个@Binding,以便更改能够反射(reflect)回父 View 中。也无需传入整个 viewModel - 一旦您拥有 @Binding,您就可以摆脱它。

// detail view
struct ActivityDetailView: View {
    @Binding var activity: Activity /// here!
    
    var body: some View {
        VStack {
            Text("Number of times completed: \(activity.completions)")
            Button("Increment completion count"){
                activity.completions += 1
            }
            Text("\(activity.completeDescription)")
        }
    }
}

但是如何获得绑定(bind)呢?如果您使用的是 iOS 15,则可以直接循环 $viewModel.activities:

/// here!
ForEach($viewModel.activities, id: \.id) { $activity in
    NavigationLink(destination: ActivityDetailView(activity: $activity)) {
        HStack {
            VStack {
                Text(activity.name)
                Text(activity.miniDescription)
            }
            Text("\(activity.completions)")
        }
    }
}

对于 iOS 14 或更低版本,您需要改为循环索引。但它确实有效。

/// from https://stackoverflow.com/a/66944424/14351818
ForEach(Array(zip(viewModel.activities.indices, viewModel.activities)), id: \.1.id) { (index, activity)  in
    NavigationLink(destination: ActivityDetailView(activity: $viewModel.activities[index])) {
        HStack {
            VStack {
                Text(activity.name)
                Text(activity.miniDescription)
            }
            Text("\(activity.completions)")
        }
    }
}

关于swift - 如何更新可观察对象中数组的元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69136746/

相关文章:

ios - Realm 因 RLMException : object has been deleted or invalidated 崩溃

ios - SwiftUI:从初始化器返回而不初始化所有存储的属性

swift - 在 SwiftUI 中使用状态变量作为 func 的输入

ssis - 测量 SSIS 数据流的进度

video - Magento-模块VS数据流

ios - 点击时从 UILabel 获取文本

ios - 继续误解

ios - 使用 Swift 将照片上传到网络服务

swift - 在这种情况下,如何使NavigationLink工作?我可以在其中添加按钮吗?

java - 写入 Avro 文件时架构更新