SwiftUI MVVM 绑定列表项
SwiftUI MVVM Binding List Item
我正在尝试创建这样的列表视图和详细屏幕:
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 真实来源:
注意:AFAIK 绑定旨在用于视图结构而不是“普通”类。
PS:什么是 identifiableIndicies?
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.
的使用
我正在尝试创建这样的列表视图和详细屏幕:
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 真实来源:
注意:AFAIK 绑定旨在用于视图结构而不是“普通”类。
PS:什么是 identifiableIndicies?
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.
的使用