SwiftUI:如何迭代可绑定(bind)对象的数组?

标签 swift binding swiftui

我正在尝试学习 SwiftUI 以及绑定(bind)的工作原理。

我有这段有效的代码,它显示了项目列表。当点击一个项目时,对该项目的绑定(bind)将传递到 subview :

struct ProjectsView: View {
  @ObjectBinding var state: AppState
  @State var projectName: String = ""

  var body: some View {
    NavigationView {
      List {
        ForEach(0..<state.projects.count) { index in
          NavigationLink(destination: ProjectView(project: self.$state.projects[index])) {
            Text(self.state.projects[index].title)
          }
        }
      }
      .navigationBarTitle("Projects")
    }
  }
}

subview ,我在其中使用绑定(bind)改变项目:

struct ProjectView: View {
  @Binding var project: Project
  @State var projectName: String = ""

  var body: some View {
    VStack {
      Text(project.title)
      TextField(
        $projectName,
        placeholder: Text("Change project name"),
        onCommit: {
          self.project.title = self.projectName
          self.projectName = ""
      })
      .padding()
    }
  }
}

但是,我宁愿在不使用索引的情况下迭代项目数组(因为我想学习,并且它更易于阅读),但不确定如何将绑定(bind)传递到单个项目。我尝试了这样的操作,但是我无法访问 project.title,因为它是一个绑定(bind),而不是一个字符串。

ForEach($state.projects) { project in
  NavigationLink(destination: ProjectView(project: project)) {
    Text(project.title)
  }
}

我怎样才能实现这个目标?

最佳答案

注意:我使用的是 Xcode 11.2,其中 @ObjectBinding 已过时(因此您需要更新以验证以下代码)。

我询问了模型,因为它可能对方法很重要。对于类似的功能,我更喜欢合并的 ObservableObject,因此模型是引用而不是值类型。

这是我根据您的场景进行调整的方法。它不完全按照您的要求,因为 ForEach 需要一些序列,但您尝试使用不支持的类型提供它。

无论如何,您可以考虑将下面的内容作为替代(并且它没有索引)。它是完整的模块,因此您可以将其粘贴到 Xcode 11.2 中并在预览中进行测试。希望它能有所帮助。

预览:

simple iteration preview

解决方案:

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()
    }
}

关于SwiftUI:如何迭代可绑定(bind)对象的数组?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57161893/

相关文章:

ios - ObservableObject 不更新 View

swift - 在 SwiftUI View 中创建一个拖动 handle (用于可拖动窗口等)

uikit - 如何找到 SwiftUI UIViewRepresentable 的框架

ios - 在一定时间后重复翻转 x 动画

ios - 正确配置闭包以捕获结果

ios - UIImage 3快速变换

c# - 将 ListView 与来自 ViewModel 的 List 绑定(bind)

mvvm - 根据不同的数据绑定(bind)到自定义构建的控件

swift - 在 Swift 中创建运行时已知类的实例

css - 如何对 backgroundImage URL 进行 knockout 绑定(bind)?