核心数据一对多关系不存储其数据 [英] Core Data One to many relation not storing its data
问题描述
我有一个基于表视图的应用程序,它有一个MasterTableViewController和DetailChildTableViewController。
MasterTableViewController在导航栏中有一个+,所以一个用户呈现一个视图在文本字段中输入该文件夹的名称。一旦用户完成,完成时钟,然后使用Core Data存储此文件夹,并在MasterTableViewController中显示此新文件夹。
这是特定的TableViewController的代码
$ b
MasterTableViewController
class MasterTableViewController: UITableViewController,NSFetchedResultsControllerDelegate {
让managedObjectContext =(UIApplication.sharedApplication()。delegate as!AppDelegate).managedObjectContext
var fetchedResultsController:NSFetchedResultsController = NSFetchedResultsController()
//填充获取的结果控制器
func getFetchedResultController() - > NSFetchedResultsController {
fetchedResultsController = NSFetchedResultsController(fetchRequest:taskFetchRequest(),managedObjectContext:managedObjectContext,sectionNameKeyPath:nil,cacheName:nil)
return fetchedResultsController
}
func taskFetchRequest() - > NSFetchRequest {
let fetchRequest = NSFetchRequest(entityName:Folder)
let sortDescriptor = NSSortDescriptor(key:name,ascending:true)
fetchRequest.sortDescriptors = [sortDescriptor]
return fetchRequest
}
}
override viewDidLoad(){
fetchedResultsController = getFetchedResultController )
fetchedResultsController.delegate = self
do {
try fetchedResultsController.performFetch()
} catch _ {
}
}
//表视图在Custom UITableViewCell和Segue方法中显示数据的方法....
此控制器中的其余代码只具有在表视图单元格中显示数据的方法,以及在用户点击新创建的单元格对象时将Core数据名称传递给下一个视图控制器的segue方法。
下面是在MasterTableViewController上创建要保存和显示的特定对象的代码
AddViewController >
class addItemTableViewController:UITableViewController {
@IBOutlet weak var FolderName:UITextField!
//使用相同的托管对象保存
让managedObjectContext =(UIApplication.sharedApplication()。delegate as!AppDelegate).managedObjectContext
@IBAction func Done(sender:AnyObject){
//调用函数将文件夹保存到核心数据
saveFolder()
self.dismissViewControllerAnimated(true,completion:nil )
}
//保存的函数
func saveFolder(){
let entityDescription = NSEntityDescription.entityForName(Folder,inManagedObjectContext:managedObjectContext )
let item = Folder(entity:entityDescription !, insertIntoManagedObjectContext:managedObjectContext)
item.name = FolderName.text!
// Passed颜色选择器视图的值低于
do {
try managedObjectContext.save()
print(Successfully Saved \\\
)
} catch _ {
}
}
}
因此,此代码基本上保存新输入的文件夹,并关闭文件夹名称视图,MasterTableViewController显示此新文件夹。
问题在于 DetailChildTableViewController ,它是在MasterTableViewController中创建的每个文件夹的细节。
下面是最终tableview控制器的代码
** DetailChildTableViewController **
code> class DetailChildTableViewController:UITableViewController,UIPopoverPresentationControllerDelegate,NSFetchedResultsControllerDelegate {
let managedObjectContext =(UIApplication.sharedApplication()。delegate as!AppDelegate).managedObjectContext
var fetchedResultsController: NSFetchedResultsController = NSFetchedResultsController()
//填充获取的结果控制器
func getFetchedResultController() - > NSFetchedResultsController {
fetchedResultsController = NSFetchedResultsController(fetchRequest:taskFetchRequest(),managedObjectContext:managedObjectContext,sectionNameKeyPath:nil,cacheName:nil)
return fetchedResultsController
}
func taskFetchRequest() - > NSFetchRequest {
let fetchRequest = NSFetchRequest(entityName:List)
let sortDescriptor = NSSortDescriptor(key:itemListName,ascending:false)
fetchRequest.sortDescriptors = [sortDescriptor]
return fetchRequest
}
override func viewDidLoad(){
super.viewDidLoad(){
//核心数据
fetchedResultsController = getFetchedResultController()
fetchedResultsController.delegate = self
do {
try fetchedResultsController.performFetch()
} catch _ {
}
self.tableView.reloadData()
}
// MARK: - 在表视图中显示结果
override func numberOfSectionsInTableView(tableView:UITableView) - > Int {
if let sections = fetchedResultsController.sections {
return sections.count
}
return 0
}
override func tableView(tableView:UITableView,numberOfRowsInSection section:Int) - > Int {
if let sections = fetchedResultsController.sections {
let currentSection = sections [section]
return currentSection.numberOfObjects
}
return 0
}
}
此DetailChildTableViewController也有+按钮,并提出一个类似的AddViewController添加数据到这个特定的文件夹与相同的Core Data方法。
问题是,当我创建2个实体在我的MasterTableViewController中,例如:
- Folder_One
- Folder_Two
,然后进入 Folder_Two ,并在此文件夹中创建一个列表对象,然后返回MasterTableViewController,然后选择 Folder_One 。
我仍然可以看到我在 Folder_Two 中创建的同一个列表对象。不清楚的问题?
这是我的数据模型:
TestApp.xcdatamodel
实体:
- 文件夹
-
列表
属性:
name )
列表实体 类型的 itemListName 关系:文件夹实体)
关系目标:列表
该列表实体未列为与文件夹具有关系。我设置文件夹与列表的关系为一对多关系。
问题是你的 DetailChildTableViewController
显示全部 列表
对象。您需要限制它才能获取与当前文件夹
相关的列表
对象。为此,您必须具有从列表
到文件夹
的关系。这种关系将是一对一,并且是从文件夹
到列表
的一对多关系的逆。 (你应该几乎总是为每个关系创建一个逆,即使你觉得你不需要它,无论如何创建它。在这种情况下,如下所示,你确实需要它)。所以你的实体模型应该看起来像这样:
文件夹列表
====== ====
name itemListName
lists< ------>>文件夹
在 DetailChildTableViewController
变量,将显示列表
的文件夹
:
var currentFolder:Folder?
要限制提取,因此只显示 List
与此文件夹相关的对象,请向获取请求中添加一个谓词:
如果let thisFolder = currentFolder {
fetchRequest.predicate = NSPredicate(format:folder ==%@,thisFolder)
}
当用户点击 MasterTableViewController
中的单元格时,在显示 DetailChildTableViewController
之前,必须为 currentFolder
。在 prepareForSegue
中,您将需要类似于:
let indexPath = self .tableView.indexPathForSelectedRow!
let currentFolder = self.fetchedResultsController.objectAtIndexPath(indexPath)as!文件夹
detailViewController.currentFolder = currentFolder
同样, AddListViewController
必须有一个 currentFolder
变量来了解新创建的列表
应属于(并且 DetailChildTableViewController
中的代码必须设置 currentFolder
)。在 AddListViewController
中,当您存储 List
项目时,需要设置关系:
newList.folder = currentFolder
请注意,当设置关系时,CoreData会自动设置反向关系(假设您定义了一个关系)。因此 newList
将被添加到 currentFolder
lists 属性>。
I have a Table view based app that has a MasterTableViewController and a DetailChildTableViewController.
MasterTableViewController has a + in the Nav bar so a user is presented with a view to enter a name for that Folder in a textfield. Once the user is done, the clock done and this folder is then stored using Core Data and displays this new folder in the MasterTableViewController.
Here is the code for this specific TableViewController
MasterTableViewController
class MasterTableViewController: UITableViewController, NSFetchedResultsControllerDelegate {
let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
var fetchedResultsController : NSFetchedResultsController = NSFetchedResultsController()
// Populate fetched results controller
func getFetchedResultController() -> NSFetchedResultsController {
fetchedResultsController = NSFetchedResultsController(fetchRequest: taskFetchRequest(), managedObjectContext: managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
return fetchedResultsController
}
func taskFetchRequest() -> NSFetchRequest {
let fetchRequest = NSFetchRequest(entityName: "Folder")
let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
return fetchRequest
}
}
override viewDidLoad(){
fetchedResultsController = getFetchedResultController()
fetchedResultsController.delegate = self
do {
try fetchedResultsController.performFetch()
} catch _ {
}
}
// Table View Methods to show data in Custom UITableViewCell and Segue method....
The rest of the code in this controller just has methods to display the data in the table view cell and a segue method that passes the Core Data name to the next view controller as the user taps the newly created cell object.
Here is the Code to Create this specific Object to save and display on MasterTableViewController
AddViewController
class addItemTableViewController: UITableViewController {
@IBOutlet weak var FolderName: UITextField!
// Use the same managed object to save
let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
@IBAction func Done(sender: AnyObject){
// Calling a function to save the folder to core data
saveFolder()
self.dismissViewControllerAnimated(true, completion: nil)
}
// Function that saves
func saveFolder(){
let entityDescription = NSEntityDescription.entityForName("Folder", inManagedObjectContext: managedObjectContext)
let item = Folder(entity: entityDescription!, insertIntoManagedObjectContext: managedObjectContext)
item.name = FolderName.text!
// Passed Value of the color picker table view below
do {
try managedObjectContext.save()
print("Successfully Saved \n")
} catch _ {
}
}
}
So this code basically saves the newly entered Folder and dismisses the folder name view and the MasterTableViewController shows this new folder.
The problem lies in the DetailChildTableViewController where it is the detail of each Folder created in MasterTableViewController.
Here is the code for the final tableview Controller
** DetailChildTableViewController**
class DetailChildTableViewController: UITableViewController, UIPopoverPresentationControllerDelegate, NSFetchedResultsControllerDelegate {
let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
var fetchedResultsController : NSFetchedResultsController = NSFetchedResultsController()
// Populate fetched results controller
func getFetchedResultController() -> NSFetchedResultsController {
fetchedResultsController = NSFetchedResultsController(fetchRequest: taskFetchRequest(), managedObjectContext: managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
return fetchedResultsController
}
func taskFetchRequest() -> NSFetchRequest {
let fetchRequest = NSFetchRequest(entityName: "List")
let sortDescriptor = NSSortDescriptor(key: "itemListName", ascending: false)
fetchRequest.sortDescriptors = [sortDescriptor]
return fetchRequest
}
override func viewDidLoad() {
super.viewDidLoad(){
// Core data
fetchedResultsController = getFetchedResultController()
fetchedResultsController.delegate = self
do {
try fetchedResultsController.performFetch()
} catch _ {
}
self.tableView.reloadData()
}
// MARK: - Display the results in table view
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if let sections = fetchedResultsController.sections {
return sections.count
}
return 0
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let sections = fetchedResultsController.sections {
let currentSection = sections[section]
return currentSection.numberOfObjects
}
return 0
}
}
This DetailChildTableViewController also has a + button in the navigation bar and brings up a similar AddViewController to add data to this specific folder with the same Core Data methods.
The problem is that when i create 2 entities in my MasterTableViewController, say for example:
- Folder_One
- Folder_Two
and i go into Folder_Two and create a list object in this folder and then go back to the MasterTableViewController and then select Folder_One, It still shows that same list object which i created in Folder_Two.
How can i approach a solution to this simple yet unclear problem?
Here is my Data model by the way:
TestApp.xcdatamodel
Entities:
- Folder
List
Atributes: name of type String (for Folder entity) itemListName of type String (for List entity)
Relationships: (for Folder entity) relation destination: List
Note that List entity is not listed as having a relationship to Folder. I set Folder to have relationship to List as a One to many relation.
What did i do wrong and how can i approach this?
The problem is that your DetailChildTableViewController
displays all the List
objects. You need to restrict it to fetch only the List
objects related to your current Folder
. To do this you must have a relationship from List
to Folder
. This relationship will be to-one, and will be the inverse of the to-many relationship from Folder
to List
. (You should almost always create an inverse for every relationship. Even if you feel you don't need it, create it anyway. In this case, as you'll see below, you do need it). So your entity model should look something like this:
Folder List
====== ====
name itemListName
lists <------>> folder
In your DetailChildTableViewController
, add a new variable which will be the Folder
for which you are displaying the Lists
:
var currentFolder : Folder?
To limit the fetch so it only displays the List
objects related to this folder, add a predicate to the fetch request:
if let thisFolder = currentFolder {
fetchRequest.predicate = NSPredicate(format:"folder == %@",thisFolder)
}
When the user taps a cell in the MasterTableViewController
, before showing the DetailChildTableViewController
, you must set the correct value for currentFolder
. In prepareForSegue
, you will need something like:
let indexPath = self.tableView.indexPathForSelectedRow!
let currentFolder = self.fetchedResultsController.objectAtIndexPath(indexPath) as! Folder
detailViewController.currentFolder = currentFolder
Similarly, the AddListViewController
must have a currentFolder
variable to know which Folder
the newly created List
should belong to (and the code in the DetailChildTableViewController
that segues to it must set the value for currentFolder
). In the AddListViewController
, when you store the List
item, you need to set the relationship:
newList.folder = currentFolder
Note that when you set a relationship, CoreData will automatically set the inverse relationship (assuming you defined one). So the newList
will be added to the lists
property of currentFolder
.
这篇关于核心数据一对多关系不存储其数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!