在表格视图中滑动以删除时,出现以下错误:
CoreData:错误:NSFetchedResultsController:无法在-performFetch之前访问获取的对象: 无法在-performFetch之前访问获取的对象: (空)
我已经尝试了堆栈溢出搜索和Google搜索中的几种解决方案,但是找不到解决我问题的方法。
下面是我的代码:
//
// ViewController.swift
// CalendarPrototype
//
// Created by Don Oleksa on 5/27/18.
// Copyright © 2018 Don Oleksa. All rights reserved.
//
import UIKit
import CVCalendar
import CoreData
class CalendarViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, NSFetchedResultsControllerDelegate, CVCalendarViewDelegate, CVCalendarMenuViewDelegate, CVCalendarViewAppearanceDelegate {
@IBOutlet weak var menuView: CVCalendarMenuView!
@IBOutlet weak var calendarView: CVCalendarView!
@IBOutlet weak var monthLabel: UILabel!
@IBOutlet weak var tableView: UITableView!
// MARK: - Properties
var events: [EventsMO] = []
var event: [Event] = []
var fetchResultController: NSFetchedResultsController<EventsMO>!
var dateHold = ""
var nameHold = ""
var dates = [String:String]()
var dateSelected = ""
var currentDate = ""
var convertedDate = ""
var displayDateTime = ""
var displayName = ""
var todaysDate = Foundation.Date()
private var currentCalendar: Calendar?
private var animationFinished = true
override func awakeFromNib() {
let timeZoneBias = 480 // (UTC+08:00)
currentCalendar = Calendar(identifier: .gregorian)
currentCalendar?.locale = Locale(identifier: "fr_FR")
if let timeZone = TimeZone(secondsFromGMT: -timeZoneBias * 60) {
currentCalendar?.timeZone = timeZone
}
}
override func viewWillAppear(_ animated: Bool) {
// Get dates from Core Data for dotMarker
event = getJobData()
self.tableView.reloadData()
dotDates()
calendarView.contentController.refreshPresentedMonth()
}
override func viewDidLoad() {
super.viewDidLoad()
// Appearance delegate [Unnecessary]
calendarView.calendarAppearanceDelegate = self
// Animator delegate [Unnecessary]
calendarView.animatorDelegate = self
// Menu delegate [Required]
menuView.menuViewDelegate = self
// Calendar delegate [Required]
calendarView.calendarDelegate = self
if let currentCalendar = currentCalendar {
monthLabel.text = CVDate(date: Date(), calendar: currentCalendar).globalDescription
}
tableView.delegate = self
tableView.dataSource = self
// Hide Navigation Bar
self.navigationController?.isNavigationBarHidden = true
let conversion = Convert()
currentDate = conversion.convertCurrentDate(date: todaysDate)
dotDates()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
menuView.commitMenuViewUpdate()
calendarView.commitCalendarViewUpdate()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// Sets the View
func presentationMode() -> CalendarMode{
return .monthView
}
// Sets the Day the Week Starts On
func firstWeekday() -> Weekday{
return .sunday
}
// Sets the Appearance of the Calendar View
func dayOfWeekTextColor(by weekday: Weekday) -> UIColor {
return .white
}
func dayOfWeekFont() -> UIFont {
return UIFont.systemFont(ofSize: 14)
}
func dayLabelWeekdayFont() -> UIFont {
return UIFont.systemFont(ofSize: 16)
}
func dayLabelBackgroundColor(by weekDay: Weekday, status: CVStatus, present: CVPresent) -> UIColor? {
return UIColor(red:0.84, green:0.84, blue:0.84, alpha:1.0)
}
func dayLabelColor(by weekDay: Weekday, status: CVStatus, present: CVPresent) -> UIColor? {
return UIColor(red:0.04, green:0.33, blue:0.57, alpha:1.0)
}
// *****************************************************************************************************
// Prepare the Table View
func numberOfSections(in tableView: UITableView) -> Int {
if event.count > 0 {
tableView.backgroundView?.isHidden = true
tableView.separatorStyle = .singleLine
} else {
tableView.backgroundView?.isHidden = false
tableView.separatorStyle = .none
}
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return event.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "Cell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! EventTableViewCell
// Configure the cell...
cell.jobsLabel.text = event[indexPath.row].job
cell.dateTimeLabel.text = event[indexPath.row].dateTime
return cell
}
// *****************************************************************************************************
// Change Month
// Changes to Previous Month
@IBAction func loadPrevious(sender: AnyObject) {
event = getJobData()
self.tableView.reloadData()
dotDates()
calendarView.loadPreviousView()
}
// Changes to Next Month
@IBAction func loadNext(sender: AnyObject) {
event = getJobData()
self.tableView.reloadData()
dotDates()
calendarView.loadNextView()
}
// Changes to Today's Date
@IBAction func todayMonthView() {
event = getJobData()
self.tableView.reloadData()
dotDates()
calendarView.toggleCurrentDayView()
}
// Annimtes and changes the month label when the month changes
func presentedDateUpdated(_ date: CVDate) {
if monthLabel.text != date.globalDescription && self.animationFinished {
let updatedMonthLabel = UILabel()
updatedMonthLabel.textColor = monthLabel.textColor
updatedMonthLabel.font = monthLabel.font
updatedMonthLabel.textAlignment = .center
updatedMonthLabel.text = date.globalDescription
updatedMonthLabel.sizeToFit()
updatedMonthLabel.alpha = 0
updatedMonthLabel.center = self.monthLabel.center
let offset = CGFloat(48)
updatedMonthLabel.transform = CGAffineTransform(translationX: 0, y: offset)
updatedMonthLabel.transform = CGAffineTransform(scaleX: 1, y: 0.1)
UIView.animate(withDuration: 0.35, delay: 0, options: UIViewAnimationOptions.curveEaseIn, animations: {
self.animationFinished = false
self.monthLabel.transform = CGAffineTransform(translationX: 0, y: -offset)
self.monthLabel.transform = CGAffineTransform(scaleX: 1, y: 0.1)
self.monthLabel.alpha = 0
updatedMonthLabel.alpha = 1
updatedMonthLabel.transform = CGAffineTransform.identity
}) { _ in
self.animationFinished = true
self.monthLabel.frame = updatedMonthLabel.frame
self.monthLabel.text = updatedMonthLabel.text
self.monthLabel.transform = CGAffineTransform.identity
self.monthLabel.alpha = 1
updatedMonthLabel.removeFromSuperview()
}
self.view.insertSubview(updatedMonthLabel, aboveSubview: self.monthLabel)
}
}
// Shows next month
func didShowNextMonthView(_ date: Date) {
guard let currentCalendar = currentCalendar else { return }
let components = Manager.componentsForDate(date, calendar: currentCalendar) // from today
print("Showing Month: \(components.month!)")
}
// Shows previous month
func didShowPreviousMonthView(_ date: Date) {
guard let currentCalendar = currentCalendar else { return }
let components = Manager.componentsForDate(date, calendar: currentCalendar) // from today
print("Showing Month: \(components.month!)")
}
// *****************************************************************************************************
// Setup dotMarker
func dotMarker(colorOnDayView dayView: DayView) -> [UIColor] {
return [.red]
}
func dotMarker(sizeOnDayView dayView: DayView) -> CGFloat {
return 5
}
// Function to Retrieve Dates from Core Data
func dotDates() {
let fetchRequest: NSFetchRequest<EventsMO> = EventsMO.fetchRequest()
let sortDescriptor = NSSortDescriptor(key: "dateTime", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
if let appDelegate = (UIApplication.shared.delegate as? AppDelegate) {
let context = appDelegate.persistentContainer.viewContext
fetchResultController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
fetchResultController.delegate = self
do {
let result = try context.fetch(fetchRequest)
// Retrieve Dates
for data in result as [NSManagedObject] {
dateHold = data.value(forKey: "dateTime") as! String
nameHold = data.value(forKey: "job") as! String
// Date Conversion to CVDate
let conversion = Convert()
dateHold = conversion.convertStringToDate(date: dateHold)
// Store Dates in a Dictionary
dates[dateHold] = nameHold
}
} catch {
print(error)
}
}
}
// Display dots for date events
func dotMarker(shouldShowOnDayView dayView: DayView) -> Bool{
// Look up date in dictionary
if(dates[dayView.date.commonDescription] != nil){
return true // date is in the array so draw a dot
}
return false
}
// *****************************************************************************************************
func didSelectDayView(_ dayView: DayView, animationDidFinish: Bool) {
dateSelected = dayView.date.commonDescription
if dateSelected != currentDate {
event = getJobData()
self.tableView.reloadData()
} else {
event = getJobData()
}
}
func getJobData() -> [Event] {
var tempEvents: [Event] = []
let conversion = Convert()
convertedDate = conversion.convertDate(date: dateSelected)
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Events")
request.returnsObjectsAsFaults = false
request.predicate = NSPredicate(format: "dateTime CONTAINS[cd] %@", convertedDate)
do {
let results = try context.fetch(request)
if results.count != 0 {
for result in results {
displayDateTime = (result as AnyObject).value(forKey: "dateTime") as! String
displayName = (result as AnyObject).value(forKey: "job") as! String
let stripDate = String(displayDateTime.dropFirst(10))
displayDateTime = stripDate
let jobs = Event(job: displayName, dateTime: displayDateTime)
tempEvents.append(jobs)
}
} else {
displayName = "None"
displayDateTime = " "
let jobs = Event(job: displayName, dateTime: displayDateTime)
tempEvents.append(jobs)
}
} catch {
print("failed")
}
return tempEvents
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.destination is AddJobViewController
{
let vc = segue.destination as? AddJobViewController
vc?.calendarDateSelected = convertedDate
}
}
// MARK: - UITableViewDelegate Protocol
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let deleteAction = UIContextualAction(style: .destructive, title: "Delete") { (action, sourceView, completionHandler) in
// Delete the row from the data store
if let appDelegate = (UIApplication.shared.delegate as? AppDelegate) {
let context = appDelegate.persistentContainer.viewContext
let jobToDelete = self.fetchResultController.object(at: indexPath)
context.delete(jobToDelete)
appDelegate.saveContext()
}
// Call completion handler with true to indicate
completionHandler(true)
}
// Customize the action buttons
deleteAction.backgroundColor = UIColor(red: 231.0/255.0, green: 76.0/255.0, blue: 60.0/255.0, alpha: 1.0)
deleteAction.image = UIImage(named: "delete")
let swipeConfiguration = UISwipeActionsConfiguration(actions: [deleteAction])
return swipeConfiguration
}
// MARK: - NSFetchedResultsControllerDelegate methods
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.beginUpdates()
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch type {
case .insert:
if let newIndexPath = newIndexPath {
tableView.insertRows(at: [newIndexPath], with: .fade)
}
case .delete:
if let indexPath = indexPath {
tableView.deleteRows(at: [indexPath], with: .fade)
}
case .update:
if let indexPath = indexPath {
tableView.reloadRows(at: [indexPath], with: .fade)
}
default:
tableView.reloadData()
}
if let fetchedObjects = controller.fetchedObjects {
events = fetchedObjects as! [EventsMO]
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.endUpdates()
}
}
有什么想法我做错了吗?
答案 0 :(得分:0)
您必须在初始化performFetch
后调用fetchResultController
才能正确使用它。
fetchResultController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
fetchResultController.delegate = self
do {
try fetchResultController.performFetch()
} catch {
fatalError("Failed to fetch entities: \(error)")
}