核心数据滑动错误以删除实施

时间:2018-09-02 22:23:54

标签: xcode core-data swift4 swipe-gesture

在表格视图中滑动以删除时,出现以下错误:

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()
    }
}

有什么想法我做错了吗?

1 个答案:

答案 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)")
}