Sectioning Tableview and Rows with Core Data Swift

Displaying data in sections in a Table View using Core Data in Swift

As Wain has said, the FRC doesn't help much. An FRC has two main advantages, over and above an ordinary fetch: it can manage the sections for you, and it can automatically handle inserts/deletes/updates, via its delegate methods. The first is of no use in your case, since you want one section per Order. And the second is of little help since the FRC will watch only one Entity for changes, yet your table view is built from three.

Nevertheless, I think you might be able to use an FRC to some effect. First, do not bother with sectionNameKeyPath: you have a one-one mapping between Orders and table view sections, so you can use the tableview's section as an index on the FRC's fetchedObjects to identify the order for each section. The numberOrRowsInSection can then be found by summing the count of products and services for the relevant Order. The messy (and possibly slow) bit is mapping the table view's row to the correct element of either products or services.

import UIKit
import CoreData

class ExampleViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, NSFetchedResultsControllerDelegate {

@IBOutlet weak var tableView: UITableView!

let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext!
var fetchedResultController: NSFetchedResultsController = NSFetchedResultsController()

var orders = [Order]()
var startDate : NSDate = NSDate()
var endDate : NSDate = NSDate()

override func viewDidLoad() {
super.viewDidLoad()
fetchedResultController.delegate = self
tableView.dataSource = self
tableView.delegate = self

fetchData()
}

func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return fetchedResultController.fetchedObjects!.count
}

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let order = fetchedResultController.fetchedObjects![section] as! Order
return ((order.products?.count ?? 0) + (order.services?.count ?? 0))
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let textCellIdentifier = "ExampleTableViewCell"
let row = indexPath.row

let cell = tableView.dequeueReusableCellWithIdentifier(textCellIdentifier, forIndexPath: indexPath) as! ExampleTableViewCell

let order = fetchedResultController.fetchedObjects![indexPath.section] as! Order // Data fetched using NSFetchedResultsController
let products = (order.products?.allObjects ?? [Product]()) as! [Product] // Swift Array
let services = (order.services?.allObjects ?? [Service]()) as! [Service] // Swift Array

if (row < products.count) { // this is a Product row
cell.orderLabel.text = products[row].name!
} else { // this is a Service row
cell.orderLabel.text = services[row-products.count].name!
}
return cell
}

func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
let order = fetchedResultController.fetchedObjects![section] as! Order
return "\(order.date)"
}

func orderFetchRequest() -> NSFetchRequest {
let fetchRequest = NSFetchRequest(entityName: "Order")
let sortDescriptor = NSSortDescriptor(key: "date", ascending: true)
let predicate = NSPredicate(format: "date >= %@ AND date <= %@", startDate, endDate) // startDate and endData are defined elsewhere

fetchRequest.sortDescriptors = [sortDescriptor]
fetchRequest.predicate = predicate

return fetchRequest
}

func fetchData() {
let fetchRequest = orderFetchRequest()
fetchedResultController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedObjectContext, sectionNameKeyPath:nil, cacheName: nil)

do {
try fetchedResultController.performFetch()
}
catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
}
}

There are probably neater ways to code this; I'm not a Swift aficionado.

If you use the FRC delegate methods, you can at least watch for new/deleted Orders and add/delete sections to the TV as necessary, and you could use updates to reload the relevant section.

Separate array from core-data in tableView Sections

The problem is here

cell.textLabel?.text = players2[indexPath.row].names

You are only looking at the row number and are ignoring the section number. So with your example of 10 and 2 the row will always be between 0 and 4.

So you need to do something like (not tested):

let rowsPerSection = players2.count / numberOfTeams
let rowInSection = indexPath.row + rowsPerSection * indexPath.section

cell.textLabel?.text = players2[rowInSection].names

Sectioning TableView and rows with Core Data Swift

Just a hint: If you're using CoreData and UiTableView use NSFetchedResultsController to make things much, much easier. If you're searching for a starting point & sample code - just create a new master-detail-application project in Xcode and turn on "Use Core Data" in the dialog.

Now, straight to your question: an example "implementation" of the NSFetchResultsController:

var fetchedResultsController: NSFetchedResultsController {

  if _fetchedResultsController != nil {
return _fetchedResultsController!
}
let fetchRequest = NSFetchRequest()
// Edit the entity name as appropriate.
let entity = NSEntityDescription.entityForName("Event", inManagedObjectContext: self.managedObjectContext!)
fetchRequest.entity = entity

// Set the batch size to a suitable number.
fetchRequest.fetchBatchSize = 20

// Edit the sort key as appropriate.
let sectionSortDescriptor = NSSortDescriptor(key: "startDate", ascending: true)
let secondSortDescriptor = NSSortDescriptor(key: "title", ascending: true)

let sortDescriptors = [sectionSortDescriptor, secondSortDescriptor]

fetchRequest.sortDescriptors = sortDescriptors

// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext!, sectionNameKeyPath: "startDate", cacheName: nil)
aFetchedResultsController.delegate = self
_fetchedResultsController = aFetchedResultsController

var error: NSError? = nil
if !_fetchedResultsController!.performFetch(&error) {
// Replace this implementation with code to handle the error appropriately.
// abort() 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.
//println("Unresolved error \(error), \(error.userInfo)")
abort()
}

return _fetchedResultsController!
}

// MARK: - Table view data source

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return self.fetchedResultsController.sections?.count ?? 0
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let sectionInfo = self.fetchedResultsController.sections![section] as NSFetchedResultsSectionInfo
return sectionInfo.numberOfObjects
}

CoreData with tableView Sections

You need to base the frc on the Item entity, and use the sectionNameKeyPath to reference the Category name:

var fetchResultsController: NSFetchedResultsController<Item>!
let request: NSFetchRequest<Item> = Item.fetchRequest()
request.sortDescriptors = [NSSortDescriptor(key: "list.name", ascending: true)]
fetchResultsController = NSFetchedResultsController<Item>(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: “list.name”, cacheName: nil)
try? fetchResultsController?.performFetch()
fetchResultsController?.delegate = self
tableView.reloadData()

Populate Table View Sections with CoreData and Array

Here's a sample project that has your basic setup - https://github.com/nambatee/CombinedFolders1

The trick is to adjust the indexPath. You're working with 2 sections but your NSFetchedResultsController knows of only 1 section. So you need to be careful not to ask NSFetchedResultsController for objects in section at index 1.

Deleting a table view row with Swift 3 and CoreData

When you delete data in coreData it wont be saved by coreData unless you save the action. The delete action should be like this..

func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
managedObjectContext.delete(sessions[indexPath.row])
do {
try managedObjectContext.save()
tableView.reloadData()
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
}
}

How can I sort and show in correct section of a UITableview in swift using CoreData results

Ok I figured it out, phew was this confusing and took a lot of research.

Okay, so first thing you have to do is create a transient property on the data model. In my case I called it lettersection. To do this in the entity just create a new attribute and call it lettersection and in graph mode if you select it (double click it), you will see option in inspector for 'transient'. This means it won't be saved to the database and is used more for internal reasons.

You then need to manually set up the variable in the extension area of the model definition. Here is how it looks for me.

import Foundation
import CoreData

extension Inventory {

@NSManaged var addCount: NSNumber?
@NSManaged var barcode: String?
@NSManaged var currentCount: NSNumber?
@NSManaged var id: NSNumber?
@NSManaged var imageLargePath: String?
@NSManaged var imageSmallPath: String?
@NSManaged var name: String?
@NSManaged var negativeCount: NSNumber?
@NSManaged var newCount: NSNumber?
@NSManaged var store_id: NSNumber?
@NSManaged var store: Store?

var lettersection: String? {
let characters = name!.characters.map { String($0) }
return characters[0].uppercaseString
}

}

Once you do this, you simply call this new 'lettersection' with the fetchedResultsController like so...

let frc = NSFetchedResultsController(
fetchRequest: inventoryFetchRequest,
managedObjectContext: self.moc,
sectionNameKeyPath: "lettersection",
cacheName: nil)

and everything will work! It sorts by the name of my inventory items, but groups them by the first letters, for a nice A,B,C type list!

CoreData entities to UITableView with sections

I don't think this code is going to do what you want. Typically the NSFetchRequest that you use to construct the NSFetchedResultsController that populates a UITableView or UICollectionView must fetch the entity type you actually want to display (or things become complicated). Have you tried something like this?

let appDel: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context: NSManagedObjectContext = appDel.managedObjectContext

let fetchRequest = NSFetchRequest(entityName: "Word")

let letterSort =
NSSortDescriptor(key: "letter.letterName", ascending: true)
let wordSort = NSSortDescriptor(key: "word", ascending: true)
fetchRequest.sortDescriptors = [letterSort, wordSort]

fetchedResultsController =
NSFetchedResultsController(fetchRequest: fetchRequest,
managedObjectContext: context,
sectionNameKeyPath: "letter.letterName",
cacheName: "dict")

fetchedResultsController.delegate = self

do {
try fetchedResultsController.performFetch()
} catch let error as NSError {
print("Error: \(error.localizedDescription)")
}

Assuming that your word values and associated letters are correct, I think this should do what you want. It's also worth noting that the cacheName must be unique in your application, so you might want something more complex than simply "dict".



Related Topics



Leave a reply



Submit