删除或插入核心数据后表视图未加载

Posted

技术标签:

【中文标题】删除或插入核心数据后表视图未加载【英文标题】:Table View not loading after deleting or inserting core data 【发布时间】:2017-03-19 23:36:31 【问题描述】:

我正在使用核心数据将事件存储到我正在制作的议程应用程序中。我有一个段控制器,允许应用程序通过日期或标签进行排序。当我在表格视图中添加或删除事件时,视图中没有任何变化并且段栏中断,没有任何反应。但是,当我重新启动应用程序时,所有元素都已排序,段栏工作并且所有元素都在那里。它只是当我添加或删除它破坏的元素时。

代码如下:

import UIKit
import CoreData

class MainVC: UIViewController, UITableViewDelegate,         UITableViewDataSource, NSFetchedResultsControllerDelegate 

    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var segControl: UISegmentedControl!

    var fetchedResultsController: NSFetchedResultsController<Event>!

    override func viewDidLoad() 
        super.viewDidLoad()
        tableView.delegate = self
        tableView.dataSource = self
        attemptFetchRequest()
    

    override func viewWillAppear(_ animated: Bool) 
        super.viewWillAppear(animated)
        attemptFetchRequest()
        tableView.reloadData()
    

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
        if let cell = tableView.dequeueReusableCell(withIdentifier: "eventCell", for: indexPath) as? EventCell 
            configureCell(cell: cell, indexPath: indexPath as NSIndexPath)
            return cell
        
        return EventCell()
    

    //calling configure cell in this VC too

    func configureCell(cell: EventCell, indexPath: NSIndexPath) 
        let event = fetchedResultsController.object(at: indexPath as IndexPath)
        cell.configureCell(event: event)
    

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) 
        if let events = fetchedResultsController.fetchedObjects , events.count > 0 
            let event = events[indexPath.row]
            performSegue(withIdentifier: "DetailsVC", sender: event)
        
    

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) 
        if segue.identifier == "DetailsVC" 
            if let dest = segue.destination as? DetailsVC 
                if let event = sender as? Event 
                    dest.eventToEdit = event
                
            
        
    

    func numberOfSections(in tableView: UITableView) -> Int 
        if let sections = fetchedResultsController.sections 
            return sections.count
        
        return 0
    

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        if let sections = fetchedResultsController.sections 
            let sectionInfo = sections[section]
            return sectionInfo.numberOfObjects
        
        return 0
    

    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? 
        if let sections = fetchedResultsController.sections 
            let currentSection = sections[section]
            return currentSection.name
        
    return nil
    

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat 
        return 126
    

    func attemptFetchRequest() 
        let fetchRequest: NSFetchRequest<Event> = Event.fetchRequest()
        let dateSort = NSSortDescriptor(key: "date", ascending: false) //sort by date
        let tagSort = NSSortDescriptor(key: "tag", ascending: false) //sort by tag
        var key: String!
        if segControl.selectedSegmentIndex == 0 
            key = "date"
            fetchRequest.sortDescriptors = [dateSort]
         else 
            key = "tag"
            fetchRequest.sortDescriptors = [tagSort]
        
        let controller = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: key, cacheName: nil)
        controller.delegate = self
        self.fetchedResultsController = controller
        do 
            try fetchedResultsController.performFetch()
         catch 
            let err = error as NSError
            print("\(err)")
        
        tableView.reloadData()
    

    func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) 
        //tableView.beginUpdates()
    

    func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) 
        tableView.reloadData()
        //tableView.endUpdates()
    

    func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) 
        switch(type) 
        case.insert:
            if let indexPath = newIndexPath 
                tableView.insertRows(at: [indexPath], with: .fade)
            
            break
        case.delete:
            if let indexPath = indexPath 
                tableView.deleteRows(at: [indexPath], with: .fade)
            
            break
        case.update:
            if let indexPath = newIndexPath 
                if let cell = tableView.cellForRow(at: indexPath) as? EventCell 
                    configureCell(cell: cell, indexPath: indexPath as NSIndexPath)
                
            
            break
        case.move:
            if let indexPath = indexPath 
                tableView.deleteRows(at: [indexPath], with: .fade)
            
            if let indexPath = newIndexPath 
                tableView.insertRows(at: [indexPath], with: .fade)
            
            break
        
    

    @IBAction func segControllerChanged(_ sender: Any) 
        attemptFetchRequest()
        tableView.reloadData()
    

这是我的另一个 VC

import UIKit
import CoreData

class DetailsVC: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UITextFieldDelegate 


@IBOutlet weak var image: UIImageView!

@IBOutlet weak var titleTextField: CustomTextField!

@IBOutlet weak var locationTextField: CustomTextField!

@IBOutlet weak var DescTextField: CustomTextField!

@IBOutlet weak var datePicker: UIDatePicker!

@IBOutlet weak var tagPicker: UIPickerView!

private var _tags = ["Meeting", "Breakfast/Lunch/Dinner", "Appointment", "Other"]

var tags: [String] 
    return _tags


var eventToEdit: Event?

var imgPicker: UIImagePickerController!


override func viewDidLoad() 
    super.viewDidLoad()

    titleTextField.delegate = self
    locationTextField.delegate = self
    DescTextField.delegate = self

    tagPicker.delegate = self
    tagPicker.dataSource = self

    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "MMMM dd, YYYY H:mm a"

    if eventToEdit != nil 
        loadEventData()
    

    imgPicker = UIImagePickerController()
    imgPicker.delegate = self



    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? 
    let tag = _tags[row]
    return tag



func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int 
    return _tags.count


func numberOfComponents(in pickerView: UIPickerView) -> Int 
    return 1



func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) 
    //do something



@IBAction func saveBtnPressed(_ sender: UIButton) 

    var event: Event! //guarenteed to have Event
    let pic = Image(context: context)
    pic.image = image.image

    if eventToEdit == nil 
        event = Event(context: context)
     else 
        event = eventToEdit
    

    if let title = titleTextField.text 
        event.title = title
    

    if let location = locationTextField.text 
        event.location = location
    

    if let desc = DescTextField.text 
        event.detail = desc
    

    event.toImage = pic

    event.tag = _tags[tagPicker.selectedRow(inComponent: 0)]


    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "MMMM dd, YYYY HH:mm"
    event.fullDate = datePicker.date as NSDate?


    dateFormatter.dateFormat = "MMMM dd, YYYY"
    event.date = dateFormatter.string(from: datePicker.date)

    dateFormatter.dateFormat = "hh:mm a"
    event.time = dateFormatter.string(from: datePicker.date)

    ad.saveContext()

    _ = navigationController?.popViewController(animated: true)


func loadEventData() 

    if let event = eventToEdit 
        titleTextField.text = event.title
        locationTextField.text = event.location
        DescTextField.text = event.detail
        image.image = event.toImage?.image as? UIImage

        if let tag = event.tag 

            var i = 0
            repeat 

                let t = _tags[i]
                if t == tag 
                    tagPicker.selectRow(i, inComponent: 0, animated: false)
                

                i += 1
             while (i < _tags.count)
        
    



@IBAction func deleteBtnPressed(_ sender: UIBarButtonItem) 
    if eventToEdit != nil 
        context.delete(eventToEdit!)
        ad.saveContext()
    

    _ = navigationController?.popViewController(animated: true)



@IBAction func imgBtnPressed(_ sender: UIButton) 
    present(imgPicker, animated: true, completion: nil)


func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) 

    if let img = info[UIImagePickerControllerOriginalImage] as? UIImage 
        image.image = img
    
    imgPicker.dismiss(animated: true, completion: nil)


override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) 
    self.view.endEditing(true)


func textFieldShouldReturn(_ textField: UITextField) -> Bool 
    titleTextField.resignFirstResponder()
    locationTextField.resignFirstResponder()
    DescTextField.resignFirstResponder()

    return true

谢谢!

【问题讨论】:

您的截止日期不是 Stack Overflow 的责任。 @matt 我的错,只是紧张.. 您说“当我向表格视图添加或删除事件时”。但我没有看到发生这种情况的任何代码。我看到您已配置为在它确实发生时做出响应,但我看不出它发生的任何原因。为此,我需要见你,例如创建一个新的实体对象并保存上下文,这永远不会发生(事实上,你根本不会保存你的上下文)。 @JasonP 在您创建新问题之前,当您学会就您从那些花费空闲时间尝试帮助您的人那里获得的答案提供反馈时,我会考虑回答您未来的问题。 【参考方案1】:

我假设您在控制台中收到The number of rows contained in an existing section after the update (0) must be equal to the number of rows contained in that section before the update (0), plus or minus the number of rows inserted or deleted from that section (1 inserted, 0 deleted). 的警告。在您收到错误后,tableview 将不再进行任何更新。

当您没有正确更新 tableview 以响应 fetchedResultsController 委托事件时,可能会发生这些错误。您可以通过将controllerDidChangeContent 的实现替换为tableview.reloadData() 并删除您在controllerWillChangeContentcontrollerDidChange 中所做的一切来测试这是否是问题所在。不幸的是,Apple 用于从 fetchedResultsController 更新 tableview 的示例代码是错误的,并且不会处理所有情况。在此处查看我的答案:App crashes after updating CoreData model that is being displayed in a UITableView 以获取有关如何修复它的完整说明。但如果你很懒,只想让它工作,你可以简单地做reloadData

【讨论】:

对于“快速”修复,在我的 'controllerDidChangeContent' 中,我删除了所有内容并简单地编写了 tableview.reloadData() 并且没有实现 'controllerWillChange',但是,我的应用程序在尝试时仍然崩溃插入超过 1 个项目或删除超过 1 个项目。另外,我读了你的解决方案,我尝试在“更新”中将 indexPath 更改为 newIndexPath,但是,当我插入和删除时,我的应用程序仍然崩溃。超级困惑.. 如果删除除 reloadData 之外的所有内容都无法解决问题,那么您遇到的问题与我想的不同。控制台中是否打印了任何警告? 我编辑了我的原始帖子,您可以看到我之前对我的 controllerDidChange 和 controllerWillChange 的实现被注释掉了。当我使用 reloadData() 时,当我尝试插入、更新或删除单元格时,我得到“以 NSException 类型的未捕获异常终止”(尽管删除有时可以完美地工作,但我不确定为什么)。但是,使用我最初的 controllerDidChange 和 controllerWillChange 实现,我会得到你上面提到的错误。 尝试同时注释掉controller(_:​did​Change:​at:​for:​new​Index​Path:​) 这似乎已经解决了崩溃问题,但你能解释一下它为什么会起作用吗?这似乎是一种非常“hacky”的修复方式,您知道如何在实现所有正确功能的同时修复它吗?顺便谢谢你编辑:现在看来,即使插入和删除工作,当我点击一个单元格时,它总是向我显示第一个单元格。

以上是关于删除或插入核心数据后表视图未加载的主要内容,如果未能解决你的问题,请参考以下文章

如何从数据数组中删除对象(核心数据)

快速加载两个集合视图和一个表视图的核心数据

java连接oracle数据库,显示表或视图不存在

从 Oracle 视图插入表

核心数据未在表视图中获取

尽管表存在,但未找到 Hive 表或视图