Nig*_*l-W 4 ios swift swift-data swiftui
我是 SwiftUI 的新手,对于我的第一个应用程序,我决定尝试 SwiftData,因为我不想稍后将其转换为 SwiftData。我的应用程序有计时器(MyTimer),每个计时器都有一个名称和几个重置,每个重置都有一个日期和一个文本字符串。我希望用户能够选择 MyTimer 并查看其重置列表,然后从那里能够编辑或删除其中的任何一个。因此,我的第一个视图允许创建 MyTimer 并向其添加重置。第二个视图显示所选 MyTimer 的重置列表。
我遇到的问题是,当我删除多个重置时,重置数组似乎没有更新,如果您从列表的同一行删除第二个重置,它会尝试删除已删除的重置,并且我收到错误:线程 1:致命错误:此方法不准备对脱离其托管对象上下文的支持数据进行操作。关系需要通过 objectID 进行深度复制才能发挥作用。
这是应用程序数据模型文件的代码:
import Foundation
import SwiftData
import SwiftUI
@Model
final class MyTimer {
var id: String
var name: String
@Relationship(deleteRule: .cascade)
var resets: [Reset]
@Transient
var sortedResets: [Reset] {
print(resets.count)
return self.resets.sorted { $0.date > $1.date }
}
init(_ name: String = "",
startDate: Date = .now) {
let id = UUID().uuidString
self.id = id
self.name = name
self.resets = [Reset(text: "Started On", date: startDate)]
}
}
@Model
class Reset: Identifiable {
var id: String
var text: String
var date: Date
init(text: String, date: Date) {
self.id = UUID().uuidString
self.text = text
self.date = date
}
}
Run Code Online (Sandbox Code Playgroud)
应用程序文件:
import SwiftUI
import SwiftData
@main
struct SwiftDataHabitTestApp: App {
let container = try? ModelContainer(for: MyTimer.self, Reset.self)
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(container!)
}
}
Run Code Online (Sandbox Code Playgroud)
内容查看:
import SwiftUI
import SwiftData
struct ContentView: View {
@Environment(\.modelContext) private var modelContext
@State var timerName: String = ""
@State var resetViewShowing = false
@State var timerForResetView: MyTimer?
@Query(sort: \MyTimer.name) var allTimers: [MyTimer]
var body: some View {
ZStack {
VStack {
TextField(text: $timerName, prompt: Text("Name your Timer")) {
Text("Name:")
}
Button {
let newTimer = MyTimer(timerName)
modelContext.insert(newTimer)
} label: {
Text("Create Timer")
}
List {
ForEach(allTimers){ dispTimer in
Text(dispTimer.name)
Button {
let newReset = Reset(text: "New REset", date: .now)
dispTimer.resets.append(newReset)
try? modelContext.save()
} label: {
Text("Reset")
}
Button {
resetViewShowing = true
timerForResetView = dispTimer
} label: {
Text("View Resets")
}
}
.onDelete(perform: { indexSet in
indexSet.forEach { index in
modelContext.delete(allTimers[index])
}
})
}
}
.padding()
if resetViewShowing {
ResetListView(dispTimer: timerForResetView!)
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
重置列表视图
import SwiftUI
import SwiftData
struct ResetListView: View {
@Environment(\.modelContext) private var modelContext
@State var dispTimer: MyTimer
var body: some View {
// var resetList: [Reset] = dispTimer.resets.sorted { $0.date > $1.date }
List {
ForEach(dispTimer.resets) {
Text($0.text)
}
.onDelete(perform: { indexSet in
indexSet.forEach { index in
print("index: \(index)")
print("length of resets: \(dispTimer.resets.count)")
modelContext.delete(dispTimer.resets[index])
do {
try modelContext.save() <<---- Error: Thread 1: Fatal error: This method isn't prepared to operate on a backing data that's unmoored from its managed object context. Relationships will need to be deep copied by objectID for that to work.
} catch {
print("error saving")
}
}
})
}
}
}
Run Code Online (Sandbox Code Playgroud)
我尝试了几种使用重置排序数组的方法,因为我希望它按日期顺序显示在列表中,将它们排序到视图中的 var 中或在 MyTimer 中使用计算属性,但随后出现不同的错误:Thread 1: EXC_BREAKPOINT (code=1, subcode=0x1a8a4fad8)出现在重置日期属性的吸气剂中我也尝试过使用 .swipeActions 而不是使用 .ondelete ,但这似乎并不能解决问题。
错误消息中有对您的问题的提示 -
此方法不准备对脱离其托管对象上下文的支持数据进行操作。关系需要通过 objectID 进行深度复制才能正常工作
那么,我们如何获得那个“真实”的对象呢?
您可以使用它persistentIdentifier从托管对象上下文中获取它。
请注意,除了删除对象外,您还需要显式地将其从对象的resets数组中删除MyTimer。
将您的delete代码更改为:
for index in IndexSet {
let objectId = timer.resets[index].persistentModelID
let reset = modelContext.model(for: objectId)
modelContext.delete(reset)
}
timer.resets.remove(atOffsets: offsets)
do {
try modelContext.save()
} catch {
print("Error saving context \(error)")
}
Run Code Online (Sandbox Code Playgroud)
会解决你的问题。
| 归档时间: |
|
| 查看次数: |
701 次 |
| 最近记录: |