我正在尝试创建一个列表视图和一个详细屏幕,如下所示:
struct MyListView: View {
@StateObject var viewModel: MyListViewModel = MyListViewModel()
LazyVStack {
// https://www.swiftbysundell.com/articles/bindable-swiftui-list-elements/
ForEach(viewModel.items.identifiableIndicies) { index in
MyListItemView($viewModel.items[index])
}
}
}
class MyListViewModel: ObservableObject {
@Published var items: [Item] = []
...
}
struct MyListItemView: View {
@Binding var item: Item
var body: some View {
NavigationLink(destination: MyListItemDetailView(item: $item), label: {
...
})
}
}
struct MyListItemDetailView: View {
@Binding var item: Item
@StateObject var viewModel: MyListViewItemDetailModel
init(item: Binding<Item>) {
viewModel = MyListViewItemDetailModel(item: item)
}
var body: some View {
...
}
}
class MyListViewItemDetailModel: ObservableObject {
var item: Binding<Item>
...
}
我不确定它出了什么问题,但我发现
item
变量彼此不同步,即使在 MyListItemDetailView 和 MyListItemDetailViewModel 之间也是如此。
有没有人可以提供最佳实践并让我知道我的实施中有什么问题?
我认为你应该考虑对你的代码进行小的重组,并且只使用 1 @StateObject/ObservableObject。这是您的代码的简化版本,使用 只有一个 StateObject 事实来源:
注意:据我所知,绑定应该在 View 结构中使用,而不是在“普通”类中使用。
PS:什么是identificIndicies?
import SwiftUI
@main
struct TestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct Item: Identifiable {
let id = UUID().uuidString
var name: String = ""
}
struct MyListView: View {
@StateObject var viewModel: MyListViewModel = MyListViewModel()
var body: some View {
LazyVStack {
ForEach(viewModel.items.indices) { index in
MyListItemView(item: $viewModel.items[index])
}
}
}
}
class MyListViewModel: ObservableObject {
@Published var items: [Item] = [Item(name: "one"), Item(name: "two")]
}
struct MyListItemView: View {
@Binding var item: Item
var body: some View {
NavigationLink(destination: MyListItemDetailView(item: $item)){
Text(item.name)
}
}
}
class MyAPIModel {
func fetchItemData(completion: @escaping (Item) -> Void) {
// do your fetching here
completion(Item(name: "new data from api"))
}
}
struct MyListItemDetailView: View {
@Binding var item: Item
let myApiModel = MyAPIModel()
var body: some View {
VStack {
Button(action: fetchNewData) {
Text("Fetch new data")
}
TextField("edit item", text: $item.name).border(.red).padding()
}
}
func fetchNewData() {
myApiModel.fetchItemData() { itemData in
item = itemData
}
}
}
struct ContentView: View {
var body: some View {
NavigationView {
MyListView()
}.navigationViewStyle(.stack)
}
}
编辑1:
要设置 API 来调用某些函数,您可以使用如下内容:
class MyAPI {
func fetchItemData(completion: @escaping (Item) -> Void) {
// do your stuff
}
}
并使用它从服务器获取您需要的任何数据。
EDIT2:添加了一些代码来演示 API 的使用。
一直在寻找这个问题的答案-发布,以防它对其他人有帮助。
将模型作为可绑定传递到子视图可以解决此问题。适用于选择器、列表和文本字段:
SelectionView(settingsStore: settingsStore)
struct SelectionView: View {
@Bindable var settingsStore: SettingsStore
在视图上添加 @State var 可以很好地演示这些 UI 元素如何工作 - 但在任何实际应用程序中,您都需要模型/状态上的选定数据。