SwiftUI:选择列表中的项目后更新数据
Posted
技术标签:
【中文标题】SwiftUI:选择列表中的项目后更新数据【英文标题】:SwiftUI: Update data after item in list is selected 【发布时间】:2020-04-03 14:24:14 【问题描述】:我花了两天时间试图解决这个问题,希望有人可以帮助我。很确定这个基本的东西一定是可行的,但我缺少一些基本概念......
在 macOS 上使用 XCode 11.4,但我怀疑这也适用于 ios。
为了说明问题,我已将源代码缩减到最低限度。
我在主详细信息视图中有一个项目列表,其中项目列表是从一个来源获取的,一旦用户单击其中一个,详细信息将从第二个来源获取并显示在右侧详细视图。
预期结果
当我点击一个项目时,项目的主体会显示在右侧的详细视图中。
实际结果
当我单击第一项时,正确的正文显示在右侧。但是ItemDetailStore.load()方法被调用了两次!
当我点击第二项和第三项时,详细视图保持不变。
来源
import SwiftUI
struct ContentView: View
@State var selectedItem: Item?
var body: some View
NavigationView
ItemList(selectedItem: $selectedItem)
if selectedItem != nil
ItemDetail(selectedItem: $selectedItem)
struct ItemList: View
@Binding var selectedItem: Item?
var items = [
Item(itemId: 0, title: "Item #0", body: "Empty."),
Item(itemId: 1, title: "Item #1", body: "Empty."),
Item(itemId: 2, title: "Item #2", body: "Empty.")
]
var body: some View
List(selection: $selectedItem)
ForEach(Array(items.enumerated()), id: \.element) index, item in
ItemRow(item: item)
.listStyle(SidebarListStyle())
struct ItemRow: View
var item: Item
var body: some View
Text("\(item.title)")
.padding(12)
struct ItemDetail: View
@EnvironmentObject var itemDetailStore: ItemDetailStore
@Binding var selectedItem: Item?
var body: some View
VStack
if itemDetailStore.items.count > 0
Text(itemDetailStore.items[0].body)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.onAppear
if self.selectedItem != nil
self.itemDetailStore.load(id: self.selectedItem!.itemId)
struct Item: Codable, Hashable
var itemId: Int
var title: String
var body: String
class ItemDetailStore: ObservableObject
@Published var items = [Item]()
var itemsList = [
Item(itemId: 0, title: "Item #0", body: "This is an excellent item #0."),
Item(itemId: 1, title: "Item #1", body: "This is an excellent item #1."),
Item(itemId: 2, title: "Item #2", body: "This is an excellent item #2.")
]
func load(id: Int)
self.items = [itemsList[id]]
print("\(self.items[0].title) loaded.")
我在 AppDelegate 中注入环境:
let contentView = ContentView().environmentObject(ItemDetailStore())
罪魁祸首似乎是 ItemDetail 中的 onAppear 修饰符,因为它仅在单击列表中的第一项时才被调用。每次 selectedItem 更改时不应该更新视图吗?
非常感谢任何反馈。
【问题讨论】:
【参考方案1】:看看这个:
import SwiftUI
struct ContentView: View
@EnvironmentObject var itemDetailStore: ItemDetailStore
var body: some View
NavigationView
ItemList()
if self.itemDetailStore.selectedItem != nil
ItemDetail()
struct ItemList: View
@EnvironmentObject var itemDetailStore: ItemDetailStore
var items = [
Item(itemId: 0, title: "Item #0", body: "Empty."),
Item(itemId: 1, title: "Item #1", body: "Empty."),
Item(itemId: 2, title: "Item #2", body: "Empty.")
]
var body: some View
List(selection: self.$itemDetailStore.selectedItem)
ForEach(Array(items.enumerated()), id: \.element) index, item in
ItemRow(item: item)
.listStyle(SidebarListStyle())
struct ItemRow: View
var item: Item
var body: some View
Text("\(item.title)")
.padding(12)
struct ItemDetail: View
@EnvironmentObject var itemDetailStore: ItemDetailStore
var body: some View
VStack
if itemDetailStore.items.count > 0
Text(itemDetailStore.items[0].body)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.onAppear
struct Item: Codable, Hashable, Identifiable
var id = UUID().uuidString
var itemId: Int
var title: String
var body: String
class ItemDetailStore: ObservableObject
@Published var items = [Item]()
@Published var selectedItem: Item?
didSet
if self.selectedItem != nil
self.load(id: self.selectedItem!.itemId)
var itemsList = [
Item(itemId: 0, title: "Item #0", body: "This is an excellent item #0."),
Item(itemId: 1, title: "Item #1", body: "This is an excellent item #1."),
Item(itemId: 2, title: "Item #2", body: "This is an excellent item #2.")
]
func load(id: Int)
self.items = [itemsList[id]]
print("\(self.items[0].title) loaded.")
struct ContentView_Previews: PreviewProvider
static var previews: some View
ContentView().environmentObject(ItemDetailStore())
【讨论】:
太棒了!您已将 selectedItem 移动到 ItemDetailStore 并摆脱了所有绑定。这是一个巨大的帮助,谢谢! 太好了,所以我们需要一个全局 ViewModel 来处理我们的列表、详细信息视图以及我们可能想要更改项目的任何进一步的编辑视图?以上是关于SwiftUI:选择列表中的项目后更新数据的主要内容,如果未能解决你的问题,请参考以下文章