Reputation: 188
UPDATE WITH ANSWER
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()
}
}
Previously, I asked help so I could adapt with UIPickerView. Like @pbasdf said, the code needed some adjustments to conform with core data and the picker view. You can see his explanation in his answer!
Upvotes: 1
Views: 1653
Reputation: 8988
This is not going to compile and I may have mixed Swift and C# (!) but the general idea is shown below...
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;
}
}
Upvotes: 0
Reputation: 21536
There are several things to address:
The TrainingDay entity
You've taken a wrong turn with the awakeFromInsert
code. It doesn't insert new objects; it is run whenever you insert a new TrainingDay
object. From what I can see in your code you never do actually insert new TrainingDays (which is lucky because the awakeFromInsert will crash - you are assigning an array of strings to a property which is a string).
You need instead to create 7 TrainingDay objects, one for each day of the week. Since these do not change, you can do this as a one-off task when your app is first run, or (as I do below) "lazily" when you try to fetch them. You do not need the awakeFromInsert
code, but I would recommend adding another attribute to the entity, say "dayIndex", which will enable you to sort the TrainingDay objects into a logical order. Your code might look something like this:
// 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)
}
You could put this in viewDidLoad
or in a func called from VDL. Then you can use daysArray
as the data source for your picker view...
Populating the picker view
Abandon daysOfPickerView
and use daysArray
instead...
//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
}
Assigning the TrainingDetails object to the correct day
In the appendTrainingDetailsToArray
method, you need to determine which day is currently selected in the picker view. For that purpose, you will need to add an @IBOutlet var for the pickerView and hook it up in your storyboard. Then you can access it and set the relationship for the TrainingDetails...
let row = pickerView.selectedRowInComponent(0)
let currentDay = daysArray[row]
trainingDetails.trainingDay = currentDay
Populating your table view Currently you are using three separate arrays. I would use just one:
var detailsArray = [TrainingDetails]()
Then use the properties of TrainingDetails
to populate the table view cells, ie.
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!
}
Amend the other table view data source methods likewise to use detailsArray.
Your three arrays are currently built up using the append...
method, but I think what you want is to populate the table view with the correct 'TrainingDetails` for the chosen day...
Responding to the picker view changing
Currently your code seems to create new TrainingDetails
every time the picker view changes. Instead, just fetch the TrainingDetails for the chosen day, and reload the 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()
}
Further thoughts
You have declared an NSFetchedResultsController
but not used it. It would make sense to use it instead of the raw NSFetchRequest, because it will automatically respond to you adding new TrainingDetails and will add them to the table - rather than needing to use reloadData(). But that's for another day...
Also, if you'll forgive me, you're a bit liberal with optionals: ! and ?. I've left them in unchanged where I've copied your code, but you could tidy them up. Likewise with the error
argument for the context operations - it's always worth using it. That said, I've drafted the above without testing it in Xcode, so forgive me if I've made the some mistakes in places; hopefully you can see the idea.
Upvotes: 1