Actualizar los datos después de seleccionar elemento de la lista: SwiftUI

vfxdev:

Acabo de pasar dos días tratando de resolver esto, espero que alguien por ahí me puede ayudar. Bastante seguro de algo tan básico debe ser factible y me falta algún concepto básico ...

Utilizando XCode 11.4 en macOS , pero sospecho que esto es aplicable a iOS también.

He reducido la fuente al mínimo para ilustrar el problema.

Tengo una lista de elementos en una vista de detalle principal, donde el elemento de la lista se obtiene de una fuente y una vez que el usuario hace clic en uno de ellos, los datos se obtienen de una segunda fuente y se muestran a la derecha vista lateral en detalle.

Resultado Esperado

Cuando hago clic en un elemento, el cuerpo del elemento se muestra en la vista detallada de la derecha.

Resultado actual

  1. Cuando hago clic en el primer elemento, el cuerpo correcta se muestra a la derecha. Pero el ItemDetailStore.load () método se llama dos veces!

  2. Cuando hago clic en el segundo y tercer elemento, la vista de detalle se mantiene sin cambios.

Fuente

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.")
    }
}

Y en el que AppDelegate inyectar el medio ambiente:

let contentView = ContentView().environmentObject(ItemDetailStore())

El culpable parece ser el modificador onAppear en ItemDetail, ya que sólo se llama cuando se hace clic en el primer elemento de la lista. No debería la vista se actualiza cada vez que cambia selectedItem?

Cualquier comentario es muy apreciada.

Chris:

mira esto:

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())
    }
}

Supongo que te gusta

Origin http://10.200.1.11:23101/article/api/json?id=400040&siteId=1
Recomendado
Clasificación