UIPickerView + 核心数据

UIPickerView + Core Data

更新答案

import UIKit
import CoreData

class ExerciseViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UIPickerViewDataSource, UIPickerViewDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        VDL()

        //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 daysPickerView: UIPickerView!

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

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

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

    func VDL () {
        let fetchRequest = NSFetchRequest(entityName: "TrainingDay")
        let sort = NSSortDescriptor(key: "dayIndex", ascending: true)
        fetchRequest.sortDescriptors = [sort]
        daysArray = (moc!.executeFetchRequest(fetchRequest, error: nil) as? [TrainingDay])!
        if daysArray.count == 0 { // nothing there
            let dayEntity = NSEntityDescription.entityForName("TrainingDay", inManagedObjectContext: moc!)
            let days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
            for (index, name) in enumerate(days) {
                let newDay = TrainingDay(entity: dayEntity!, insertIntoManagedObjectContext: moc)
                newDay.day = name
                newDay.dayIndex = index
                daysArray.append(newDay)
            }
            var error: NSError?
            moc!.save(&error)
        }

    }


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

        let row = daysPickerView.selectedRowInComponent(0)
        let currentDay = daysArray[row]


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

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

        if let err = error {
            var status = err.localizedFailureReason
            println("\(status)")
        } else {
            println("CURRENT SETTING: \(trainingdetails.trainingDay)")
        }
    }

    @IBAction func doneButton(sender: AnyObject) {
        appendTrainingDetailsToArray()

        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
        println("\(row)")
        let details = detailsArray[indexPath.row]
        cell!.textLabel!.text = details.exerciseName
        cell?.detailTextLabel?.text = "Sets: #\(details.setsNumber) Reps: #\(details.repsNumber)"
        return cell!
    }

    //PICKER VIEW DELEGATE AND DATASOURCE METHODS
    func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
        return 1
    }

    func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return daysArray.count
    }

    func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
            let trainingDay = daysArray[row]
            return trainingDay.day
    }

    func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {

        let currentDay = daysArray[row]
        let fetchRequest = NSFetchRequest(entityName: "TrainingDetails")
        let predicate = NSPredicate(format: "trainingDay = %@", currentDay)
        fetchRequest.predicate = predicate
        let sort = NSSortDescriptor(key: "exerciseName", ascending: true)
        fetchRequest.sortDescriptors = [sort]
        detailsArray = (moc!.executeFetchRequest(fetchRequest, error: nil) as? [TrainingDetails])!
        exerciseTableView.reloadData()

    }
}

之前,我寻求帮助,以便适应 UIPickerView。 正如@pbasdf 所说,代码需要进行一些调整以符合核心数据和选择器视图。你可以在他的回答中看到他的解释!

有几件事需要解决:

TrainingDay 实体

您使用 awakeFromInsert 代码走错了路。它不会插入新对象;每当您插入一个新的 TrainingDay 对象时,它就是 运行。从我在你的代码中看到的情况来看,你实际上从未做过 insert new TrainingDays(这很幸运,因为 awakeFromInsert 会崩溃 - 你正在将一个字符串数组分配给 属性是一个字符串)。

您需要改为创建 7 个 TrainingDay 对象,一个对应一周中的每一天。由于这些不会改变,因此当您的应用首次 运行 时,您可以将此作为一次性任务执行,或者(正如我在下面所做的那样)当您尝试获取它们时 "lazily" 。您不需要 awakeFromInsert 代码,但我建议向实体添加另一个属性,比如 "dayIndex",这将使您能够将 TrainingDay 对象排序为逻辑顺序。您的代码可能如下所示:

// load the TrainingDays data...
let fetchRequest = NSFetchRequest(entityName: "TrainingDay")
let sort = NSSortDescriptor(key: "dayIndex", ascending: true)
fetchRequest.sortDescriptors = [sort]
daysArray = (moc!.executeFetchRequest(fetchRequest, error: nil) as? [TrainingDay])!
if daysArray.count == 0 { // nothing there
    let dayEntity = NSEntityDescription.entityForName("TrainingDay", inManagedObjectContext: moc!)
    let days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
    for (index, name) in enumerate(days) {
        let newDay = TrainingDay(entity: dayEntity!, insertIntoManagedObjectContext: moc)
        newDay.day = name
        newDay.dayIndex = index
        daysArray.append(newDay)
    }
    moc.save(&error)
}

您可以将其放入 viewDidLoad 或从 VDL 调用的函数中。然后您可以使用 daysArray 作为选择器视图的数据源...

填充选取器视图

放弃 daysOfPickerView 并改用 daysArray...

//PICKER VIEW DELEGATE AND DATASOURCE METHODS
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
    return 1
}

func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
    return daysArray.count
}

func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
    let trainingDay = daysArray[row]
    return trainingDay.day
}

将 TrainingDetails 对象分配给正确的日期

appendTrainingDetailsToArray方法中,您需要确定当前在选择器视图中选择了哪一天。为此,您需要为 pickerView 添加一个 @IBOutlet var 并将其连接到故事板中。然后您可以访问它并设置 TrainingDetails 的关系...

let row = pickerView.selectedRowInComponent(0)
let currentDay = daysArray[row]
trainingDetails.trainingDay = currentDay

填充您的 table 视图 目前您正在使用三个独立的数组。我只会使用一个:

var detailsArray = [TrainingDetails]()

然后使用 TrainingDetails 的属性填充 table 视图单元格,即

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 details = detailsArray[indexPath.row]
    cell!.textLabel!.text = details.exerciseName
    cell?.detailTextLabel?.text = "Sets: #\(details.setsNumber) Reps: #\(details.repsNumber)"
    return cell!
}

修改其他table查看数据源的方法同样使用detailsArray。

您的三个数组目前是使用 append... 方法构建的,但我认为您想要的是使用所选日期的正确“TrainingDetails”填充 table 视图...

响应选择器视图的变化

目前,每次选择器视图更改时,您的代码似乎都会创建新的 TrainingDetails。相反,只需获取所选日期的 TrainingDetails,然后重新加载 table:

func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
    let currentDay = daysArray[row]
    let fetchRequest = NSFetchRequest(entityName: "TrainingDetails")
    let predicate = NSPredicate(format: "trainingDay = %@", currentDay)
    fetchRequest.predicate = predicate
    let sort = NSSortDescriptor(key: "exerciseName", ascending: true)
    fetchRequest.sortDescriptors = [sort]
    detailsArray = (moc!.executeFetchRequest(fetchRequest, error: nil) as? [TrainingDetails])!
    exerciseTableView.reloadData()
}

进一步思考

您已经声明了一个 NSFetchedResultsController 但没有使用它。使用它而不是原始 NSFetchRequest 是有意义的,因为它会自动响应您添加新的 TrainingDetails 并将它们添加到 table - 而不是需要使用 reloadData()。但那是另一天...

此外,如果您能原谅我,您对可选项有点自由:!和 ?。我在复制你的代码的地方没有改变它们,但你可以整理它们。与上下文操作的 error 参数类似 - 它总是值得使用的。也就是说,我在 Xcode 中没有测试就起草了以上内容,所以如果我在某些地方犯了一些错误,请原谅我;希望你能看到这个想法。

这不会编译,我可能混合了 Swift 和 C# (!),但总体思路如下所示...

func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int 
{
   return trainingDays.Count;
}

func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! 
{
   return trainingDays[row].Name
}
// Variable for holding the selected day from the PickerView...
TrainingDay selectedTrainingDay;

// Set the selected TraininDay when the user choses a day from the PickerView
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) 
{
     selectedTrainingDay = trainingDays[row]

}

// Save the record
func Done() 
{
    // Create the trainingDetails
let detailsEntity = NSEntityDescription.entityForName("TrainingDetails", inManagedObjectContext: moc!)
                        let trainingdetails = TrainingDetails(entity: detailsEntity!, insertIntoManagedObjectContext: moc)
                        trainingdetails.exerciseName = exerciseName.text
                        trainingdetails.setsNumber = setsNumber.text!
                        trainingdetails.repsNumber = repsNumber.text!
                        trainingdetails.trainingDay = selectedTrainingDay

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

}

// Array for holding the trainingDays
Array<TrainingDay> trainingDays = new Array<TrainingDay>()

// Create Core Data objects for each training day
// Call this once only or you will end up with multiple trainingDays
// So you need to do this the first time the app is run
// You can do a fetch on startup to see if they exist, and create them
// if they don't.
// Subsequent starts you will need to do a fetch to populate the 
// trainingDays array... Sorry no code for that here.
func createTrainingDays(moc:NSManagedObjectContext)
    {

        trainingDays.Add(createTrainingDay(moc, "TrainingDay", 0, "Sunday"));
        trainingDays.Add(createTrainingDay(moc, "TrainingDay", 1, "Monday"));
        trainingDays.Add(createTrainingDay(moc, "TrainingDay", 2, "Tuesday"));
        trainingDays.Add(createTrainingDay(moc, "TrainingDay", 3, "Wednesday"));
        trainingDays.Add(createTrainingDay(moc, "TrainingDay", 4, "Thursday"));
        trainingDays.Add(createTrainingDay(moc, "TrainingDay", 5, "Friday"));
        trainingDays.Add(createTrainingDay(moc, "TrainingDay", 6, "Saterday"));

        moc.save()
    }
// Helper Function
func createTrainingDay(moc:NSManagedObjectContext, entityName:String, sortIndex:Int, name:String) -> NSManagedObject
    {

        if let newManagedObject:NSManagedObject = NSEntityDescription.insertNewObjectForEntityForName(entityName, inManagedObjectContext:moc) {

            newManagedObject.setValue(sortIndex, forKey:"sortIndex")
            newManagedObject.setValue(name, forKey:"name")

            return newManagedObject;

        }

    }