SwiftUI:选择列表中的项目后更新数据

Posted

技术标签:

【中文标题】SwiftUI:选择列表中的项目后更新数据【英文标题】:SwiftUI: Update data after item in list is selected 【发布时间】:2020-04-03 14:24:14 【问题描述】:

我花了两天时间试图解决这个问题,希望有人可以帮助我。很确定这个基本的东西一定是可行的,但我缺少一些基本概念......

ma​​cOS 上使用 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:选择列表中的项目后更新数据的主要内容,如果未能解决你的问题,请参考以下文章

Swiftui 列表更新问题

在其他屏幕中更新绑定后,SwiftUI 列表行未刷新

更新列表中的一行 (SwiftUI)

在 SwiftUI 中从数据模型中搜索数组

使用 RxSwift 在网络请求后更新 SwiftUI 列表

属性更改后在 SwiftUI 中更新列表