SwiftUI: How to iterate over an array of bindable objects?

后端 未结 4 996
一生所求
一生所求 2021-02-05 14:51

I\'m trying to learn SwiftUI, and how bindings work.

I have this code that works, that shows a list of projects. When one project is tapped, a binding to that project is

4条回答
  •  误落风尘
    2021-02-05 15:31

    Note: I use Xcode 11.2, @ObjectBinding is obsoleted in it (so you need to update to verify below code).

    I asked about model, because it might matter for approach. For similar functionality I preferred Combine's ObservableObject, so model is reference not value types.

    Here is the approach, which I tune for your scenario. It is not exactly as you requested, because ForEach requires some sequence, but you try to feed it with unsupported type.

    Anyway you may consider below just as alternate (and it is w/o indexes). It is complete module so you can paste it in Xcode 11.2 and test in preview. Hope it would be helpful somehow.

    Preview:

    simple iteration preview

    Solution:

    import SwiftUI
    import Combine
    
    class Project: ObservableObject, Identifiable {
        var id: String = UUID().uuidString
        @Published var title: String = ""
    
        init (title: String) {
            self.title = title
        }
    }
    
    class AppState: ObservableObject {
        @Published var projects: [Project] = []
        init(_ projects: [Project]) {
            self.projects = projects
        }
    }
    
    struct ProjectView: View {
      @ObservedObject var project: Project
      @State var projectName: String = ""
    
      var body: some View {
        VStack {
          Text(project.title)
          TextField("Change project name",
            text: $projectName,
            onCommit: {
              self.project.title = self.projectName
              self.projectName = ""
          })
          .padding()
        }
      }
    }
    
    struct ContentView: View {
        @ObservedObject var state: AppState = AppState([Project(title: "1"), Project(title: "2")])
        @State private var refreshed = false
    
        var body: some View {
            NavigationView {
                List {
                    ForEach(state.projects) { project in
                      NavigationLink(destination: ProjectView(project: project)) {
                        // !!!  existance of .refreshed state property somewhere in ViewBuilder
                        //      is important to inavidate view, so below is just a demo
                        Text("Named: \(self.refreshed ? project.title : project.title)")
                      }
                      .onReceive(project.$title) { _ in
                            self.refreshed.toggle()
                        }
                    }
                }
                .navigationBarTitle("Projects")
                .navigationBarItems(trailing: Button(action: {
                    self.state.projects.append(Project(title: "Unknown"))
                }) {
                    Text("New")
                })
            }
        }
    }
    
    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            ContentView()
        }
    }
    

提交回复
热议问题