Swift用户界面详细信息删除
我有一个SwiftUI列表,当点击单元格时,该列表显示详细视图/推送至导航:Swift用户界面详细信息删除,swift,swiftui,swiftui-list,swiftui-navigationlink,Swift,Swiftui,Swiftui List,Swiftui Navigationlink,我有一个SwiftUI列表,当点击单元格时,该列表显示详细视图/推送至导航: import SwiftUI struct DevicesInRangeList: View { @ObservedObject var central = Central() var body: some View { NavigationView { List(central.peripheralsInRange) { peripheral in
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")
}
}
}
如果我点击一行,将显示详细信息。如果外围设备断开连接,它将从外围设备量程阵列中删除,并且该行将被删除–但仍会显示详细信息。删除关联行时,如何删除详细信息
编辑: 在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")
}
}
}
嗯。。。会有点长,但值得。。。我在简化模型上复制了缺陷行为。。。这就是问题的原因 2020-01-22 19:53:41.008064+0200测试[5539:983123][TableView]警告 仅此一次:UITableView被告知布局其可见单元格和其他单元格 不在视图层次结构中的内容(表视图或 其超级视图尚未添加到窗口)。这可能会导致以下错误: 强制表视图中的视图加载并执行布局而不显示 准确信息(例如表视图边界、特征集合、布局 页边距、安全区域插图等),也会导致不必要的 由于额外的布局过程而导致的性能开销。做一个象征 要捕获的UITableViewAlertForLayoutOutsideViewHierarchy处的断点 请在调试器中执行此操作,并查看是什么导致了这种情况,以便 如果可能的话,完全避免此操作,或者将其推迟到表中 视图已添加到窗口中。表视图: 此异常会中断导航堆栈,因此详细信息视图不会自行关闭,也不会被
isActive
状态强制关闭
下面是重现问题的初始代码(一旦开始,只需浏览任意一行并等待20秒)
这里有一个解决方案。。。这个想法在列表内容的时间更新和决策时刻是分开的,需要关闭细节
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]
}
}
}
}
所以。。您只需将上述内容应用到代码中,我相信这是可行的。最好的方法是在显示数据之前检查是否存在od数据。我采用了苹果的master/demo来展示如何做到这一点。在这个模板应用程序中,他们使用@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()
}
}
你能告诉我什么是
外围设备吗?它是不是可观察对象
?它知道自己消失了吗?没有,但是可以观察到central.peripheralsInRange,它正在正确更新列表。我不知道我需要在外设上发布什么属性来指示是否应该显示它,以及在SwiftUI中如何观察它?我找到了原因和修复,这是一次有趣的冒险。请参阅更新的答案。您的假设是正确的self.activeDetails
在断开连接时设置为false,但详细信息视图不会消失。实际上,尽管设置了该视图,self.activeDetails
的值不会更改。也许它是固定到导航链接的状态,所以不可能更改?我尝试了这个,但仍然不起作用。activeDetails在更新localPeripherals之前设置为false,但它不会忽略该细节。在asyncAfter块中检查activeDetails时仍然正确。@Nick,在我更新的代码中没有activeDetails
,这种方法是新的,因为它只包含存根模型,所以经过测试并与Xcode 11.2/iOS 13.2一起工作。但实际上,我不明白有什么不同。activeDetails的设置与所选属性的设置相同。确定是!我把iPad的快照放在这里,以便于查看结果:-)你可以在横向iPhone上尝试它(在纵向上,你看不到细节,直到选中,所以它必须存在。但是在iPhone上,如果你正在查看细节(没有主控显示)并且日期被删除(通过外部事件)我不认为这会跳回主列表?这是可以做到的,为什么不?但想想,这是个好主意吗?最好告诉用户“设备已断开连接”或者诸如此类。如果你忽略细节视图,你必须在iPad、iPhone X、iPhone SE上使用不同的方法,这取决于设备方向……在这种情况下,为了提问和理解,我想回到主视图/删除细节。你想知道如何以编程方式忽略你的细节吗看法
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()
}
}