如何更新 Observable 对象中的数组元素
How to update an element of an array in an Observable Object
抱歉,如果我的问题很愚蠢,我是编程的初学者。我有一个导航 Link 到从我的视图模型数组生成的列表中的详细视图。在详细视图中,我希望能够改变点击元素的属性之一,但我似乎无法弄清楚如何做到这一点。我觉得我解释得不是很好,所以这是代码。
// 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)
}
}
在详细视图中,我正在尝试更新特定 activity 的完成计数,并让它更新我的视图模型。我上面尝试的方法可能没有意义,显然不起作用。我只是把它留下来展示我的尝试。
感谢任何帮助或见解。
您正在更改并递增 tempActivity 的值,因此它不会影响主数组或数据源。
您可以在视图模型中添加一个更新函数并从视图中调用。
视图模型负责此更新。
class ActivityViewModel: ObservableObject {
@Published var activities: [Activity] = []
func updateCompletionCount(for id: UUID) {
if let index = activities.firstIndex(where: {[=10=].id == id}) {
self.activities[index].completions += 1
}
}
}
struct ActivityDetailView: View {
var activity: Activity
var viewModel: ActivityViewModel
var body: some View {
VStack {
Text("Number of times completed: \(activity.completions)")
Button("Increment completion count"){
updateCompletionCount()
}
Text("\(activity.completeDescription)")
}
}
func updateCompletionCount() {
self.viewModel.updateCompletionCount(for: activity.id)
}
}
如果没有进一步的操作,则不需要@State 或@ObservedObject 来查看详细信息。
问题出在这里:
struct ActivityDetailView: View {
@State var activity: Activity
...
这需要是 @Binding
才能将更改反映回父视图。也没有必要传入整个 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)")
}
}
}
但是如何获得绑定呢?如果你使用 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
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)")
}
}
}
抱歉,如果我的问题很愚蠢,我是编程的初学者。我有一个导航 Link 到从我的视图模型数组生成的列表中的详细视图。在详细视图中,我希望能够改变点击元素的属性之一,但我似乎无法弄清楚如何做到这一点。我觉得我解释得不是很好,所以这是代码。
// 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)
}
}
在详细视图中,我正在尝试更新特定 activity 的完成计数,并让它更新我的视图模型。我上面尝试的方法可能没有意义,显然不起作用。我只是把它留下来展示我的尝试。
感谢任何帮助或见解。
您正在更改并递增 tempActivity 的值,因此它不会影响主数组或数据源。
您可以在视图模型中添加一个更新函数并从视图中调用。
视图模型负责此更新。
class ActivityViewModel: ObservableObject {
@Published var activities: [Activity] = []
func updateCompletionCount(for id: UUID) {
if let index = activities.firstIndex(where: {[=10=].id == id}) {
self.activities[index].completions += 1
}
}
}
struct ActivityDetailView: View {
var activity: Activity
var viewModel: ActivityViewModel
var body: some View {
VStack {
Text("Number of times completed: \(activity.completions)")
Button("Increment completion count"){
updateCompletionCount()
}
Text("\(activity.completeDescription)")
}
}
func updateCompletionCount() {
self.viewModel.updateCompletionCount(for: activity.id)
}
}
如果没有进一步的操作,则不需要@State 或@ObservedObject 来查看详细信息。
问题出在这里:
struct ActivityDetailView: View {
@State var activity: Activity
...
这需要是 @Binding
才能将更改反映回父视图。也没有必要传入整个 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)")
}
}
}
但是如何获得绑定呢?如果你使用 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
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)")
}
}
}