我是SwiftUI的新手,对于我的第一个应用程序,我决定try SwiftData,因为我不想以后再把它转换成SwiftData.我的应用程序有计时器(MyTimer),每个计时器都有一个名称和几个重置,每个重置都有一个日期和一个文本字符串.我希望用户能够 Select 一个MyTimer,并查看其重置列表,然后从那里能够编辑或删除它们中的任何一个.因此,我的第一个视图允许创建MyTimer并向其添加重置.第二个视图显示所选MyTimer的重置列表.

我的问题是,当我删除多个重置时,重置数组似乎没有更新,如果从列表的同一行删除第二个重置,它会try 删除已经删除的重置,然后我得到错误:线程1:致命错误:此方法没有准备好操作脱离其托管对象上下文的备份数据.关系将需要由OBJECTID深度复制,才能发挥作用.

以下是APP数据模型文件的代码:

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

应用程序文件:

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

内容视图:

import SwiftUI
import SwiftData

struct 内容视图: 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!)
            }
        }
    }
}

重置列表视图

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

我try 了几种方法来使用重置的排序数组,我希望它按日期顺序显示在列表中,在视图中将它们排序到一个变量中,或者使用MyTimer中的计算(computed)属性,但我得到了一个不同的错误:Thread 1: EXC_BREAKPOINT (code=1, subcode=0x1a8a4fad8),它出现在Reset的Date属性的getter中,我也try 过使用.onDelete而不是使用.swpeActions,但这似乎没有解决问题.

推荐答案

错误消息中有关于您问题的提示-

此方法不准备对脱离其托管对象上下文的备份数据进行操作.关系需要由OBJECTID进行深度拷贝才能正常工作

那么,我们怎么才能得到那个"真实"的物体呢?

您可以使用它的persistentIdentifier从托管对象上下文中获取它.

请注意,除了删除对象之外,还需要显式地将其从MyTimer对象的resets数组中删除.

将您的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)")
}

会解决你的问题.

Ios相关问答推荐

如何在SwiftUI中顺时针和逆时针两个方向应用旋转效果?

SwiftData在获取值时应用程序崩溃:线程1:EXC_BREAKPOINT(代码=1,子代码=0x101e8303c)

磁盘上的Objective-C ISA指针与实例化对象时的比较

Flutter 应用程序中带有Firebase的GoogleService-Info.plist中缺少CLIENT_ID、REVERSED_CLIENT_ID和ANDROID_CLIENT-ID

Swift - 缺少函数参数在编译期间不会失败,甚至会导致不同的数据类型?

var name: String 时 print(nil) 和 print(name) 的区别? = 无

我无法使用 Swift 将文件上传到 firebase

叠加视图不在堆栈中维护自身 - SwiftUI

FileHandle 不会在 iOS 中释放内存

NSFetchedResultsController 由字符串的第一个字母创建的部分

光标未显示在 UITextView 中

如何使用 swift 显示 .svg 图像

UIScrollview 获取touch 事件

ios 崩溃 EXC_BAD_ACCESS KERN_INVALID_ADDRESS

在 iOS 7 上更改标签栏色调 colored颜色

aps-environment 始终在发展

swift 语言中的 null / nil

如何在 Swift 中将新单元格插入 UITableView

比较没有时间分量的 NSDates

UITextView 内容插图