SwiftUI: обновление данных после выбора элемента в списке - PullRequest
0 голосов
/ 03 апреля 2020

Я просто потратил два дня, пытаясь понять это, надеюсь, кто-то там может мне помочь. Я уверен, что этот базис c должен быть выполнимым, и мне не хватает некоторой концепции базис c ...

Использование XCode 11.4 на macOS , но Я подозреваю, что это применимо и к iOS.

Я сократил источник до минимума, чтобы проиллюстрировать проблему.

У меня есть список элементов в главный подробный вид, где список элементов выбирается из одного источника, и как только пользователь нажимает на один из них, подробности выбираются из второго источника и отображаются в подробном виде справа.

Ожидаемый результат

Когда я нажимаю на элемент, тело элемента отображается в подробном представлении справа.

Фактический результат

  1. Когда я нажимаю на первый элемент, справа отображается правильное тело. Но метод ItemDetailStore.load () вызывается дважды!

  2. Когда я нажимаю на второй и третий элемент, подробный вид остается неизменным.

Source

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

Кажется, что виновником является модификатор onAppear в ItemDetail, так как он вызывается только тогда, когда первый элемент в список нажимается. Разве представление не должно обновляться каждый раз, когда selectedItem изменяется?

Любая обратная связь высоко ценится.

1 Ответ

1 голос
/ 03 апреля 2020

проверить это:

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())
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...