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

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

ma​​cOS 上使用 XCode 11.4,但我怀疑这也适用于 iOS。

为了说明问题,我已将源代码缩减到最低限度。

我在主详细信息视图中有一个项目列表,其中项目列表是从一个来源获取的,一旦用户单击其中一个,详细信息将从第二个来源获取并显示在右侧详细视图。

预期结果

当我点击一个项目时,项目的主体会显示在右侧的详细视图中。

实际结果

  1. 当我单击第一项时,正确的正文显示在右侧。但是ItemDetailStore.load()方法被调用了两次!

  2. 当我点击第二项和第三项时,详细视图保持不变。

来源

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 更改时不应该更新视图吗?

非常感谢任何反馈。

【问题讨论】:

    标签: swift list macos swiftui selection


    【解决方案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 来处理我们的列表、详细信息视图以及我们可能想要更改项目的任何进一步的编辑视图?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-17
    • 2021-08-01
    • 1970-01-01
    相关资源
    最近更新 更多