核心数据不保存 - UITableViewCell 返回 nil

CoreData not saving - TableViewCell returning nill

这是我的代码:

    import UIKit
    import CoreData

    class ExerciseViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

        override func viewDidLoad() {
            super.viewDidLoad()

            //sets stepper configs
            setsStepper.wraps = false
            setsStepper.autorepeat = true
            setsStepper.continuous = true
            setsStepper.tintColor = UIColor.redColor()
            setsStepper.minimumValue = 0
            setsStepper.maximumValue = 500
            setsStepper.value = 0

            //reps stepper configs
            repsStepper.wraps = false
            repsStepper.autorepeat = true
            repsStepper.continuous = true
            repsStepper.tintColor = UIColor.orangeColor()
            repsStepper.minimumValue = 0
            repsStepper.maximumValue = 500
            repsStepper.value = 0

            exerciseTableView.reloadData()
        }

        var moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
        var fetchedResultsController: NSFetchedResultsController?

        @IBOutlet var exerciseTableView: UITableView!

        @IBOutlet var dayName: UITextField!
        @IBOutlet var exerciseName: UITextField!
        @IBOutlet var setsStepper: UIStepper!
        @IBOutlet var repsStepper: UIStepper!

        @IBOutlet var setsNumber: UILabel!
        @IBOutlet var repsNumber: UILabel!

        var daysArray = [String]()
        var namesArray = [String]()
        var setsArray = [Int]()
        var repsArray = [Int]()


        func appendDaysToArray() {
            let dayLabel = dayName.text
            daysArray.append(dayLabel)

            let entityDescription = NSEntityDescription.entityForName("TrainingDay", inManagedObjectContext: moc!)
            let trainingday = TrainingDay(entity: entityDescription!, insertIntoManagedObjectContext: moc)
            trainingday.day = dayName.text

            var error: NSError?
            moc?.save(&error)

            if let err = error {
                var status = err.localizedFailureReason
                println("\(status)")
            } else {
                println("Day #\(dayName.text) saved successfully!")
            }
        }

        func appendNamesToArray () {
            let nameLabel = exerciseName.text
            namesArray.append(nameLabel)

            let entityDescription = NSEntityDescription.entityForName("TrainingDetails", inManagedObjectContext: moc!)
            let trainingdetails = TrainingDetails(entity: entityDescription!, insertIntoManagedObjectContext: moc)
            trainingdetails.exerciseName = exerciseName.text

            var error: NSError?
            moc?.save(&error)

            if let err = error {
                var status = err.localizedFailureReason
                println("\(status)")
            } else {
                println("Exercise: #\(exerciseName.text) saved successfully!")
            }

        }

        func appendNumberToSets () {
            let numberOfSets = setsNumber.text?.toInt()
            setsArray.append(numberOfSets!)

            let entityDescription = NSEntityDescription.entityForName("TrainingDetails", inManagedObjectContext: moc!)
            let trainingdetails = TrainingDetails(entity: entityDescription!, insertIntoManagedObjectContext: moc)
            trainingdetails.setsNumber = setsNumber.text!

            var error: NSError?
            moc?.save(&error)

            if let err = error {
                var status = err.localizedFailureReason
                println("\(status)")
            } else {
                println("Exercise: #\(setsNumber.text) saved successfully!")
            }

        }

        func appendNumberOfReps () {
            let numberOfReps = repsNumber.text?.toInt()
            repsArray.append(numberOfReps!)

            let entityDescription = NSEntityDescription.entityForName("TrainingDetails", inManagedObjectContext: moc!)
            let trainingdetails = TrainingDetails(entity: entityDescription!, insertIntoManagedObjectContext: moc)
            trainingdetails.repsNumber = repsNumber.text!

            var error: NSError?
            moc?.save(&error)

            if let err = error {
                var status = err.localizedFailureReason
                println("\(status)")
            } else {
                println("Exercise: #\(repsNumber.text) saved successfully!")
            }

        }

        @IBAction func doneButton(sender: AnyObject) {
            println("\(dayName.text)")
            appendDaysToArray()
            println("\(exerciseName.text)")
            appendNamesToArray()
            println("\(setsNumber.text)")
            appendNumberToSets()
            println("\(repsNumber.text)")
            appendNumberOfReps()
            exerciseTableView.reloadData()
        }

        @IBAction func setsStepperAction(sender: UIStepper) {
            println("\(Int(sender.value))")
            setsNumber.text = Int(sender.value).description
        }

        @IBAction func repsStepper(sender: UIStepper) {
            println("\(Int(sender.value))")
            repsNumber.text = Int(sender.value).description
        }

        func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return namesArray.count
        }

        func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

            let cellIdentifier = "exerciseCell"
    var cell  = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? UITableViewCell
            if cell == nil {
                cell = UITableViewCell(style: UITableViewCellStyle.Value2, reuseIdentifier: cellIdentifier)
            }
            let row = indexPath.row
                let name = namesArray[indexPath.row]
            let numberReps = repsArray[indexPath.row]
            let numberSets = setsArray[indexPath.row]
                cell!.textLabel!.text = name
            cell?.detailTextLabel?.text = "Sets: #\(numberSets) Reps: #\(numberReps)"
            return cell!
        }
    }

class ViewExercisesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, NSFetchedResultsControllerDelegate {

    override func viewDidLoad() {

        fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchTrainingDetails(), managedObjectContext: moc!, sectionNameKeyPath: nil, cacheName: nil)
        fetchedResultsController?.delegate = self
        fetchedResultsController?.performFetch(nil)
        self.viewExerciseTableView.reloadData()


        sundayButton.frame = CGRectMake(-30,150,125,125)
        sundayButton.addTarget(self, action: "sundayButtonTouch:", forControlEvents: UIControlEvents.TouchDown)
        sundayButton.setImage(imageSunday, forState: .Normal)
        sundayButton.imageEdgeInsets = UIEdgeInsetsMake(30,30,30,30)
        self.view.addSubview(sundayButton)

        mondayButton.frame = CGRectMake(120,150,125,125)
        mondayButton.addTarget(self, action: "mondayButtonTouch:", forControlEvents: UIControlEvents.TouchDown)
        mondayButton.setImage(imageMonday, forState: .Normal)
        mondayButton.imageEdgeInsets = UIEdgeInsetsMake(30,30,30,30)
        self.view.addSubview(mondayButton)

        tuesdayButton.frame = CGRectMake(270,150,125,125)
        tuesdayButton.addTarget(self, action: "tuesdayButtonTouch:", forControlEvents: UIControlEvents.TouchDown)
        tuesdayButton.setImage(imageTuesday, forState: .Normal)
        tuesdayButton.imageEdgeInsets = UIEdgeInsetsMake(30,30,30,30)
        self.view.addSubview(tuesdayButton)

        wednesdayButton.frame = CGRectMake(-30,250,125,125)
        wednesdayButton.addTarget(self, action: "wednesdayButtonTouch:", forControlEvents: UIControlEvents.TouchDown)
        wednesdayButton.setImage(imageWednesday, forState: .Normal)
        wednesdayButton.imageEdgeInsets = UIEdgeInsetsMake(30,30,30,30)
        self.view.addSubview(wednesdayButton)

        thursdayButton.frame = CGRectMake(70,250,125,125)
        thursdayButton.addTarget(self, action: "thursdayButtonTouch:", forControlEvents: UIControlEvents.TouchDown)
        thursdayButton.setImage(imageThursday, forState: .Normal)
        thursdayButton.imageEdgeInsets = UIEdgeInsetsMake(30,30,30,30)
        self.view.addSubview(thursdayButton)

        fridayButton.frame = CGRectMake(170,250,125,125)
        fridayButton.addTarget(self, action: "fridayButtonTouch:", forControlEvents: UIControlEvents.TouchDown)
        fridayButton.setImage(imageFriday, forState: .Normal)
        fridayButton.imageEdgeInsets = UIEdgeInsetsMake(30,30,30,30)
        self.view.addSubview(fridayButton)

        saturdayButton.frame = CGRectMake(270,250,125,125)
        saturdayButton.addTarget(self, action: "saturdayButtonTouch:", forControlEvents: UIControlEvents.TouchDown)
        saturdayButton.setImage(imageSaturday, forState: .Normal)
        saturdayButton.imageEdgeInsets = UIEdgeInsetsMake(30,30,30,30)
        self.view.addSubview(saturdayButton)


    }

    //VAR AND LET

    var sundayButton = UIButton.buttonWithType(UIButtonType.Custom) as! UIButton
    var imageSunday = UIImage(named: "day.png")

    var mondayButton = UIButton.buttonWithType(UIButtonType.Custom) as! UIButton
    var imageMonday = UIImage(named: "day.png")

    var tuesdayButton = UIButton.buttonWithType(UIButtonType.Custom) as! UIButton
    var imageTuesday = UIImage(named: "day.png")

    var wednesdayButton = UIButton.buttonWithType(UIButtonType.Custom) as! UIButton
    var imageWednesday = UIImage(named: "day.png")

    var thursdayButton = UIButton.buttonWithType(UIButtonType.Custom) as! UIButton
    var imageThursday = UIImage(named: "day.png")

    var fridayButton = UIButton.buttonWithType(UIButtonType.Custom) as! UIButton
    var imageFriday = UIImage(named: "day.png")

    var saturdayButton = UIButton.buttonWithType(UIButtonType.Custom) as! UIButton
    var imageSaturday = UIImage(named: "day.png")

    @IBOutlet var viewExerciseTableView: UITableView!

    var moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
    var fetchedResultsController: NSFetchedResultsController?

    // FUNCTIONS

    func sundayButtonTouch(sender: UIButton!) {
        println("future event will be added, button working fine - sunday")
    }

    func mondayButtonTouch(sender: UIButton!) {
        println("future event will be added, button working fine - monday")
    }

    func tuesdayButtonTouch(sender: UIButton!) {
        println("future event will be added, button working fine - tuesday")
    }

    func wednesdayButtonTouch(sender: UIButton!) {
        println("future event will be added, button working fine - wednesday")
    }

    func thursdayButtonTouch(sender: UIButton!) {
        println("future event will be added, button working fine - thursday")
    }

    func fridayButtonTouch(sender: UIButton!) {
        println("future event will be added, button working fine - friday")
    }

    func saturdayButtonTouch(sender: UIButton!) {
        println("future event will be added, button working fine - saturday")
    }

    // FETCH REQUEST METHODS
    func fetchTrainingDay() -> NSFetchRequest {
        let fetchRequest = NSFetchRequest(entityName: "TrainingDay")
       // let predicate = NSPredicate(format: "day == %@")
        let sortDescriptor = NSSortDescriptor(key: "day", ascending: true)
        fetchRequest.predicate = nil
        fetchRequest.sortDescriptors = [sortDescriptor]
        fetchRequest.fetchBatchSize = 20
        return fetchRequest
    }

    func fetchTrainingDetails() -> NSFetchRequest {
        let fetchRequest = NSFetchRequest(entityName: "TrainingDetails")
        fetchRequest.predicate = nil
        let sortDescriptor1 = NSSortDescriptor(key: "exerciseName", ascending: true)
        let sortDescriptor2 = NSSortDescriptor(key: "repsNumber", ascending: true)
        let sortDescriptor3 = NSSortDescriptor(key: "setsNumber", ascending: true)
        fetchRequest.sortDescriptors = [sortDescriptor1, sortDescriptor2, sortDescriptor3]
        fetchRequest.fetchBatchSize = 20
        return fetchRequest
    }



    //TABLE VIEW DELEGATE METHODS
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return fetchedResultsController?.sections?[section].numberOfObjects ?? 0
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

        let cellIdentifier = "exCell"
        var cell  = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? UITableViewCell

        if let exCell = fetchedResultsController?.objectAtIndexPath(indexPath) as? TrainingDetails {
            println("THE ERROR IS HERE #\(exCell.exerciseName) \(exCell.repsNumber) \(exCell.setsNumber)")
            cell!.textLabel?.text = exCell.exerciseName
            cell?.detailTextLabel?.text = "Sets: #\(exCell.setsNumber) Reps: #\(exCell.repsNumber)"
        }
        return cell!
    }

    // MARK: NSFetchedResultsControllerDelegate
    func controllerWillChangeContent(controller: NSFetchedResultsController) {
        self.viewExerciseTableView.beginUpdates()
    }
    func controller(controller: NSFetchedResultsController,
        didChangeObject anObject: AnyObject,
        atIndexPath indexPath: NSIndexPath?,
        forChangeType type: NSFetchedResultsChangeType,
        newIndexPath: NSIndexPath?)
    {
        switch(type) {
        case .Insert:
            if let newIndexPath = newIndexPath {
                viewExerciseTableView.insertRowsAtIndexPaths([newIndexPath],
                    withRowAnimation:UITableViewRowAnimation.Fade)
            }
        case .Delete:
            if let indexPath = indexPath {
                viewExerciseTableView.deleteRowsAtIndexPaths([indexPath],
                    withRowAnimation: UITableViewRowAnimation.Fade)
            }
        case .Update:
            break
        case .Move:
            if let indexPath = indexPath {
                if let newIndexPath = newIndexPath {
                    viewExerciseTableView.deleteRowsAtIndexPaths([indexPath],
                        withRowAnimation: UITableViewRowAnimation.Fade)
                    viewExerciseTableView.insertRowsAtIndexPaths([newIndexPath],
                        withRowAnimation: UITableViewRowAnimation.Fade)
                }
            }
        }
    }

    func controller(controller: NSFetchedResultsController,
        didChangeSection sectionInfo: NSFetchedResultsSectionInfo,
        atIndex sectionIndex: Int,
        forChangeType type: NSFetchedResultsChangeType)
    {
        switch(type) {
        case .Insert:
            viewExerciseTableView.insertSections(NSIndexSet(index: sectionIndex),
                withRowAnimation: UITableViewRowAnimation.Fade)
        case .Delete:
            viewExerciseTableView.deleteSections(NSIndexSet(index: sectionIndex),
                withRowAnimation: UITableViewRowAnimation.Fade)
        default:
            break
        }
    }
    func controllerDidChangeContent(controller: NSFetchedResultsController) {
        viewExerciseTableView.endUpdates()
    }





}

import Foundation
import CoreData

class TrainingDetails: NSManagedObject {

    @NSManaged var exerciseName: String
    @NSManaged var setsNumber: String
    @NSManaged var repsNumber: String
    @NSManaged var relationship2: NSManagedObject

}

逻辑是这样的:在"ExerciseViewController"中,我在CoreData中放了一些东西,这个视图工作正常。现在,在 ViewExercisesViewController 中,我应该检索 exerciseName 并将其设置为单元格的文本标签,但它就在此处崩溃:println("THE ERROR IS HERE #\(exCell.exerciseName) \(exCell.repsNumber) \(exCell.setsNumber)")。似乎我没有正确检索它,但我确定它被保存在 CoreData 中。

有人知道吗?我有一个应用程序可以做类似的事情,但是这个不工作。

感谢您的帮助,我期待着找出问题所在。

编辑 -> 图片有挤压错误:

编辑 2

这样做:

导致这个:

最终编辑 -> 解决方案

正如汤姆所说,这样做:

func appendTrainingDetails () {
        let nameLabel = exerciseName.text
        namesArray.append(nameLabel)
        let numberOfSets = setsNumber.text?.toInt()
        setsArray.append(numberOfSets!)
        let numberOfReps = repsNumber.text?.toInt()
        repsArray.append(numberOfReps!)

        let entityDescription = NSEntityDescription.entityForName("TrainingDetails", inManagedObjectContext: moc!)
        let trainingdetails = TrainingDetails(entity: entityDescription!, insertIntoManagedObjectContext: moc)
        trainingdetails.exerciseName = exerciseName.text
        trainingdetails.setsNumber = setsNumber.text!
        trainingdetails.repsNumber = repsNumber.text!

        var error: NSError?
        moc?.save(&error)

        if let err = error {
            var status = err.localizedFailureReason
            println("\(status)")
        } else {
            println("Exercise: #\(exerciseName.text) saved successfully!")
            println("Number of sets: #\(setsNumber.text) saved successfully!")
            println("Number of reps: #\(repsNumber.text) saved successfully!")
        }
    }

通过这些方法,让我修正了我做错的地方!非常非常感谢!!

在你的函数 appendNumberOfReps() 中,你似乎使用点语法将它添加到 TrainingDay 对象(不确定你在哪里定义它,虽然我假设它是一个 NSManagedObject?) .在您的核心数据实体 TrainingDay 中,创建一个名为 repsNumber 的属性,我假设我们可以将其设置为 String 并试试这个:

// Save To Core Data
let entityDescription =  NSEntityDescription.entityForName("TrainingDay", inManagedObjectContext: moc!)
let trainingday = NSManagedObject(entity: entityDescription!, insertIntoManagedObjectContext: moc)

var error: NSError?
moc?.save(&error)

    trainingday.setValue(repsNumber.text!, forKey: "repsNumber")

    if !moc.save(&error) {
        println("Could not save \(error)")
    } else {
        println("Exercise: #\(repsNumber.text) saved successfully!")
    }

然后为了正确设置您的 table,最简单的方法是通过读取数组的计数和数据来设置它们。因此,也许您可​​以调用 Core Data 来 return 数据,然后将它们附加到名为 var repsArray: String = []:

的数组中
// Fetch From Core Data
let trainingFetchRequest = NSFetchRequest(entityName:"TrainingDay")
let fetchedResults = moc.executeFetchRequest(trainingFetchRequest, error: &error) as? [NSManagedObject]

    if let results = fetchedResults {

        if results.count > 0 {
            for index in 0...results.count-1 {

                let match = results[index] as NSManagedObject

                repsArray.append(match.valueForKey("repsNumber") as! String)
            }
        }

    }

然后通过使用 repsArray(仅作为示例)到 return 行计数可能更容易计算 table 中应该有多少行:

func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
    return repsArray.count
}

然后只要你有正确的table单元格class(例如下面的CustomCell)和在Interface Builder中创建的标签名称(例如repNumberLabel 下面)然后附加到 CustomCell,你可以只使用 indexPath.Row 来处理你构建的数组:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    var cell = tableView.dequeueReusableCellWithIdentifier("cell") as? CustomCell

    if !(cell != nil) {
        cell = CustomCell(style: .Subtitle, reuseIdentifier: "cell")
    }

    cell?.repNumberLabel.text = self.repsArray[indexPath.row]
}

希望这对您已经尝试过的内容有所帮助,并且不是多余的!这就是我尝试保存到核心数据然后检索数据以在 table 单元格中使用时对我有用的方法。

这是正在发生的事情...

您的方法 appendNumberToSets()appendNumberOfReps()appendNamesToArray() 都有以下代码:

let entityDescription = NSEntityDescription.entityForName("TrainingDetails", inManagedObjectContext: moc!)
let trainingdetails = TrainingDetails(entity: entityDescription!, insertIntoManagedObjectContext: moc)

这意味着 其中每一个 都在创建 TrainingDetails 的新实例。然而,

  • appendNumberToSets() setsNumber 属性
  • 设置一个值
  • appendNamesToArray() exerciseName 属性
  • 设置一个值
  • appendNumberOfReps() repsNumber 属性
  • 设置一个值

在所有这三种方法中,您都在创建新的 TrainingDetails 实例,但将大部分属性保留为 nil 值。 那没问题 只是稍后您尝试在同一实例上查找 exerciseNamesetsNumberrepsNumber 。由于 none 个实例具有所有三个值,因此您的应用程序崩溃了。

这是因为 Core Data 知道可选值是什么,Swift 也知道 "optional" 是什么意思,但是 这些不是同样的事情。 Core Data 不关心您是否将这些属性保留为 nil 值,但 Swift 会。这两种想法的冲突导致了你的问题。

你应该做什么:

  • 如果这些值应该是可选的,也就是说,它们可以为零,你应该改变你的TrainingDetails class来使它们Swift 选项。看起来您正在使用 Xcode 生成的代码,但 该代码是错误的 并且完全可以修复它。然后编译器会强制你检查属性是否有值,你不会崩溃。
  • 如果这些值不应该是可选的,也就是说,任何 TrainingDetails 都必须具有这些属性中的每一个的值,您应该做两件事。首先,编辑您的核心数据模型。对于这些属性中的每一个,取消选中模型编辑器中的 "Optional" 框。这样 Core Data 就会知道 nil 是不允许的,如果你有意外的 nil,它会阻止你保存更改。其次,您需要将上述三种方法更改为 (a) 始终为所有属性赋值,或 (b) 重复使用相同的 TrainingDetails 实例,而不是在每个方法中创建一个新实例(选择取决于您的要求,因此由您决定哪个是正确的。