核心数据一对多关系不存储其数据 [英] Core Data One to many relation not storing its data

查看:200
本文介绍了核心数据一对多关系不存储其数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个基于表视图的应用程序,它有一个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



实体:


  1. 文件夹

  2. 列表



    属性:
    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:

  1. Folder
  2. 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆