Data stored fails to display in the table view, in one to many relationship of core data?

会有一股神秘感。 提交于 2019-12-12 03:26:25

问题


I used core data to make a one to many relationship where the scenario of my application is to display the members for the respected team. Both teams and members are added dynamically.

The issue occurs while storing the data at the member part. The data is getting stored but it won't get displayed in the table view. where I get this error.

data: { memberDesignation = ""; memberImage = nil; memberName = bbgbb; teams = nil; }) returned nil value for section name key path 'Teams.teams'. Object will be placed in unnamed section

I have done even a silly mistake as I'm new to core data. I can't figure it out what I'm doing wrong. I will show what I have done, rectify me what I did wrong. Please manipulate my code and give me the answer, no logical or random answers 'coz I don't have enough time to try out every possibilities.Help is very much appreciated, thanks in advance.

ER Model

Team table view controller

import UIKit
import CoreData

class GroupTable: UITableViewController, NSFetchedResultsControllerDelegate {

let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext

var teamData = [Teams]()

var fetchedResultsController : NSFetchedResultsController = NSFetchedResultsController()

let statusbarHeight: CGFloat = 20


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

override func viewDidLoad() {
    super.viewDidLoad()

    tableView.contentInset.top = statusbarHeight
    self.navigationItem.leftBarButtonItem!.image = UIImage(named: "Arrow")?.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal)

}

@IBAction func backToLogo(sender: AnyObject) {

    let previous = self.storyboard?.instantiateViewControllerWithIdentifier("Logo")
    self.presentViewController(previous!, animated: true, completion: nil)
}

override func viewWillAppear(animated: Bool) {

    let request = NSFetchRequest(entityName: "Teams")

    do{
        teamData = try managedObjectContext.executeFetchRequest(request) as! [Teams]
    } catch let error as NSError {
        print("\(error), \(error.userInfo)")
    }

    self.tableView.reloadData()

}


override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if teamData.count > 0{

        self.tableView.backgroundView = nil
        return teamData.count

    } else {
        let emptyLabel = UILabel(frame: CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height))
        emptyLabel.text = "No Teams available at the moment, create one!"
        emptyLabel.textAlignment = NSTextAlignment.Center
        self.tableView.backgroundView = emptyLabel
        self.tableView.separatorStyle = UITableViewCellSeparatorStyle.None
        return 0

    }

}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("groupCell", forIndexPath: indexPath) 

    let teamDetails = teamData[indexPath.row]

    cell.textLabel?.text = teamDetails.teamName
    cell.imageView?.image = teamDetails.teamImage as? UIImage
    return cell

}

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    // Get the new view controller using segue.destinationViewController.
    // Pass the selected object to the new view controller.
    if segue.identifier == "memberView" {
        let destination = segue.destinationViewController as! MemberTableViewController
        let indexPath = tableView.indexPathForSelectedRow!
        let selectedObject = fetchedResultsController.objectAtIndexPath(indexPath) as! Teams
        destination.currentTeam = selectedObject            
    }
  }  
}

Member table view controller

import UIKit
import CoreData


class MemberTableViewController: UITableViewController, NSFetchedResultsControllerDelegate {

let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext

var memberData = [Members]()

var currentTeam : Teams?

var fetchedResultsController: NSFetchedResultsController!

override func viewDidLoad() {
    super.viewDidLoad()

}

override func viewWillAppear(animated: Bool) {

    let request = NSFetchRequest(entityName: "Members")
    let members = NSSortDescriptor(key: "memberName", ascending: false)
    request.sortDescriptors = [members]
    if let thisTeam = currentTeam {

    request.predicate = NSPredicate(format:"teams == %@",thisTeam)
    }

    let fetchedResults = NSFetchedResultsController(fetchRequest: request, managedObjectContext: managedObjectContext, sectionNameKeyPath: "Teams.members", cacheName: nil)
    fetchedResults.delegate = self

    do {
        try fetchedResults.performFetch()
    } catch {
        fatalError("Failed to initialize FetchedResultsController: \(error)")
    }
    self.tableView.reloadData()

}


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

// MARK: - Table view data source

override func numberOfSectionsInTableView(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

    if memberData.count > 0{

        self.tableView.backgroundView = nil
        return memberData.count

    } else {
        let emptyLabel = UILabel(frame: CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height))
        emptyLabel.text = "No Members in the team, add one!"
        emptyLabel.textAlignment = NSTextAlignment.Center
        self.tableView.backgroundView = emptyLabel
        self.tableView.separatorStyle = UITableViewCellSeparatorStyle.None
        return 0

    }

}



override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("memberCell", forIndexPath: indexPath) as! ScreenThreeTableViewCell

    // Configure the cell...

    let memberDetails = memberData[indexPath.row]

    cell.memberName?.text = memberDetails.memberName
    cell.memberDesignation?.text = memberDetails.memberDesignation
    cell.memberImage?.image = memberDetails.memberImage as? UIImage

    return cell
}

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {


}

/*
// Override to support conditional editing of the table view.
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
    // Return false if you do not want the specified item to be editable.
    return true
}
*/

/*
// Override to support editing the table view.
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
    if editingStyle == .Delete {
        // Delete the row from the data source
        tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
    } else if editingStyle == .Insert {
        // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
    }    
}
*/

/*
// Override to support rearranging the table view.
override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {

}
*/

/*
// Override to support conditional rearranging of the table view.
override func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
    // Return false if you do not want the item to be re-orderable.
    return true
}
*/


// MARK: - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    // Get the new view controller using segue.destinationViewController.
    // Pass the selected object to the new view controller.      
  }
}

To Add teams

import UIKit
import CoreData

class ScreenTwoPopOverViewController: UIViewController, UIImagePickerControllerDelegate,UINavigationControllerDelegate,UIPopoverControllerDelegate {

@IBOutlet weak var teamNamePO: UITextField!
@IBOutlet weak var teamImagePO: UIImageView!
@IBOutlet weak var selectPicturePO: UIButton!


var picker:UIImagePickerController?=UIImagePickerController()
let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext

override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view.


}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}
@IBAction func submit(sender: AnyObject) {

    let entity = NSEntityDescription.entityForName("Teams", inManagedObjectContext: managedObjectContext)
    let team = Teams(entity: entity!, insertIntoManagedObjectContext: managedObjectContext)

    team.teamName = teamNamePO.text
    team.teamImage = teamImagePO.image

    do{
        try managedObjectContext.save()
    } catch let error as NSError{
        print("\(error), \(error.userInfo)")
    }

    dismissViewControllerAnimated(true, completion: nil)

}
@IBAction func cancel(sender: AnyObject) {

    dismissViewControllerAnimated(true, completion: nil)
}

@IBAction func btnImagePickerClicked(sender: AnyObject)
{
    let alert:UIAlertController=UIAlertController(title: "Choose Image", message: nil, preferredStyle: UIAlertControllerStyle.ActionSheet)

    let cameraAction = UIAlertAction(title: "Camera", style: UIAlertActionStyle.Default)
        {
            UIAlertAction in
            self.openCamera()

    }
    let gallaryAction = UIAlertAction(title: "Gallary", style: UIAlertActionStyle.Default)
        {
            UIAlertAction in
            self.openGallary()
    }
    let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel)
        {
            UIAlertAction in

    }

    // Add the actions
    picker?.delegate = self
    alert.addAction(cameraAction)
    alert.addAction(gallaryAction)
    alert.addAction(cancelAction)
    // Present the controller
    self.presentViewController(alert, animated: true, completion: nil)
}

func openCamera()
{
    if(UIImagePickerController .isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera))
    {
        picker!.sourceType = UIImagePickerControllerSourceType.Camera
        self .presentViewController(picker!, animated: true, completion: nil)
    }else {
        openGallary()
    }
}

func openGallary()
{
    picker!.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
    self.presentViewController(picker!, animated: true, completion: nil)
}

func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject])
{
    picker .dismissViewControllerAnimated(true, completion: nil)
    teamImagePO.image=info[UIImagePickerControllerOriginalImage] as? UIImage
}

func imagePickerControllerDidCancel(picker: UIImagePickerController)
{
    print("picker cancel.")
    self.presentViewController(self, animated: true, completion: nil)
}



/*
// MARK: - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    // Get the new view controller using segue.destinationViewController.
    // Pass the selected object to the new view controller.
}
*/

}

To add members

import UIKit
import CoreData

class ScreenThreePopOverViewController: UIViewController,UIImagePickerControllerDelegate,UINavigationControllerDelegate,UIPopoverControllerDelegate {

@IBOutlet weak var memberDesignationPO: UITextField!
@IBOutlet weak var memberNamePO: UITextField!
@IBOutlet weak var memberImagePO: UIImageView!

var picker:UIImagePickerController?=UIImagePickerController()
let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext

var currentTeam : Teams?


override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view.
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}
@IBAction func selectPicture(sender: AnyObject) {

    let alert:UIAlertController=UIAlertController(title: "Choose Image", message: nil, preferredStyle: UIAlertControllerStyle.ActionSheet)

    let cameraAction = UIAlertAction(title: "Camera", style: UIAlertActionStyle.Default)
        {
            UIAlertAction in
            self.openCamera()

    }
    let gallaryAction = UIAlertAction(title: "Gallary", style: UIAlertActionStyle.Default)
        {
            UIAlertAction in
            self.openGallary()
    }
    let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel)
        {
            UIAlertAction in

    }

    // Add the actions
    picker?.delegate = self
    alert.addAction(cameraAction)
    alert.addAction(gallaryAction)
    alert.addAction(cancelAction)
    // Present the controller
    self.presentViewController(alert, animated: true, completion: nil)
}

func openCamera()
{
    if(UIImagePickerController .isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera))
    {
        picker!.sourceType = UIImagePickerControllerSourceType.Camera
        self .presentViewController(picker!, animated: true, completion: nil)
    }else {
        openGallary()
    }
}

func openGallary()
{
    picker!.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
    self.presentViewController(picker!, animated: true, completion: nil)
}

func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject])
{
    picker .dismissViewControllerAnimated(true, completion: nil)
    memberImagePO.image=info[UIImagePickerControllerOriginalImage] as? UIImage
}

func imagePickerControllerDidCancel(picker: UIImagePickerController)
{
    print("picker cancel.")
            dismissViewControllerAnimated(true, completion: nil)
}


@IBAction func cancel(sender: AnyObject) {

    dismissViewControllerAnimated(true, completion: nil)

}
@IBAction func submit(sender: AnyObject) {

    let entity = NSEntityDescription.entityForName("Members", inManagedObjectContext: managedObjectContext)
    let member = Members(entity: entity!, insertIntoManagedObjectContext: managedObjectContext)

    member.memberName = memberNamePO.text
    member.memberDesignation = memberDesignationPO.text
    member.memberImage = memberImagePO.image

if let team  = currentTeam {
        member.teams = team
    }        
    do{
        try managedObjectContext.save()
    } catch let error as NSError{
        print("\(error), \(error.userInfo)")
    }
    dismissViewControllerAnimated(true, completion: nil)


}


/*
// MARK: - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    // Get the new view controller using segue.destinationViewController.
    // Pass the selected object to the new view controller.
}
*/

}

Team entity

extension Teams {

@NSManaged var teamImage: NSObject?
@NSManaged var teamName: String?
@NSManaged var members: NSSet?

}

Members entity

extension Members {

@NSManaged var memberDesignation: String?
@NSManaged var memberImage: NSObject?
@NSManaged var memberName: String?
@NSManaged var teams: Teams?

}

I'm very much confused at, how to add the members with respect to the teams?! How could i resolve this? For a better understanding about the scenario you could refer to this question Can I add an NSManagedObject to the selected parent object just using Interface Builder and Core Data? and also you could refer to this https://github.com/pbasdf/DemoMasterDetail which is exactly how I would like my app to produce the output.


回答1:


Hi Praveen Kumar you can do something like this :)

@IBAction func submit(sender: AnyObject) {

    let entity = NSEntityDescription.entityForName("Members", inManagedObjectContext: managedObjectContext)
    let member = Members(entity: entity!, insertIntoManagedObjectContext: managedObjectContext)

    member.memberName = memberNamePO.text
    member.memberDesignation = memberDesignationPO.text
    member.memberImage = memberImagePO.image

     member.setValue(currentTeam, forKey: "teams") //mistake you did is you were trying to set the wrong relationship :) member has a relationship to teams and is called teams. So you can access member.teams not member.members 

    //or
    member.teams = currentTeam

    do{
        try managedObjectContext.save()
    } catch let error as NSError{
        print("\(error), \(error.userInfo)")
    }
    dismissViewControllerAnimated(true, completion: nil)


}

TIP

Relation ship names should clearly explain the link between two entities. Rather then giving simply names like members & teams make a much sensible names :)

Teams -> contains -> Members Members -> Belongs_To -> Teams

So Team entity should have one to many relation ship to Members with name Contains. So when one sees it and reads it will be Team contains members :)

Similarly Members entity should have one to one relation ship to Teams with name Belongs_To. So when one sees it and reads it will be member belongs to Team :)

Hope my answer helped you :)



来源:https://stackoverflow.com/questions/37074847/data-stored-fails-to-display-in-the-table-view-in-one-to-many-relationship-of-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!