如何在CoreData中存储URL与TableView单元格顺序?解决重排保存报错
你不需要更换从Bundle读取.txt文件的方式,问题出在没有把Bundle获取的URL转换成CoreData的Story实体对象,直接把[URL]赋值给[NSManagedObject]类型的数组当然会报错。下面是具体的解决步骤:
1. 完善CoreData的Story实体
首先给你的Story实体添加两个关键属性:
fileURI:URI类型,用来存储txt文件的URL(对应Bundle里的文件路径)sortOrder:Integer 32类型,用来记录每个故事的排序位置(这是实现重排保存的核心)
建议给Story生成NSManagedObject子类,这样操作起来更方便(Xcode里可以通过Editor > Create NSManagedObject Subclass...生成)。
2. 初始化:把Bundle中的文件导入CoreData
第一次启动App时,检查CoreData里是否已经有Story数据,如果没有,就把Bundle里的URL逐个转换成Story对象保存:
func initializeStoriesIfNeeded() { let context = persistentContainer.viewContext let fetchRequest: NSFetchRequest<Story> = Story.fetchRequest() do { let count = try context.count(for: fetchRequest) if count == 0 { // 从Bundle获取所有txt文件URL guard let fileURLs = Bundle.main.urls(forResourcesWithExtension: "txt", subdirectory: nil) else { return } for (index, url) in fileURLs.enumerated() { let story = Story(context: context) story.fileURI = url as NSURL story.sortOrder = Int32(index) // 初始顺序就是Bundle读取的顺序 } try context.save() } } catch { print("初始化故事数据失败:\(error)") } }
在App启动时调用这个方法(比如AppDelegate的didFinishLaunchingWithOptions或者SceneDelegate的相关方法)。
3. 从CoreData获取排序后的故事数据
之后TableView的数据源不再直接用Bundle的URL数组,而是从CoreData获取Story对象数组,并且按sortOrder排序:
var stories: [Story] = [] func fetchStories() { let context = persistentContainer.viewContext let fetchRequest: NSFetchRequest<Story> = Story.fetchRequest() // 按sortOrder升序排列 fetchRequest.sortDescriptors = [NSSortDescriptor(key: "sortOrder", ascending: true)] do { stories = try context.fetch(fetchRequest) tableView.reloadData() } catch { print("获取故事数据失败:\(error)") } }
在ViewController的viewDidLoad里调用fetchStories()来加载数据。
4. 处理单元格重排并保存新顺序
当你通过UILongPressGestureRecognizer完成重排后,更新每个Story的sortOrder属性,然后保存CoreData上下文:
// 假设你已经实现了TableView的移动逻辑,比如在tableView(_:moveRowAt:to:)方法中 override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) { // 先更新内存中的数组顺序 let movedStory = stories.remove(at: sourceIndexPath.row) stories.insert(movedStory, at: destinationIndexPath.row) // 更新每个Story的sortOrder for (index, story) in stories.enumerated() { story.sortOrder = Int32(index) } // 保存到CoreData do { try persistentContainer.viewContext.save() } catch { print("保存排序失败:\(error)") } }
5. 点击单元格打开对应txt文件
现在点击单元格时,从Story对象中取出fileURI转成URL,再读取文件内容:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let story = stories[indexPath.row] guard let fileURL = story.fileURI as URL? else { return } do { let content = try String(contentsOf: fileURL) // 这里打开显示内容的页面,比如push一个新的ViewController展示content } catch { print("读取文件失败:\(error)") } }
这样就能完美实现你的需求:初始加载Bundle中的文件标题到TableView,支持重排并保存顺序到CoreData,点击打开对应文件。
内容的提问来源于stack exchange,提问作者bobcat

