Swift CoreData 在 UITableViews 之间传递选定的行值
Swift CoreData passing selected row value between UITableViews
我是 Swift 和编码的新手,才大约一个月,我正在尝试构建一些简单的 UItableViews 来设置一些 CoreData 属性。
我的 CoreData 实体的结构是 Workouts 实体和 Exercises 实体之间的多对多关系。 (我会 post 一些图片,但我没有足够高的代表!)
我想要实现的是一个简单的设置菜单,用户可以在其中创建一个锻炼,然后通过使用顶部带有导航控制器的 tableView(就像 iOS 设置菜单)
目前我已经让它工作了,所以你可以添加一些锻炼,然后你可以去练习表视图添加一些练习。但是我无法做两件事:
1) 我如何确保当用户添加一个锻炼时,它被分配到他们从之前的 tableView 中选择的正确锻炼?
2) 如何确保 Exercise tableView 只显示他们选择的锻炼的练习?
我已经围绕这个主题做了很多阅读,并认为答案与从锻炼到练习的转折有关(通过使用委托将锻炼名称传递给练习表视图?然后使用 NSPredicate 来将显示的锻炼限制为所选的锻炼?
我不是 100% 确定,但我们将不胜感激!
这里是从 Workouts 到 Exercises 的 segue 代码:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "excerciseMaster" {
let ExcerciseMasterTableViewController = segue.destinationViewController as UIViewController
let indexPath = tableView.indexPathForSelectedRow()!
let workout = workouts[indexPath.row]
let destinationTitle = workout.workoutName
ExcerciseMasterTableViewController.title = destinationTitle
}
}
这是我的练习 tableViewController 的代码:
import UIKit
import CoreData
class ExcerciseMasterTableViewController: UITableViewController {
// Create an empty array of Excercises
var excercises = [Excercises]()
// Retreive the managedObjectContext from AppDelegate
let managedObjectContext = (UIApplication.sharedApplication().delegate as AppDelegate).managedObjectContext
override func viewDidLoad() {
super.viewDidLoad()
// Use optional binding to confirm the managedObjectContext
if let moc = self.managedObjectContext {
}
fetchExcercises()
}
func fetchExcercises() {
let fetchRequest = NSFetchRequest(entityName: "Excercises")
// Create a sort descriptor object that sorts on the "excerciseName"
// property of the Core Data object
let sortDescriptor = NSSortDescriptor(key: "excerciseName", ascending: true)
// Set the list of sort descriptors in the fetch request,
// so it includes the sort descriptor
fetchRequest.sortDescriptors = [sortDescriptor]
if let fetchResults = managedObjectContext!.executeFetchRequest(fetchRequest, error: nil) as? [Excercises] {
excercises = fetchResults
}
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// How many rows are there in this section?
// There's only 1 section, and it has a number of rows
// equal to the number of excercises, so return the count
return excercises.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("Excercise Cell", forIndexPath: indexPath) as UITableViewCell
// Get the Excercises for this index
let excercise = excercises[indexPath.row]
// Set the title of the cell to be the title of the excercise
cell.textLabel!.text = excercise.excerciseName
cell.detailTextLabel!.text = "\(excercise.sets)x\(excercise.reps)"
cell.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator
return cell
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if(editingStyle == .Delete ) {
// Find the Excercise object the user is trying to delete
let excerciseToDelete = excercises[indexPath.row]
// Delete it from the managedObjectContext
managedObjectContext?.deleteObject(excerciseToDelete)
// Refresh the table view to indicate that it's deleted
self.fetchExcercises()
// Tell the table view to animate out that row
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
save()
}
}
// MARK: UITableViewDelegate
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let excercise = excercises[indexPath.row]
}
let addExcerciseAlertViewTag = 0
let addExcerciseTextAlertViewTag = 1
@IBAction func addExcerciseButton(sender: AnyObject) {
var namePrompt = UIAlertController(title: "Add Excercise",
message: "Enter Name",
preferredStyle: .Alert)
var excerciseNameTextField: UITextField?
namePrompt.addTextFieldWithConfigurationHandler {
(textField) -> Void in
excerciseNameTextField = textField
textField.placeholder = "Title"
}
namePrompt.addAction(UIAlertAction(title: "Ok",
style: .Default,
handler: { (action) -> Void in
if let textField = excerciseNameTextField {
self.saveNewItem(textField.text, workoutName: "Workout A")
}
}))
self.presentViewController(namePrompt, animated: true, completion: nil)
}
func saveNewItem(excerciseName : String, workoutName: String) {
// Create the new excercise item
var newExcercise = Excercises.createExcerciseInManagedObjectContext(self.managedObjectContext!, name: excerciseName)
println(excerciseName)
println(workoutName)
// Update the array containing the table view row data
self.fetchExcercises()
// Animate in the new row
// Use Swift's find() function to figure out the index of the newExcercise
// after it's been added and sorted in our Excercises array
if let newExcerciseIndex = find(excercises, newExcercise) {
// Create an NSIndexPath from the newExcerciseIndex
let newExcerciseIndexPath = NSIndexPath(forRow: newExcerciseIndex, inSection: 0)
// Animate in the insertion of this row
tableView.insertRowsAtIndexPaths([ newExcerciseIndexPath ], withRowAnimation: .Automatic)
save()
}
}
func save() {
var error : NSError?
if(managedObjectContext!.save(&error) ) {
println(error?.localizedDescription)
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "excerciseSettings" {
let ExcerciseSettingsDetailViewController = segue.destinationViewController as UIViewController
let indexPath = tableView.indexPathForSelectedRow()!
let excercise = excercises[indexPath.row]
let destinationTitle = excercise.excerciseName
ExcerciseSettingsDetailViewController.title = destinationTitle
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
1.The 转场结束。您的 ExcerciseMasterTableViewController 没有 属性 命名的锻炼。您需要添加
var workout:Workout!
到您的 ExcerciseMasterTableViewController。
你的序列应该看起来更像这样
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "excerciseMaster" {
let desitnationController = segue.destinationViewController as ExcerciseMasterTableViewController
let indexPath = tableView.indexPathForSelectedRow()!
let workout = workouts[indexPath.row]
destinationController.workout = workout
let destinationTitle = workout.workoutName
desitnationController.title = destinationTitle // Usually you would put this in the viewDidLoad method of the destinationController
}
}
然后在您的 ExcerciseMasterTableViewController 中添加练习时,只需设置他们的锻炼 属性
workout.exercises = exercises // See note in #2
- 为确保仅显示正确的练习,请在您的 viewDidLoad 中将您的练习数组设置为 workout.exercises。请注意,workout.exercises 应该是一个 NSSet,因此您需要将您的集合转换为数组,或者让练习的类型为 NSSet 而不是数组。
我是 Swift 和编码的新手,才大约一个月,我正在尝试构建一些简单的 UItableViews 来设置一些 CoreData 属性。
我的 CoreData 实体的结构是 Workouts 实体和 Exercises 实体之间的多对多关系。 (我会 post 一些图片,但我没有足够高的代表!)
我想要实现的是一个简单的设置菜单,用户可以在其中创建一个锻炼,然后通过使用顶部带有导航控制器的 tableView(就像 iOS 设置菜单)
目前我已经让它工作了,所以你可以添加一些锻炼,然后你可以去练习表视图添加一些练习。但是我无法做两件事:
1) 我如何确保当用户添加一个锻炼时,它被分配到他们从之前的 tableView 中选择的正确锻炼?
2) 如何确保 Exercise tableView 只显示他们选择的锻炼的练习?
我已经围绕这个主题做了很多阅读,并认为答案与从锻炼到练习的转折有关(通过使用委托将锻炼名称传递给练习表视图?然后使用 NSPredicate 来将显示的锻炼限制为所选的锻炼?
我不是 100% 确定,但我们将不胜感激!
这里是从 Workouts 到 Exercises 的 segue 代码:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "excerciseMaster" {
let ExcerciseMasterTableViewController = segue.destinationViewController as UIViewController
let indexPath = tableView.indexPathForSelectedRow()!
let workout = workouts[indexPath.row]
let destinationTitle = workout.workoutName
ExcerciseMasterTableViewController.title = destinationTitle
}
}
这是我的练习 tableViewController 的代码:
import UIKit
import CoreData
class ExcerciseMasterTableViewController: UITableViewController {
// Create an empty array of Excercises
var excercises = [Excercises]()
// Retreive the managedObjectContext from AppDelegate
let managedObjectContext = (UIApplication.sharedApplication().delegate as AppDelegate).managedObjectContext
override func viewDidLoad() {
super.viewDidLoad()
// Use optional binding to confirm the managedObjectContext
if let moc = self.managedObjectContext {
}
fetchExcercises()
}
func fetchExcercises() {
let fetchRequest = NSFetchRequest(entityName: "Excercises")
// Create a sort descriptor object that sorts on the "excerciseName"
// property of the Core Data object
let sortDescriptor = NSSortDescriptor(key: "excerciseName", ascending: true)
// Set the list of sort descriptors in the fetch request,
// so it includes the sort descriptor
fetchRequest.sortDescriptors = [sortDescriptor]
if let fetchResults = managedObjectContext!.executeFetchRequest(fetchRequest, error: nil) as? [Excercises] {
excercises = fetchResults
}
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// How many rows are there in this section?
// There's only 1 section, and it has a number of rows
// equal to the number of excercises, so return the count
return excercises.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("Excercise Cell", forIndexPath: indexPath) as UITableViewCell
// Get the Excercises for this index
let excercise = excercises[indexPath.row]
// Set the title of the cell to be the title of the excercise
cell.textLabel!.text = excercise.excerciseName
cell.detailTextLabel!.text = "\(excercise.sets)x\(excercise.reps)"
cell.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator
return cell
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if(editingStyle == .Delete ) {
// Find the Excercise object the user is trying to delete
let excerciseToDelete = excercises[indexPath.row]
// Delete it from the managedObjectContext
managedObjectContext?.deleteObject(excerciseToDelete)
// Refresh the table view to indicate that it's deleted
self.fetchExcercises()
// Tell the table view to animate out that row
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
save()
}
}
// MARK: UITableViewDelegate
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let excercise = excercises[indexPath.row]
}
let addExcerciseAlertViewTag = 0
let addExcerciseTextAlertViewTag = 1
@IBAction func addExcerciseButton(sender: AnyObject) {
var namePrompt = UIAlertController(title: "Add Excercise",
message: "Enter Name",
preferredStyle: .Alert)
var excerciseNameTextField: UITextField?
namePrompt.addTextFieldWithConfigurationHandler {
(textField) -> Void in
excerciseNameTextField = textField
textField.placeholder = "Title"
}
namePrompt.addAction(UIAlertAction(title: "Ok",
style: .Default,
handler: { (action) -> Void in
if let textField = excerciseNameTextField {
self.saveNewItem(textField.text, workoutName: "Workout A")
}
}))
self.presentViewController(namePrompt, animated: true, completion: nil)
}
func saveNewItem(excerciseName : String, workoutName: String) {
// Create the new excercise item
var newExcercise = Excercises.createExcerciseInManagedObjectContext(self.managedObjectContext!, name: excerciseName)
println(excerciseName)
println(workoutName)
// Update the array containing the table view row data
self.fetchExcercises()
// Animate in the new row
// Use Swift's find() function to figure out the index of the newExcercise
// after it's been added and sorted in our Excercises array
if let newExcerciseIndex = find(excercises, newExcercise) {
// Create an NSIndexPath from the newExcerciseIndex
let newExcerciseIndexPath = NSIndexPath(forRow: newExcerciseIndex, inSection: 0)
// Animate in the insertion of this row
tableView.insertRowsAtIndexPaths([ newExcerciseIndexPath ], withRowAnimation: .Automatic)
save()
}
}
func save() {
var error : NSError?
if(managedObjectContext!.save(&error) ) {
println(error?.localizedDescription)
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "excerciseSettings" {
let ExcerciseSettingsDetailViewController = segue.destinationViewController as UIViewController
let indexPath = tableView.indexPathForSelectedRow()!
let excercise = excercises[indexPath.row]
let destinationTitle = excercise.excerciseName
ExcerciseSettingsDetailViewController.title = destinationTitle
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
1.The 转场结束。您的 ExcerciseMasterTableViewController 没有 属性 命名的锻炼。您需要添加
var workout:Workout!
到您的 ExcerciseMasterTableViewController。
你的序列应该看起来更像这样
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "excerciseMaster" {
let desitnationController = segue.destinationViewController as ExcerciseMasterTableViewController
let indexPath = tableView.indexPathForSelectedRow()!
let workout = workouts[indexPath.row]
destinationController.workout = workout
let destinationTitle = workout.workoutName
desitnationController.title = destinationTitle // Usually you would put this in the viewDidLoad method of the destinationController
}
}
然后在您的 ExcerciseMasterTableViewController 中添加练习时,只需设置他们的锻炼 属性
workout.exercises = exercises // See note in #2
- 为确保仅显示正确的练习,请在您的 viewDidLoad 中将您的练习数组设置为 workout.exercises。请注意,workout.exercises 应该是一个 NSSet,因此您需要将您的集合转换为数组,或者让练习的类型为 NSSet 而不是数组。