CoreData没有保存 - TableViewCell返回nill

时间:2015-08-13 16:22:30

标签: swift uitableview core-data nsfetchedresultscontroller

这是我的代码:

    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并将其设置为单元格的textLabel,但它在此处崩溃:println("THE ERROR IS HERE #\(exCell.exerciseName) \(exCell.repsNumber) \(exCell.setsNumber)")。我似乎没有正确地检索它,但我确定它正在CoreData中保存。

任何人都有任何想法?我有一个类似的应用程序,但这个不起作用。

感谢您的帮助,我期待弄清楚这里有什么问题。

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

enter image description here

编辑2

这样做: enter image description here

导致这个:

enter image description here

最终编辑 - >的

汤姆说,做到这一点:

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!")
        }
    }

用这些方法,让我解决了我做错了什么!非常感谢你!!

2 个答案:

答案 0 :(得分:2)

这就是它的样子......

您的方法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 。由于您的实例都没有全部三个值,因此您的应用程序崩溃了。

这种情况正在发生,因为Core Data知道可选值是什么,Swift也知道“可选”是什么意思,但这些不是同一个东西。 Core Data并不关心你是否将这些属性保留为nil值,但Swift确实如此。这两个想法之间的冲突导致了你的问题。

你应该做什么:

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

答案 1 :(得分:0)

在你的函数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!")
    }

然后,为了正确设置表,最简单的方法是通过读取数组的计数和数据进行设置。所以也许您可以调用Core Data来返回数据,然后将它们附加到名为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(仅举例来说)返回行数来计算表中应该有多少行可能更容易:

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

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

CustomCell

希望这对您已经尝试的内容有所帮助并且不会多余!在尝试保存到Core Data然后检索数据以便在表格单元格中使用时,这对我有用。