2

我有一个 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")
        }
    }
}

如果我点击一行,则会显示详细信息。如果外围设备断开连接,它将从 peripheralsInRange 数组中删除,并且该行被删除——但仍会显示详细信息。删除关联行时如何删除详细信息?


编辑:在 Asperi 的回答之后,我有以下内容,但仍然无法正常工作:

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

2 回答 2

1

最好的方法是在显示之前检查是否存在 od 数据。我采用了苹果的大师/演示来展示如何做到这一点。在这个模板应用程序中,他们使用@State var 作为记录源,但想法是一样的。在详细视图中检查“记录”是否存在。

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

在此处输入图像描述

于 2020-01-22T17:00:24.583 回答
0

嗯......会有点长,但值得......我已经在简化模型上重现了缺陷行为......这是问题的原因

2020-01-22 19:53:41.008064+0200 测试[5539:983123] [TableView] 仅警告一次:UITableView 被告知在视图层次结构中布局其可见单元格和其他内容(表格视图或其之一superviews 尚未添加到窗口中)。这可能会通过强制表视图中的视图在没有准确信息的情况下加载和执行布局(例如表视图边界、特征集合、布局边距、安全区域插入等)而导致错误,并且还会由于额外的布局传递而导致不必要的性能开销. 在 UITableViewAlertForLayoutOutsideViewHierarchy 处创建一个符号断点,以便在调试器中捕获它并查看导致这种情况发生的原因,因此如果可能的话,您可以完全避免此操作,或者将其推迟到表格视图添加到窗口中。表视图:< _TtC7SwiftUIP33_BFB370BA5F1BADDC9D83021565761A4925UpdateCoalescingTableView:0x7fd095042600;基类 = UITableView; 帧 = (0 0; 375 667); clipsToBounds = YES; 自动调整大小 = W+H;手势识别器 = ; 层 = ; 内容偏移:{0,-116};内容大小:{375, 400.5};调整内容插入:{116, 0, 0, 0}; 数据源:<_TtGC7SwiftUIP13$7fff2c6b223419ListCoreCoordinatorGVS_20SystemListDataSourceOs5Never_GOS_19SelectionManagerBoxS2___:0x7fd093f62b60>>

此异常会破坏导航堆栈,因此详细信息视图不会自行关闭或按isActive状态强制关闭。

所以这里是重现问题的初始代码(一旦开始,只需导航任何行并等待 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]
            }
        }
    }
}

这是一个解决方案......这个想法是独立的,列表内容的时间更新和决策时刻是否需要关闭细节

在此处输入图像描述

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]
            }
        }
    }
}

所以..您所需要的就是在您的代码中采用上述方法,我相信这是可行的。

于 2020-01-22T15:30:32.097 回答