Swift UI деталь удалить - PullRequest
       36

Swift UI деталь удалить

2 голосов
/ 22 января 2020

У меня есть список SwiftUI, который представляет подробный вид / толкает к навигации при касании ячейки:

import SwiftUI

struct DevicesInRangeList: View {
    @ObservedObject var central = Central()

    var body: some View {
        NavigationView {
            List(central.peripheralsInRange) { peripheral in
                NavigationLink(destination: DeviceView(peripheral: peripheral).onAppear {
                    self.central.connect(peripheral: peripheral)
                }.onDisappear {
                    self.central.disconnect(peripheral: peripheral)
                }) {
                    DeviceRow(deviceID: peripheral.deviceID, name: peripheral.name)
                }
            }.onAppear {
                self.central.scanning = true
            }.onDisappear {
                self.central.scanning = false
            }.navigationBarTitle("Devices in range")
        }
    }
}

Если я коснусь строки, отобразятся подробности. Если периферийное устройство отключается, оно удаляется из массиваfaceralsInRange, а строка удаляется, но детали все равно отображаются. Как можно удалить детали при удалении связанной строки?


Редактировать: После ответа Аспери у меня есть следующее, которое все еще не работает:

struct DevicesInRangeList: View {
    @ObservedObject var central = Central()

    @State private var localPeripherals: [Peripheral] = []

    @State private var activeDetails = false
    var body: some View {
        NavigationView {
            List(localPeripherals, id: \.self) { peripheral in
                NavigationLink(destination:
                    DeviceView(peripheral: peripheral)
                        .onReceive(self.central.$peripheralsInRange) { peripherals in
                            if !peripherals.contains(peripheral) {
                                self.activeDetails = false
                            }
                        }
                        .onAppear {
                            self.central.connect(peripheral: peripheral)
                        }
                        .onDisappear {
                            self.central.disconnect(peripheral: peripheral)
                        }
                , isActive: self.$activeDetails) {
                    DeviceRow(deviceID: peripheral.deviceID, name: peripheral.name)
                }
            }.onReceive(central.$peripheralsInRange) { peripherals in
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                    self.localPeripherals = peripherals
                }
            }.onAppear {
                self.central.scanning = true
                self.localPeripherals = self.central.peripheralsInRange
            }.onDisappear {
                self.central.scanning = false
            }.navigationBarTitle("Devices in range")
        }
    }
}

Ответы [ 2 ]

0 голосов
/ 22 января 2020

Лучший способ - проверить наличие данных перед их отображением. Я принял мастер / демо Apple, чтобы показать, как это сделать. В этом шаблонном приложении они используют @State var в качестве источника записей, но идея та же. Проверьте наличие «записи» в DETAIL VIEW.

import SwiftUI

private let dateFormatter: DateFormatter = {
    let dateFormatter = DateFormatter()
    dateFormatter.dateStyle = .medium
    dateFormatter.timeStyle = .medium
    return dateFormatter
}()

struct ContentView: View {
    @State private var dates = [Date]()

    var body: some View {
        NavigationView {
            MasterView(dates: $dates)
                .navigationBarTitle(Text("Master"))
                .navigationBarItems(
                    leading: EditButton(),
                    trailing: Button(
                        action: {
                            withAnimation { self.dates.insert(Date(), at: 0) }
                        }
                    ) {
                        Image(systemName: "plus")
                    }
                )
            DetailView(dates: $dates).navigationBarTitle(Text("Detail"))
        }.navigationViewStyle(DoubleColumnNavigationViewStyle())
    }
}

struct MasterView: View {
    @Binding var dates: [Date]

    var body: some View {
        List {
            ForEach(dates, id: \.self) { date in
                NavigationLink(
                    destination: DetailView(dates: self._dates, selectedDate: date).navigationBarTitle(Text("Detail"))
                ) {
                    Text("\(date, formatter: dateFormatter)")
                }
            }.onDelete { indices in
                indices.forEach { self.dates.remove(at: $0) }
            }
        }
    }
}

struct DetailView: View {
    @Binding var dates: [Date]
    var selectedDate: Date?

    var body: some View {
            if let selectedDate = selectedDate, dates.contains(selectedDate) {
                return Text("\(selectedDate, formatter: dateFormatter)")
            } else {
                return Text("Detail view content goes here")
            }
    }
}


struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

enter image description here

0 голосов
/ 22 января 2020

Ну ... будет немного долго, но оно того стоит ... Я воспроизвел поведение дефектов на упрощенной модели ... и вот причина проблемы

2020- 01-22 19: 53: 41.008064 + 0200 Test [5539: 983123] [TableView] Только одно предупреждение: UITableView было приказано расположить видимые ячейки и другое содержимое, не находясь в иерархии представления (представление таблицы или одно из ее суперпредставлений имеет не был добавлен в окно). Это может привести к ошибкам, заставляя представления внутри табличного представления загружаться и выполнять компоновку без точной информации (например, границ табличного представления, сбора признаков, полей макета, вставок безопасной области и т. Д. c), а также приведет к ненужным перегрузкам производительности из-за дополнительные макеты проходит. Создайте символическую точку останова в UITableViewAlertForLayoutOutsideViewHierarchy, чтобы перехватить это в отладчике и посмотреть, что вызвало это, чтобы вы могли вообще избежать этого действия, если это возможно, или отложить его, пока представление таблицы не будет добавлено в окно. Представление таблицы: <_TtC7SwiftUIP33_BFB370BA5F1BADDC9D83021565761A4925UpdateCoalescingTableView: 0x7fd095042600; baseClass = UITableView; кадр = (0 0; 375 667); clipsToBounds = YES; авторазмер = W + H; жестRecognizers =; слой =; contentOffset: {0, -116}; contentSize: {375, 400,5}; AdjustContentInset: {116, 0, 0, 0}; dataSource: <_TtGC7SwiftUIP13 $ 7fff2c6b223419ListCoreCoordinatorGVS_20SystemListDataSourceOs5Never_GOS_19SelectionManagerBoxS2 ___: 0x7fd093f62b60 >>

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *, так что он не работает, так что это только 100 * * * * * исходный код, который воспроизводит проблему (после запуска просто перейдите по любой строке и подождите 20 секунд)
// view model holding some sequence of data to be shown in List
class TestedModel: ObservableObject {
    @Published var originalRange = [1, 2, 3, 4, 5, 6, 7, 8, 9]
}

// simple detail view 
struct DetachedDetailView: View {
    let item: Int
    var body: some View {
        Text("Details of item \(item)")
    }
}

// Issue demo view
struct TestNavigationLinkDestruction_Issue: View {
    @ObservedObject var model = TestedModel()

    var body: some View {
        NavigationView {
            List(model.originalRange, id: \.self) { item in
                NavigationLink("Item \(item)", destination:
                    DetachedDetailView(item: item))
            }
        }
        .onAppear {
            // >> by this simulated async update of List while in Details
            DispatchQueue.main.asyncAfter(deadline: .now() + 20) {
                self.model.originalRange = [10, 20, 30, 40, 50, 60, 70, 80, 90]
            }
        }
    }
}

И вот решение ... идея заключается в раздельном обновлении содержимого Списка и времени создания решение необходимо закрыть детали

enter image description here

struct TestNavigationLinkDestruction_Fixed: View {
    @ObservedObject var model = TestedModel()

    @State private var selected: Int? = nil
    @State private var localStorage: [Int] = []


    var body: some View {
        NavigationView {
            // List locally stored items
            List(localStorage, id: \.self) { item in
                NavigationLink("Item \(item)", destination:
                    DetachedDetailView(item: item)
                        .onReceive(self.model.$originalRange) { items  in
                            if !items.contains(item) {
                                self.selected = nil // !!! unwind at once
                            }
                        }
                , tag:item, selection: self.$selected)
            }
            .onReceive(self.model.$originalRange) { items  in
                DispatchQueue.main.async {
                    self.localStorage = items // !!! postpone local data update
                }
            }
        }
        .onAppear {
            self.localStorage = self.model.originalRange // ! initial load from model

            // >>> simulate async data update
            DispatchQueue.main.asyncAfter(deadline: .now() + 20) {
                self.model.originalRange = [10, 20, 30, 40, 50, 60, 70, 80, 90]
            }
        }
    }
}

Итак ... все, что вам нужно, это принять выше к вашему коду, я уверен, что это возможно.

...