简体   繁体   中英

core data relationships in creating a favorite button in swift

I need some help creating a favorite button in swift. I have been trying to study how to use core data, and i thought that i had a good handle on it, expect i am struggling to use it to create the favorite button. I have a sample project that has two views, a table view with a few numbers and a view controller that has the button. 这就是它的样子

this is the core data part of the application 核心数据

after watching a video i was told to take the core data functions that save the data from the appDelegate and put it in a new view, which i call PersistenceService.swift

import Foundation
import CoreData

class PersistenceService {

    private init() {}

    static var context: NSManagedObjectContext {
        return persistentContainer.viewContext
    }

    static  var persistentContainer: NSPersistentContainer = {
        /*
         The persistent container for the application. This implementation
         creates and returns a container, having loaded the store for the
         application to it. This property is optional since there are legitimate
         error conditions that could cause the creation of the store to fail.
         */
        let container = NSPersistentContainer(name: "otherButton")
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                // Replace this implementation with code to handle the error appropriately.
                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

                /*
                 Typical reasons for an error here include:
                 * The parent directory does not exist, cannot be created, or disallows writing.
                 * The persistent store is not accessible, due to permissions or data protection when the device is locked.
                 * The device is out of space.
                 * The store could not be migrated to the current model version.
                 Check the error message to determine what the actual problem was.
                 */
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        return container
    }()

    // MARK: - Core Data Saving support

    static func saveContext () {
        let context = persistentContainer.viewContext
        if context.hasChanges {
            do {
                try context.save()
                print("SAVED")
            } catch {
                // Replace this implementation with code to handle the error appropriately.
                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
                let nserror = error as NSError
                fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
            }
        }
    }
}

the first view, the tableview, is simple, i just created a table with three cells that when pressed go to a new view.

idTableView.swift

import UIKit
import CoreData

class idTableView: UITableViewController {


    var idNumber = [1, 2, 3]

    override func viewDidLoad() {
        super.viewDidLoad()

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        self.performSegue(withIdentifier: "segue", sender: indexPath)
    }

    override func numberOfSections(in tableView: UITableView) -> Int {
        // #warning Incomplete implementation, return the number of sections
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // #warning Incomplete implementation, return the number of rows
        return idNumber.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! idCell
        cell.idCell.text = "\(idNumber[indexPath.row])"
        return cell
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "segue" {
            let nxtVC = segue.destination as? ViewController
            let myIndexPath = self.tableView.indexPathForSelectedRow!
            let row = myIndexPath.row
            nxtVC?.idNumber = idNumber[row]
        }
    }

When the cell from idTableView.swift is pressed it takes you to a new view with the single empty star button in the middle.

ViewController.swift

import UIKit
import CoreData

class ViewController: UIViewController {

    var buttonIsSelected = false

    @IBOutlet var navigation: UINavigationItem!
    var idNumber: Int?

    @IBOutlet var onOffButton: UIButton!
    let image1 = UIImage(named: "empty") as UIImage?
    let image2 = UIImage(named: "filled") as UIImage?

    override func viewDidLoad() {
        super.viewDidLoad()
        getId()

    }
//button Action
    @IBAction func onOffButtonPressed(_ sender: Any) {
        buttonIsSelected = !buttonIsSelected
        if buttonIsSelected == true {
            getFavorite(bool: buttonIsSelected)
            print("favorited")
            onOffButton.setImage(image2, for: .normal)
        } else if buttonIsSelected == false {
            getFavorite(bool: buttonIsSelected)
            print("unfavorited")
            onOffButton.setImage(image1, for: .normal)
        }
    }
    func getId() {
        if let id = idNumber {
            let newNumber = NSEntityDescription.insertNewObject(forEntityName: "IdNumber", into: PersistenceService.context) as! IdNumber
            let newBool = NSEntityDescription.insertNewObject(forEntityName: "Favorite", into: PersistenceService.context) as! Favorite
            newNumber.setValue(Int16(id), forKey: "idNumber")
            newBool.setValue(buttonIsSelected, forKey: "favorite")
            newNumber.favorite = newBool
            print("newNumber: \(newNumber.favorite)")
            print("newBool: \(newBool)")
            PersistenceService.saveContext()
            //self.navigationItem.title = "\(id)"
        }
    }

    func getFavorite(bool: Bool) {
        let newFavorite = NSEntityDescription.insertNewObject(forEntityName: "Favorite", into: PersistenceService.context)
        newFavorite.setValue(bool, forKey: "favorite")
        PersistenceService.saveContext()
    }

The thing i am seeing for core data is something like creating a workplace app where there are departments which are the parent and then people which populate the department. These apps create adepartment and then add people into that department that then populate a tableView. What i am trying to do is different because my parent in this case is the id number and each id number has only one bool. When the bool is changed it changes the image of the button to a filled star. What i am having trouble doing is saving each bool to each id number. For example i have three id numbers, and each of those three id numbers can have a single bool, like 1: true, 2: false. 3: true. The default for each id number is false but when the button is pressed it should save the change of the bool which in turn changes the image of the button.

I think the problem i am having is getting the relationship right in the code. It seems that i can do it for a single view, but when i go to a different view the app either crashes or something else, but the button state that i saved before changes to what it's default state is, which is false/ empty star button.

If anyone has a question i will answer to the best of my ability. Thank you for the help.

I would design that in a different way: For your purposes, you just need a single CD Object, that have the attributes idNumber and a Bool isFavorite . When you load your tableView , you will fetch all related Objects from CD and hold them in your idNumber Array. Then you can easily access the Array Element for the selected TableView Cell and simply set isFavorite and then save your CD context.

If you want to get all objects with isFavorite == true , you can simply perform a fetch with a Request that have a Predicate for this...

And way you create every time when getID is called a new object? once created, you want to reuse it of course... Otherwise you will have multiple objects with the same idNumber...

By the way: its totally unnecessary to create IDs for CD objects by yourself. All CD Objects have a unique id by default. In CD, you just take care about your natural properties and relations.

Set UIbuton image from the storyboard for selected and default button.

在此处输入图片说明 在此处输入图片说明

Then Select and Unselect manage programmatically.

func btnfavoriteClicked(_ sender: UIButton) {
     sender.isSelected = !sender.isSelected
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM