UITableViewCell doesn't match sectionNameKeyPath property of NSFetchedResultsController - uitableview

I am confused with sectionNameKeyPath, because the tableViewCell are not seperated by the sectionNameKeyPath of fetchResultsController.
Hoping your answer,thanks in advance.
Core data table has a "ctype" property to store "0" or "1"
and UITableViewController is like this:
func configureCell(_ cell: UITableViewCell, withCate catalog:Cate) {
cell.textLabel!.text = catalog.emoji! + catalog.citem! + catalog.ctype.description
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let cataitem = fetchedResultsController.object(at: indexPath)
configureCell(cell, withCate:cataitem)
return cell
}
and the fetchResultsController just like this:
let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.viewContext, sectionNameKeyPath: "ctype", cacheName: "Money")
but the objects of the FetchResultController are not separated by ctype.

I have got it.
The reason is that I should add a "ctype" sortDescriptors as the first sortDescriptors

Related

CoreData, displaying saved journeys in a tableview

I am trying to display a list of all journeys recorded in a fitness-style app in a table view to show the distance (boolean) and date (timestamp) of each journey.
At the moment I have just created a variable to contain the Journeys from the core data file. When I print out the journeysArray, it shows 0 in the console even though there are some recorded journeys.
import UIKit
import CoreData
class SavedJourneysViewController: UITableViewController {
var journeyArray: [Journey] = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(journeyArray.count)
return journeyArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "JourneyItem", for: indexPath)
return cell
}
If Journey is your NSManagedObject subclass, you should use a NSFetchedResultsController to fetch the persisted objects.
Your SavedJourneysViewController must have a reference to the NSManagedObjectContext instance that you'll use to fetch your Journey objects. Let's assume that you have a viewContext property of type NSManagedObjectContext in your SavedJourneysViewController that's being set from the outside, wherever you initialize your SavedJourneysViewController.
You'll want to declare a fetchedResultsController in SavedJourneysViewController.
private lazy var fetchedResultsController: NSFetchedResultsController<Journey> = {
let fetchRequest: NSFetchRequest< Journey > = Journey.fetchRequest()
let sortDescriptor = NSSortDescriptor(keyPath: \Journey.date, ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: viewContext, sectionNameKeyPath: nil, cacheName: nil)
return fetchedResultsController
}()
Then perform fetch in viewDidLoad (for example) by calling try? fetchedResultsController.performFetch():
Then in numberOfRowsInSection return fetchedResultsController.sections?[section].objects?.count ?? 0:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return fetchedResultsController.sections?[section].objects?.count ?? 0
}
Don't forget about implementing func numberOfSections(in tableView: UITableView) -> Int and returning fetchedResultsController.sections?.count ?? 0:
func numberOfSections(in tableView: UITableView) -> Int {
return fetchedResultsController.sections?.count ?? 0
}
In cellForRowAt, configure your cell with a Journey object:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = let cell = tableView.dequeueReusableCell(withIdentifier: "JourneyItem", for: indexPath)
guard let journey = fetchedResultsController.sections?[indexPath.section].objects?[indexPath.row] as? Journey else {
return cell
}
// handle cell configuration
cell.textLabel?.text = String(journey.distance)
return cell
}
More about using NSFetchedResultsController with UITableViewController -
https://cocoacasts.com/populate-a-table-view-with-nsfetchedresultscontroller-and-swift-3
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CoreData/nsfetchedresultscontroller.html

Failed to execute method-- error:Value of type 'UITableViewCell' has no member 'getCell'

I'm trying to call a func in cell method but before that I need to call the func in View controller cell. Every time I try to call Xcode doesn't recognize it and an error appears. If I just copy it (Value of type 'UITableViewCell' has no member 'getCell').
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:TableViewCell1 = tableViewFullP1.dequeueReusableCell(withIdentifier: "cell01", for: indexPath) as! TableViewCell1
configureCell(cell, at: indexPath) // The one I want to call in the end
return cell
}
//Fetch Method P1
func fetchInfoToHomepage() {
let fetchRequest88: NSFetchRequest<Information> = Information.fetchRequest()
let sortOnDate = NSSortDescriptor(key: "date_info9", ascending: false)
fetchRequest88.sortDescriptors = [sortOnDate]
controllerItch = NSFetchedResultsController(fetchRequest: fetchRequest88, managedObjectContext: context77, sectionNameKeyPath: nil, cacheName: nil)
controllerItch.delegate = self
do {
try controllerItch.performFetch()
} catch {print("Failed fetch p1")}
}
// Sepcical func
func configureCell(_ cell: UITableViewCell, at indexPath: IndexPath) {
var malekHosny = controllerItch.object(at: indexPath)
cell.getCell(infos: malekHosny) //Here's the problem
}
Presumably your UITableViewCell subclass does have a getCell method. So you need to cast to that subclass:
(cell as! TableViewCell1).getCell...

multiple sections with different order using 1 NSFetchedResultsController

I have a tableview with two sections, "favorite" and "recent" to order stored contacts.
I would like to sort the favorite alphabetically, and the recent by date.
Each contact has a "name" and a "createdAt" value (and a value "favorite" if it is a favorite).
Using the sort below I have my sections "favorite" and "recent" with the correct cells but both sorted alphabetically.
...
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "favorite", ascending: false),
NSSortDescriptor(key: "firstName", ascending: true)]
fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: stack.viewContext, sectionNameKeyPath: "favorite", cacheName: nil)
...
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
guard let currentSection = fetchedResultsController.sections?[section] else { return nil }
if currentSection.name == "0" {
return "Recent".localized
} else {
return "Favorites".localized
}
}
How can I sort the section "recent" by date?
I solved it by manually changing the indexPath of the second section.
I have a function that return indexPath ordered differently for the second section.
func changeIndexPath (_ indexPath: IndexPath) -> IndexPath {
... //sort you want
}
Then I can use the functions of the tableview
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
...
newIndexPath = changeIndexPath(indexPath)
...
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
...
}
etc

How to create alphabetical section headers with NSFetchedResultsController - Swift 3

I am using NSFetchedResultsController to populate a tableView. The tableView can get quite long because it shows a list of people, and I want to sort it alphabetically. I know I need to use titleForHeaderInSection, but I am stuck on how to get the first letter of each object in my fetchedObjectsController.fetchedObjects and display that as the section header as well as sort it, just how the Contacts app works.
This is what my View Controller looks like.
var fetchedResultsController: NSFetchedResultsController<Client> = {
let fetchRequest: NSFetchRequest<Client> = Client.fetchRequest()
let sortDescriptors = [NSSortDescriptor(key: "name", ascending: false)]
fetchRequest.sortDescriptors = sortDescriptors
return NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: CoreDataStack.context, sectionNameKeyPath: "name", cacheName: nil)
}()
override func numberOfSections(in tableView: UITableView) -> Int {
guard let sections = fetchedResultsController.sections else { return 0 }
return sections.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard let sections = fetchedResultsController.sections else { return 0 }
return sections[section].numberOfObjects
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "clientCell", for: indexPath)
let client = fetchedResultsController.object(at: indexPath)
cell.textLabel?.text = client.name
return cell
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let client = fetchedResultsController.object(at: indexPath)
ClientController.sharedController.delete(client)
}
}
This is a very small example of how you can get your headers texts, I use a class only for test that have only name, then applying map using characters.prefix we get the first characters of the names and after casting to String and sorting we have what you need
var arrayOfUsers : [User] = [User(name:"test"),User(name:"pest"),User(name:"aest"),User(name:"nest"),User(name:"best")]
let finalArray = arrayOfUsers.map({String.init($0.name.characters.prefix(1)) }).sorted(by: {$0 < $1})
debugPrint(finalArray)
Console log result
["a", "b", "n", "p", "t"]
Hope this helps you

how to display 2 different prototype cells at different different sizes

UPDATE:
I went a different route. Heres what I would like to do. Design my app that lets me save core data and view it in another console in tableview. Once in the tableview console, I can also see a chart at the top of the console as well.
What I did:
I created a UIViewController, dragged over an imageview just to use that as an example. I also dragged in a tableview, cells...etc.
My Problem:
I can view the blank tableview cells and see the sample image. Once I save the core data and go back to try viewing the data, I get an error. I have the datasource and delegate implemented but, do I need to put that in my code.
class ViewMealsViewController: UIViewController, NSFetchedResultsControllerDelegate {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var menuButton: UIBarButtonItem!
let managedObjectContext = (UIApplication.shared.delegate as! AppDelegate).managedObjectContext
var fetchedResultController: NSFetchedResultsController<MealStats> = NSFetchedResultsController()
override func viewDidLoad() {
super.viewDidLoad()
fetchedResultController = getFetchedResultController()
fetchedResultController.delegate = self
do {
try fetchedResultController.performFetch()
} catch _ {
}
if revealViewController() != nil {
revealViewController().rearViewRevealWidth = 325
menuButton.target = revealViewController()
menuButton.action = #selector(SWRevealViewController.revealToggle(_:))
view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK:- Retrieve Stats
func getFetchedResultController() -> NSFetchedResultsController<MealStats> {
fetchedResultController = NSFetchedResultsController(fetchRequest: taskFetchRequest(), managedObjectContext: managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
return fetchedResultController
}
func taskFetchRequest() -> NSFetchRequest<MealStats> {
let fetchRequest = NSFetchRequest<MealStats> (entityName: "MealStats")
let timeSortDescriptor = NSSortDescriptor(key: "mealtype",
ascending: true, selector: #selector(NSString.caseInsensitiveCompare(_:)))
let milesSortDescriptor = NSSortDescriptor(key: "mealname",
ascending: true, selector: #selector(NSString.caseInsensitiveCompare(_:)))
fetchRequest.sortDescriptors = [timeSortDescriptor, milesSortDescriptor]
return fetchRequest
}
// MARK: - TableView data source
func numberOfSections(in tableView: UITableView) -> Int {
let numberOfSections = fetchedResultController.sections?.count
return numberOfSections!
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let numberOfRowsInSection = fetchedResultController.sections?[section].numberOfObjects
return numberOfRowsInSection!
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let mealstats = fetchedResultController.object(at: indexPath) as! MealStats
cell.textLabel?.text = mealstats.mealtype
cell.detailTextLabel!.text = mealstats.mealname
return cell
}
// MARK: - TableView DeleteĆ’
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
let managedObject:NSManagedObject = fetchedResultController.object(at: indexPath) as! NSManagedObject
managedObjectContext.delete(managedObject)
do {
try managedObjectContext.save()
} catch _ {
}
}
// MARK: - TableView Refresh
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.reloadData()
}
}
UIViewController with a sample image for an example
Error I get once I try to view saved core data in the tableview
Use UITableViewController
add two different cells to tableview on storyboard,
set two unique identifiers for them i.e
Cell No. 1 identifier : iden_1
Cell No. 2 identifier : iden_2
then in your class
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell
if(condition)
{
cell = tableView.dequeueReusableCell(withIdentifier: "iden_1", for: indexPath)
let stats = fetchedResultController.object(at: indexPath) as! Stats
cell.textLabel?.text = stats.type
cell.detailTextLabel!.text = stats.name
}
else{
cell = tableView.dequeueReusableCell(withIdentifier: "iden_2", for: indexPath)
let stats = fetchedResultController.object(at: indexPath) as! Stats
cell.textLabel?.text = stats.type
cell.detailTextLabel!.text = stats.name
}
return cell
}
and use this for identifying height for both cells.
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if(condition)
return 100
return 200
}

Resources