首页 > 解决方案 > 在 SwiftUI 的 ForEach 中保持实例的唯一性

问题描述

我有一个包含 ForEach 的视图,并且我有一个向列表添加更多项目的按钮。在每个实例中,在循环中,我都有一个预先填充有自动生成的计数器名称的 TextField。但是当我添加一个新实例时,所有以前添加的项目都将名称更改为相同的名称:

添加一个后

加 2 后,两个元素都称为 Counter 2

加 3 后,所有元素称为 Counter 3

我想要的是将计数器名称设置为第一个计数器 1、第二个计数器 2、第三个计数器 3 等。

这是我的代码:

    import SwiftUI

    struct Counter: Identifiable, Equatable {
        var id: UUID = UUID()
        var name: String = ""
        var rows: Int = 0
        var repeats: Int?
        var rowsPerRepeat: Int?
        var countRepeats: Bool = false
    }

    struct Project: Identifiable, Equatable {
       var id:UUID = UUID()
       var name:String = ""
       var counters: [Counter] = [Counter]()
    }

    class AddEditCounterViewModel : ObservableObject {
        @Published var counter : Counter
        @Published var project: Project
       
        init(counter: Counter, project: Project) {
            self.project = project
           self.counter = counter
           if self.counter.name.isEmpty {
               self.counter.name = counterNameGenerator()
           }
       }
       
        func counterNameGenerator() -> String {
            let count = project.counters.count
            return String.localizedStringWithFormat(NSLocalizedString("Counter %d", comment: "Counter name"), count)
       }
       
       func countRepeats(countRepeats : Bool) {
           if countRepeats {
               counter.countRepeats = true
               if counter.rowsPerRepeat == nil {
                   counter.rowsPerRepeat = 2
               }
           } else {
               counter.countRepeats = false
               counter.rowsPerRepeat = nil
           }
       }
    }

    struct AddEditCounterView: View {
       @ObservedObject var viewModel : AddEditCounterViewModel
       
       @State var countRepeats = false
       @State var hiddenHeight : CGFloat = 0.0
        @State var opacity = 0.0
       
       init(viewModel: AddEditCounterViewModel) {
           self.viewModel = viewModel
       }
       
       var body: some View {
           VStack(spacing: 20) {
               VStack (alignment: .leading) {
                   
                   Text("Counter Name")
                       .multilineTextAlignment(.leading)
                   TextField("", text: $viewModel.counter.name)
               }
               HStack {
                   Text("Start at")
                       .multilineTextAlignment(.leading)
                   Spacer()
                   TextField("", value: $viewModel.counter.rows, formatter: NumberFormatter())
                       .keyboardType(.numberPad)
                       .multilineTextAlignment(.center)
                       .frame(width: 70.0, height: nil, alignment: .leading)
               }.frame(maxWidth: .infinity, alignment: .leading)
               
               Toggle(isOn: $countRepeats) {
                       Text("Count sets (repeats)")
               }.onChange(of: countRepeats, perform: { value in
                   viewModel.countRepeats(countRepeats: value)
                   hiddenHeight = value ? 30.0 : 0
                   opacity = value ? 1 : 0
               })
               HStack {
                   Text("How many rows per set (repeat)")
                       .multilineTextAlignment(.leading)
                   Spacer()
                   TextField("", value: $viewModel.counter.rowsPerRepeat, formatter: NumberFormatter())
                       .keyboardType(.numberPad)
                       .multilineTextAlignment(.center)
                       .frame(width: 70.0, height: nil, alignment: .leading)
               }
               .opacity(opacity)
               .frame(maxWidth: .infinity, maxHeight: $hiddenHeight.wrappedValue, alignment: .leading)
               .animation(.easeIn)
           }
       }
    }


    class AddEditProjectViewModel: ObservableObject  {
       @Published var project : Project
       
       init(project: Project) {
           self.project = project
           if self.project.counters.count < 1 {
               addNewCounter()
           }
       }
       func addNewCounter()  {
           project.counters.append(Counter())
       }
    }

    struct AddEditProjectView: View {
       
       @ObservedObject var viewModel : AddEditProjectViewModel
       
       var body: some View {
          
           VStack {
                ForEach(viewModel.project.counters) { counter in
                    AddEditCounterView(viewModel: AddEditCounterViewModel(counter: viewModel.project.counters[0], project: viewModel.project))
                }
               Button( action: {
                   viewModel.addNewCounter()
               }){
                   Text("Add Counter")
               }
           }
       }
    }

    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            AddEditProjectView(viewModel: AddEditProjectViewModel(project: Project()))
        }
    }

标签: swiftuicombine

解决方案


推荐阅读