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