【问题标题】:SwiftUI MVVM Binding List ItemSwiftUI MVVM 绑定列表项
【发布时间】:2021-10-11 18:02:55
【问题描述】:

我正在尝试创建这样的列表视图和详细屏幕:

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

【问题讨论】:

  • 每次使用“MyListItemDetailView”时,都会重新创建一个新的MyListViewItemDetailModel。那是你想做的吗?这可能是您问题的根源吗?
  • 当我刚刚让它工作时,我没有发现任何问题,但这可能只是为了解决所有问题,甚至让它编译跨度>
  • @workingdog 我认为没问题。因此,当我在MyListViewItemDetailModel 中更新item 时,它不适用于MyListViewMyListItemView
  • @George 你认为没有问题吗?确实,视图之间的值不同步。
  • @Yun 你能比'视图之间不同步'更清楚吗?例如NavigationLink 在目的地显示正确的东西,我可以添加新项目。 Bear in bind 我不得不编辑很多东西才能编译它,所以我可能有不同的运行代码。

标签: ios mvvm swiftui binding


【解决方案1】:

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

【讨论】:

  • 我需要编辑MyListItemDetailView中的项目,所以我创建了MyListItemDetailViewModel。你怎么看?
  • 在我的回答中,在 MyListItemDetailView 中,您可以使用例如我提供的 TextField 来编辑项目。更改将反映在视图中。你试过我的代码了吗?
  • 我的意思是我这里需要调用一些API,所以我创建了一个viewModel类。我不知道我该怎么做
  • 应该没有什么问题。您可以在 MyListItemDetailView 中调用所有 api 并更新项目。
  • 那么,您的意思是不创建视图模型?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-18
  • 2013-11-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多