使用 CoreData 存储

Posted

技术标签:

【中文标题】使用 CoreData 存储【英文标题】:Storing with CoreData 【发布时间】:2015-07-29 21:34:35 【问题描述】:

我的应用程序非常简单:我有一个 tableview 来存储食谱 *names,并且对于每个食谱名称,另一个 tableview 包含每个食谱的几个 *ingredients。

我已经设法用 CoreData 保存了名称和成分,但问题是:当我按下添加新配方名称并进入成分表视图区域时,为之前的配方保存的成分就在那里!如何清除表格视图以开始一个新的?

另外,我的表格视图不会立即更新,我必须关闭应用程序并再次打开它。我该如何解决?

注意:如果我的问题太难理解,我可以贴一些代码!提前谢谢大家=)

编辑

代码:

import UIKit
import CoreData

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, NSFetchedResultsControllerDelegate 

    @IBOutlet var tableView: UITableView!
    var imageList: [UIImage] = []
    var moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
    var fetchedResultsController: NSFetchedResultsController?

    override func viewDidLoad() 
        fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchName(), managedObjectContext: moc!, sectionNameKeyPath: nil, cacheName: nil)
        fetchedResultsController?.delegate = self
        fetchedResultsController?.performFetch(nil)
        tableView.reloadData()
    

    func fetchName() -> NSFetchRequest 
        var fetchRequest = NSFetchRequest(entityName: "Details")
        let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
        fetchRequest.predicate = nil
        fetchRequest.sortDescriptors = [sortDescriptor]
        fetchRequest.fetchBatchSize = 20
        return fetchRequest
    


    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        return fetchedResultsController?.sections?[section].numberOfObjects ?? 0
    

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell 
        let cell = tableView.dequeueReusableCellWithIdentifier("recipeCell", forIndexPath: indexPath) as! UITableViewCell
        if let recipeCell = fetchedResultsController?.objectAtIndexPath(indexPath) as? Details 
            cell.textLabel?.text = recipeCell.name
        
        return cell 
    

-

import UIKit
import CoreData

class InfoViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UITableViewDelegate 


    @IBOutlet var nameField: UITextField!
    @IBOutlet var imageView: UIImageView!
    var moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext

    override func viewDidLoad() 
        super.viewDidLoad()

        //Pick the image by tap
        let tapGestureRecognizer: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "chooseImage:")
        tapGestureRecognizer.numberOfTapsRequired = 1
        imageView.addGestureRecognizer(tapGestureRecognizer)
        imageView.userInteractionEnabled = true

    

    //Pick the image by tapping, accessing the photoLibrary
    func chooseImage(recognizer: UITapGestureRecognizer) 
        let imagePicker: UIImagePickerController = UIImagePickerController()
        imagePicker.delegate = self
        imagePicker.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
        self.presentViewController(imagePicker, animated: true, completion: nil)
    

    //Put the selected image into the screen
    func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject:AnyObject]) 
        let pickedImage: UIImage = (info as NSDictionary).objectForKey(UIImagePickerControllerOriginalImage) as! UIImage
        // small picture
        let smallPicture = scaleImageWith(pickedImage, newSize: CGSizeMake(288,148))
        var sizeOfImageView:CGRect = imageView.frame
        sizeOfImageView.size = smallPicture.size
        imageView.frame = sizeOfImageView
        imageView.image = smallPicture
        picker.dismissViewControllerAnimated(true, completion: nil)
    


    func imagePickerControllerDidCancel(picker: UIImagePickerController) 
        picker.dismissViewControllerAnimated(true, completion: nil)
    

    func scaleImageWith(image:UIImage, newSize: CGSize) -> UIImage 
        UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)
        image.drawInRect(CGRectMake(0,0, newSize.width, newSize.height))
        let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return newImage
    

    @IBAction func addButton(sender: AnyObject) 

        let entityDescription = NSEntityDescription.entityForName("Details", inManagedObjectContext: moc!)
        let details = Details(entity: entityDescription!, insertIntoManagedObjectContext: moc)
        details.name = nameField.text

        var error: NSError?
        moc?.save(&error)

        if let err = error 
            var status = err.localizedFailureReason
            println("\(status)")
         else 
            println("Ingredient \(nameField.text) saved successfully!")
        
            if let navigation = navigationController 
            navigation.popViewControllerAnimated(true)

        
    

-

import UIKit
import CoreData

class IngredientListViewController: UIViewController, NSFetchedResultsControllerDelegate 

    @IBOutlet var tableView: UITableView!
    var moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
    var fetchedResultsController: NSFetchedResultsController?

    override func viewDidLoad() 
        super.viewDidLoad()
        fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchIngredient(), managedObjectContext: moc!, sectionNameKeyPath: nil, cacheName: nil)
        fetchedResultsController?.delegate = self
        fetchedResultsController?.performFetch(nil)
        tableView.reloadData()
    

    func fetchIngredient() -> NSFetchRequest 
        var fetchRequest = NSFetchRequest(entityName: "Ingredients")
        let sortDescriptor = NSSortDescriptor(key: "ingredients", ascending: true)
        fetchRequest.predicate = nil
        fetchRequest.sortDescriptors = [sortDescriptor]
        fetchRequest.fetchBatchSize = 20
        return fetchRequest
    

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        return fetchedResultsController?.sections?[section].numberOfObjects ?? 0
    

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell 
        let cell = tableView.dequeueReusableCellWithIdentifier("ingCell", forIndexPath: indexPath) as! UITableViewCell
        if let recipeCell = fetchedResultsController?.objectAtIndexPath(indexPath) as? Ingredients 
            cell.textLabel?.text = recipeCell.ingredients
        
        return cell
    

-

import UIKit
import CoreData

class IngredientViewController: UIViewController 


    @IBOutlet var nameField: UITextField!
    var moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext



    override func viewDidLoad() 
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    

    @IBAction func addButton(sender: AnyObject) 

        let entityDescription = NSEntityDescription.entityForName("Ingredients", inManagedObjectContext: moc!)
        let details = Ingredients(entity: entityDescription!, insertIntoManagedObjectContext: moc)
        details.ingredients = nameField.text

        var error: NSError?
        moc?.save(&error)

        if let err = error 
            var status = err.localizedFailureReason
            println("\(status)")
         else 
            println("Ingredient \(nameField.text) saved successfully!")
        
        if let navigation = navigationController 
            navigation.popViewControllerAnimated(true)

        
    

和模型:

import Foundation
import CoreData

class Ingredients: NSManagedObject 

    @NSManaged var ingredients: String
    @NSManaged var relationship: NSSet

import Foundation
import CoreData

class Details: NSManagedObject 

    @NSManaged var name: String
    @NSManaged var relationship: Ingredients


【问题讨论】:

【参考方案1】:

大纲:

    你需要修改你的模型:目前每个Details对象只能有一个Ingredients。我怀疑你需要这种关系“对许多人”,这样一个食谱就可以有很多成分。 您需要向您的IngredientListViewController 添加一个变量(Details? 类型)。这将代表所选配方。 (例如var chosenRecipe : Details?) 在fetchIngredient 中,您需要为提取添加一个谓词,以将结果限制为所选配方。例如。 fetch.predicate = NSPredicate(format:"ANY relationship == %@", chosenRecipe) 在 segue 到这个 VC 之前,您需要设置 chosenRecipe(可能在 prepareForSegue 中,或者在前面的表视图中的 didSelectRowAtIndexPath 中)。 要让您的电视自动更新,请使用fetchedResultsController 委托方法。 (你实现了这些吗?)

【讨论】:

我设法让我的表格视图工作,但我仍然不知道如何清除列表,因为当我添加新项目时,旧项目不会显示。完成后我会告诉你的! 我不知道如何设置CoreData在我开始一个新项目时清除所有数据:(你能教我吗? @NicholasPiccoli 能否再解释一下残差问题? 想象一下:我有一个包含多个项目的 tableview,每个项目都有自己的 tableview 和自己的项目。例如。当我添加第一个食谱并添加其成分时,一切正常,但是当我输入第二个食谱并尝试添加其新成分时,预览食谱的成分就在那里!等等。他们积累。我想学习每次添加新食谱时如何清除配料表。 注意:我必须重置它,但将其保存在内存中,因为稍后当我通过 didSelecRowAtIndexPath 访问添加的每个配方的行时,将显示一个屏幕,其中包含每个配方的详细信息。

以上是关于使用 CoreData 存储的主要内容,如果未能解决你的问题,请参考以下文章

使用 CoreData 存储

使用内置 CoreData 选项将 blob 存储在外部位置

CoreData:持久和临时存储

Coredata — 入门使用

使用 coredata 存储/缓存非标准数据类型

使用 CoreData 存储 JSON 字符串