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