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

时间:2017-03-19 23:36:32

标签: ios core-data swift3

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

继承人代码:

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
}

}

谢谢!

1 个答案:

答案 0 :(得分:0)

我假设您在控制台中按照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委托事件,则可能会发生这些错误。您可以通过使用tableview.reloadData()替换controllerDidChangeContent的实现并删除您在controllerWillChangeContent controllerDidChange中执行的所有操作来测试是否存在问题。不幸的是,用于从fetchedResultsController更新tableview的Apple示例代码是错误的,并且不会处理所有情况。请在此处查看我的答案:App crashes after updating CoreData model that is being displayed in a UITableView以获取有关如何修复它的完整说明。但如果你很懒,只想让它工作,你可以简单地做reloadData