UIPickerView + Основные данные

ОБНОВЛЕНИЕ С ОТВЕТОМ

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, код нуждался в некоторых корректировках, чтобы соответствовать основным данным и представлению выбора. Вы можете увидеть его объяснение в его ответе!

2 ответа

Решение

Есть несколько вещей для решения:

Сущность TrainingDay

Вы ошиблись с awakeFromInsert код. Он не вставляет новые объекты; он запускается всякий раз, когда вы вставляете новый TrainingDay объект. Из того, что я вижу в вашем коде, вы фактически никогда не вставляете новые TrainingDays (что повезло, потому что awakeFromInsert вылетает - вы присваиваете массив строк свойству, которое является строкой).

Вместо этого вам нужно создать 7 объектов TrainingDay, по одному на каждый день недели. Поскольку они не меняются, вы можете сделать это как разовую задачу, когда ваше приложение запускается впервые, или (как я делаю ниже) "лениво", когда вы пытаетесь получить их. Вам не нужно 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 метод, вам нужно определить, какой день в настоящее время выбран в представлении выбора. Для этого вам нужно добавить переменную @IBOutlet для pickerView и подключить ее к раскадровке. Затем вы можете получить к нему доступ и установить отношения для TrainingDetails...

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

Заполнение табличного представления В настоящее время вы используете три отдельных массива. Я бы использовал только один:

var detailsArray = [TrainingDetails]()

Тогда используйте свойства TrainingDetails заполнить ячейки табличного представления, т.е.

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

Также измените другие методы источника данных табличного представления, чтобы использовать detailsArray.

Ваши три массива в настоящее время создаются с использованием append... метод, но я думаю, что вы хотите, чтобы заполнить табличное представление с правильным 'TrainingDetails` для выбранного дня...

Реагирование на изменение вида сборщика

В настоящее время ваш код, кажется, создает новый TrainingDetails каждый раз, когда меняется вид сборщика. Вместо этого просто выберите TrainingDetails для выбранного дня и перезагрузите таблицу:

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 и добавлять их в таблицу, а не использовать 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;

        }

    }
Другие вопросы по тегам