Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/visual-studio-2012/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用SwiftUI和Combine进行双向绑定_Swiftui_Combine - Fatal编程技术网

使用SwiftUI和Combine进行双向绑定

使用SwiftUI和Combine进行双向绑定,swiftui,combine,Swiftui,Combine,我正在尝试解决如何在父子关系中的两个ViewModel之间正确传递一个对象或一组值,以便在更新子ViewModel时将更改气泡返回到父视图 当只使用SwiftUI视图并直接绑定到商店时,这非常简单,但我希望将用于字段验证等的业务逻辑与SwiftUI视图分开 下面的代码显示了当父对象更新时,子对象将按预期进行更新,但我需要以某种方式将子对象中更改的值传递回父对象。我对移动应用开发非常陌生,而且还在学习,所以我肯定我错过了一些非常简单的东西 import SwiftUI import Combine

我正在尝试解决如何在父子关系中的两个ViewModel之间正确传递一个对象或一组值,以便在更新子ViewModel时将更改气泡返回到父视图

当只使用SwiftUI视图并直接绑定到商店时,这非常简单,但我希望将用于字段验证等的业务逻辑与SwiftUI视图分开

下面的代码显示了当父对象更新时,子对象将按预期进行更新,但我需要以某种方式将子对象中更改的值传递回父对象。我对移动应用开发非常陌生,而且还在学习,所以我肯定我错过了一些非常简单的东西

import SwiftUI
import Combine

struct Person: Hashable {
  var givenName: String
  var familyName: String
}

// my person store - in the real app it's backed by coredata
class PersonStore: ObservableObject {
  @Published var people: [Person] = [
    Person(
      givenName: "Test",
      familyName: "Person"
    )
  ]
  static let shared = PersonStore()
}

// app entrypoint
struct PersonView: View {
  @ObservedObject var viewModel: PersonView_ViewModel = PersonView_ViewModel()

  var body: some View {
    NavigationView {
      VStack {
        List(viewModel.people.indices, id: \.self) { idx in
          NavigationLink(destination: PersonDetailView(viewModel: PersonDetailView_ViewModel(personIndex: idx))) {
            Text(self.viewModel.people[idx].givenName)
          }
        }
      }
    }
  }
}

class PersonView_ViewModel: ObservableObject {
  @Published var people: [Person] = PersonStore.shared.people
}

// this is the detail view
struct PersonDetailView: View {
  @ObservedObject var viewModel: PersonDetailView_ViewModel

  var body: some View {
    Form {
      Section(header: Text("Parent View")) {
        VStack {
          TextField("Given Name", text: self.$viewModel.person.givenName)
          Divider()
          TextField("Family Name", text: self.$viewModel.person.familyName)
        }
      }
      PersonBasicDetails(viewModel: PersonBasicDetails_ViewModel(person: viewModel.person))
    }
  }
}

// viewmodel associated with detail view
class PersonDetailView_ViewModel: ObservableObject {
  @Published var person: Person

  init(personIndex: Int) {
    self.person = PersonStore.shared.people[personIndex]
  }
}

// this is the child view - in the real app there are multiple sections which are conditionally rendered
struct PersonBasicDetails: View {
  @ObservedObject var viewModel: PersonBasicDetails_ViewModel

  var body: some View {
    Section(header: Text("Child View")) {
      VStack {
        TextField("Given Name", text: self.$viewModel.person.givenName)
        Divider()
        TextField("Family Name", text: self.$viewModel.person.familyName)
      }
    }
  }
}

class PersonBasicDetails_ViewModel: ObservableObject {
  @Published var person: Person

  init(person: Person) {
    self.person = person
  }
}

struct PersonView_Previews: PreviewProvider {
  static var previews: some View {
    PersonView()
  }
}
在web上的大多数SwiftUI文本字段示例中,绑定是通过使用@State变量来提供的,该变量为您创建绑定实例

但是,也可以使用绑定构造函数创建自定义绑定。下面是一个这样的例子:

TextField(
  "Given Name",
  text: Binding(
    get: { self.$viewModel.person.givenName },
    set: { self.$viewModel.person.givenName = $0 }))

如果您想要双向工作,不仅需要发布,还必须使用绑定向上

struct Person: Hashable {
    var givenName: String
    var familyName: String
}

// my person store - in the real app it's backed by coredata
class PersonStore: ObservableObject {
    @Published var people: [Person] = [
        Person(givenName: "Test",familyName: "Person")
    ]

    static let shared = PersonStore()
}

// app entrypoint
struct PersonView: View {
    @ObservedObject var viewModel: PersonView_ViewModel = PersonView_ViewModel()

    var body: some View {
        NavigationView {
            VStack {
                List(viewModel.people.indices, id: \.self) { idx in
                    NavigationLink(destination: PersonDetailView(viewModel: PersonDetailView_ViewModel(person: self.$viewModel.people , index: idx ))) {
                        Text(self.viewModel.people[idx].givenName)
                    }
                }
            }
        }
    }
}

class PersonView_ViewModel: ObservableObject {
    @Published var people: [Person] = PersonStore.shared.people
}

// this is the detail view
struct PersonDetailView: View {
    @ObservedObject var viewModel: PersonDetailView_ViewModel

    var body: some View {
        Form {
            Section(header: Text("Parent View")) {
                VStack {
                    TextField("Given Name", text: self.viewModel.person.givenName)
                    Divider()
                    TextField("Family Name", text: self.viewModel.person.familyName)
                }
            }
             PersonBasicDetails(viewModel: PersonBasicDetails_ViewModel(person: viewModel.person))
        }
    }
}

// viewmodel associated with detail view
class PersonDetailView_ViewModel: ObservableObject {
    @Published var person: Binding<Person>

    init(person: Binding<[Person]> ,index: Int) {
        self.person = person[index]
    }
}

// this is the child view - in the real app there are multiple sections which are conditionally rendered
struct PersonBasicDetails: View {
    @ObservedObject var viewModel: PersonBasicDetails_ViewModel

    var body: some View {
        Section(header: Text("Child View")) {
            VStack {
                TextField("Given Name", text: self.viewModel.person.givenName)
                Divider()
                TextField("Family Name", text: self.viewModel.person.familyName)
            }
        }
    }
}

class PersonBasicDetails_ViewModel: ObservableObject {
    @Published var person: Binding<Person>

    init(person: Binding<Person>) {
        self.person = person //person
    }
}

struct PersonView_Previews: PreviewProvider {
    static var previews: some View {
        PersonView()
    }
}

非常感谢你发布这篇文章。这正是我一直在到处寻找的解决方案!那么,当观察到的值在viewmodel中时,如何使用@state呢?而不是在SwiftUI导入视图类中?