SwiftUI MVVM 绑定列表项

问题描述 投票:0回答:2

我正在尝试创建一个列表视图和一个详细屏幕,如下所示:

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 之间也是如此。 有没有人可以提供最佳实践并让我知道我的实施中有什么问题?

ios mvvm swiftui binding
2个回答
1
投票

我认为你应该考虑对你的代码进行小的重组,并且只使用 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 的使用。


0
投票

一直在寻找这个问题的答案-发布,以防它对其他人有帮助。

将模型作为可绑定传递到子视图可以解决此问题。适用于选择器、列表和文本字段:

SelectionView(settingsStore: settingsStore)

struct SelectionView: View {
   @Bindable var settingsStore: SettingsStore

在视图上添加 @State var 可以很好地演示这些 UI 元素如何工作 - 但在任何实际应用程序中,您都需要模型/状态上的选定数据。

© www.soinside.com 2019 - 2024. All rights reserved.