Trouble with multiple-sectioned TableView in Swift - ios

I apologize in advance if this is confusing. I'm new to Swift with no prior experience and learning.
Basically, I have a UITableView that pulls its data form another viewController with four text fields. The data is saved and added to the tableView (only displaying the two of the fields in the cell). When the cell is selected it segues back to the viewController and adds the data in the appropriate text fields. This also utilized CoreData and NSFetchResultsController. All of this is working properly so far, just wanted to give a little back ground on whats going on.
The Problem...
I am trying to get the tableView to display the data in two sections. I want the first section (section 0) to display the data added to the list created by adding the text fields on the viewController. However, the tableView adds first row as the first section (with the header correct) and then adds the second row (and on) as the second section (section 1)(with section header). I want to add all new items from the viewController to section 0, leaving section 1 blank for when I figure out how to cross items off section 0 and move to section 1 by selecting the row (Coming in the future).
I am new to this site as well and could not figure out how to post my code. If anyone needs to look through it you may have to walk me through adding it. Also, I have trouble converting Objective C to Swift please keep that in mind while answering. Thank you!! and I apologize for being difficult.
(tableView Controller "SList")
class ShoppingList: UIViewController, NSFetchedResultsControllerDelegate, UITableViewDataSource, UITableViewDelegate {
let moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
var frc : NSFetchedResultsController = NSFetchedResultsController()
func itemFetchRequest() -> NSFetchRequest{
let fetchRequest = NSFetchRequest(entityName: "SList")
let primarySortDescription = NSSortDescriptor(key: "slitem", ascending: true)
let secondarySortDescription = NSSortDescriptor(key: "slcross", ascending: true)
fetchRequest.sortDescriptors = [primarySortDescription, secondarySortDescription]
return fetchRequest
}
func getFetchRequetController() ->NSFetchedResultsController{
frc = NSFetchedResultsController(fetchRequest: itemFetchRequest(), managedObjectContext: moc, sectionNameKeyPath: "slitem", cacheName: nil)
return frc
}
#IBOutlet weak var tableView: UITableView!
#IBAction func AddNew(sender: AnyObject) {
frc = getFetchRequetController()
frc.delegate = self
do {
try frc.performFetch()
} catch _ {
print("Failed to perform inital fetch.")
return
}
self.tableView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
frc = getFetchRequetController()
frc.delegate = self
do {
try frc.performFetch()
} catch _ {
print("Failed to perform inital fetch.")
return
}
self.tableView.reloadData()
//TableView Background Color
self.tableView.backgroundColor = UIColor.clearColor()
tableView.reloadData()
self.navigationItem.leftBarButtonItem = self.editButtonItem()
self.tableView.separatorColor = UIColor.blackColor()
}
override func viewDidDisappear(animated: Bool) {
frc = getFetchRequetController()
frc.delegate = self
do {
try frc.performFetch()
} catch _ {
print("Failed to perform inital fetch.")
return
}
self.tableView.reloadData()
}
//TableView Data
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
let managedObject:NSManagedObject = frc.objectAtIndexPath(indexPath) as! NSManagedObject
moc.deleteObject(managedObject)
do {
try moc.save()
} catch _ {
print("Failed to save.")
return
}
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
let numberOfSections = frc.sections?.count
return numberOfSections!
}
//table section headers
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String?{
let sectionHeader = "Items - #\(frc.sections![section].numberOfObjects)"
let sectionHeader1 = "Crossed Off Items - #\(frc.sections![section].numberOfObjects)"
if (section == 0) {
return sectionHeader
}
if (section == 1){
return sectionHeader1
}else{
return nil
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let numberOfRowsInSection = frc.sections?[section].numberOfObjects
return numberOfRowsInSection!
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
let items = frc.objectAtIndexPath(indexPath) as! SList
cell.backgroundColor = UIColor.clearColor()
cell.textLabel?.text = "\(items.slitem!) - Qty: \(items.slqty!)"
cell.textLabel?.font = UIFont.systemFontOfSize(23)
return cell
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func controllerDidChangeContent(controller: NSFetchedResultsController) {
tableView.reloadData()
}
//segue to add/edit
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "edit" {
let cell = sender as! UITableViewCell
let indexPath = tableView.indexPathForCell(cell)
let SListController:SLEdit = segue.destinationViewController as! SLEdit
let items:SList = frc.objectAtIndexPath(indexPath!) as! SList
SListController.item = items
}
}
}
(ViewController "SLEdit")
class SLEdit: UIViewController {
let moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
#IBOutlet weak var slitem: UITextField!
#IBOutlet weak var sldesc: UITextField!
#IBOutlet weak var slqty: UITextField!
#IBOutlet weak var slprice: UITextField!
var item: SList? = nil
override func viewDidLoad() {
super.viewDidLoad()
if item != nil{
slitem.text = item?.slitem
sldesc.text = item?.sldesc
slqty.text = item?.slqty
slprice.text = item?.slprice
}
// "x" Delete Feature
self.slitem.clearButtonMode = UITextFieldViewMode.WhileEditing
self.sldesc.clearButtonMode = UITextFieldViewMode.WhileEditing
self.slqty.clearButtonMode = UITextFieldViewMode.WhileEditing
self.slprice.clearButtonMode = UITextFieldViewMode.WhileEditing
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func dismissVC() {
navigationController?.popViewControllerAnimated(true)
}
#IBAction func saveButton(sender: AnyObject) {
if item != nil {
edititems()
} else {
createitems()
}
dismissVC()
}
func createitems() {
let entityDescription = NSEntityDescription.entityForName("SList", inManagedObjectContext: moc)
let item = SList(entity: entityDescription!, insertIntoManagedObjectContext: moc)
item.slitem = slitem.text
item.sldesc = sldesc.text
item.slqty = slqty.text
item.slprice = slprice.text
if slitem.text == nil{
createitems()
}else{
edititems()
}
do {
try moc.save()
} catch _ {
return
}
}
func edititems() {
item?.slitem = slitem.text!
item?.sldesc = sldesc.text!
item?.slqty = slqty.text!
item?.slprice = slprice.text!
do {
try moc.save()
} catch {
return
}
}
}

I think the problem lies in your FRC configuration:
frc = NSFetchedResultsController(fetchRequest: itemFetchRequest(), managedObjectContext: moc, sectionNameKeyPath: "slitem", cacheName: nil)
Because sectionNameKeyPath is slitem, the FRC creates a new section for each different value of slitem.
If the slcross attribute is used to indicate that the item has been crossed off the list, specify that as the sectionNameKeyPath:
frc = NSFetchedResultsController(fetchRequest: itemFetchRequest(), managedObjectContext: moc, sectionNameKeyPath: "slcross", cacheName: nil)
You will also need to modify the sorting of the fetch request (it MUST be sorted so that all items in a given section are together):
let primarySortDescription = NSSortDescriptor(key: "slcross", ascending: true)
let secondarySortDescription = NSSortDescriptor(key: "slitem", ascending: true)
fetchRequest.sortDescriptors = [primarySortDescription, secondarySortDescription]

Related

Sorting fetch results by Bool attribute in CoreData Swift 4

I am creating a ToDo list app and would like to sort the tableviewcontroller by priority. if the task is important i would like it on top of the todo list, if it is not important than i would like it under/after the important tasks. I have been trying to figure out how to sort this but have not been able to. Appreciate some help.
class ToDoTableViewController: UITableViewController {
var toDos : [ToDoCoreData] = []
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
getToDos()
}
func getToDos() {
if let context = (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer.viewContext{
if let coreDataToDos = try? context.fetch(ToDoCoreData.fetchRequest()) as? [ToDoCoreData] {
if let theToDos = coreDataToDos {
toDos = theToDos
tableView.reloadData()
}
}
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
let toDo = toDos[indexPath.row]
if let name = toDo.name {
if toDo.important {
cell.textLabel?.text = "❗️" + name
} else {
cell.textLabel?.text = toDo.name
}
}
return cell
}
Update getToDos() function like that:
func getToDos() {
if let context = (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer.viewContext{
let fetchRequest: NSFetchRequest<ToDoCoreData> = ToDoCoreData.fetchRequest()
let descriptors = [NSSortDescriptor(key: "important", ascending: true)] // try true/false
fetchRequest.sortDescriptors = descriptors
if let coreDataToDos = try? context.fetch(fetchRequest) {
if let theToDos = coreDataToDos {
toDos = theToDos
tableView.reloadData()
}
}
}
}

TableController doesn't show the data from Core-data

I want to make simple Contact by using core-data and tableView for practicing CoreData.
So I have watched youtube and write it's code.
Finally, I thought that I got this, I made by myself, but tableView doesn't contain any data, I could compile though. Could anyone tell me what is wrong? and hopefully tell me how to check the stored data in core-data?
import UIKit
import CoreData
class ViewController: UIViewController {
var context = (UIApplication.sharedApplication().delegate as!AppDelegate).managedObjectContext
var stores : Contact? = nil
#IBOutlet weak var nameTextField: UITextField!
#IBOutlet weak var phoneTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
if stores != nil {
nameTextField.text = stores?.name
phoneTextField.text = stores?.phone
context?.save(nil)
} }
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func saveTapped(sender: AnyObject) {
let context = self.context
// Get the description of the entity
if stores != nil { let storeDescription = NSEntityDescription.entityForName("stores", inManagedObjectContext: context!)
// Then, We Create the Managed Object to be inserted into the cored data
stores = Contact(entity: storeDescription!, insertIntoManagedObjectContext: context)
}
// set the attributes
stores?.name = nameTextField.text
stores?.phone = phoneTextField.text
context!.save(nil) // Save The object
let alert = UIAlertView(title: "저장 완료", message: "\(nameTextField.text)님이 전화번호부에 저장 되었습니다", delegate: nil, cancelButtonTitle: "OK")
alert.show()
}
}
And here is my tableviewcontroller.
import UIKit
import CoreData
class TableViewController: UITableViewController , NSFetchedResultsControllerDelegate{
let context = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
var frc : NSFetchedResultsController = NSFetchedResultsController()
var stores = [Contact]()
override func viewDidLoad() {
super.viewDidLoad()
frc.delegate = self
frc.performFetch(nil)
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
override func viewWillAppear(animated: Bool) {
var error:NSError?
let request = NSFetchRequest(entityName: "Contact")
stores = context?.executeFetchRequest(request, error: &error) as! [Contact]
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 Potentially incomplete method implementation.
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return stores.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
// Configure the cell...
let save = stores[indexPath.row]
cell.textLabel!.text = save.name
cell.detailTextLabel!.text = save.phone
return cell
}
func getFetchedResultsController() ->NSFetchedResultsController {
frc = NSFetchedResultsController(fetchRequest: listFetchRequest(), managedObjectContext: context!, sectionNameKeyPath: nil, cacheName: nil)
return frc
}
func listFetchRequest() -> NSFetchRequest {
let fetchRequest = NSFetchRequest(entityName: "Contact")
let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
return fetchRequest
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 == "edit"
{
let destViewController = segue.destinationViewController as! ViewController
let indexPath = self.tableView.indexPathForSelectedRow()
let row = indexPath?.row
destViewController.stores = stores[row!]
}
}
}
Update ----Since I fixed my viewController, It works. It would be like
import UIKit
import CoreData
class ViewController: UIViewController {
var context = (UIApplication.sharedApplication().delegate as!AppDelegate).managedObjectContext
var stores : Contact? = nil
#IBOutlet weak var nameTextField: UITextField!
#IBOutlet weak var phoneTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
if stores != nil {
nameTextField.text = stores?.name
phoneTextField.text = stores?.phone
context?.save(nil)
} }
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func saveTapped(sender: AnyObject) {
if stores != nil {
edit()
}else {
addNew()
}
navigationController?.popViewControllerAnimated(true)
}
func addNew() {
let description = NSEntityDescription.entityForName("Contact", inManagedObjectContext: context!)
let stores = Contact(entity: description!, insertIntoManagedObjectContext: context)
stores.name = nameTextField.text
stores.phone = phoneTextField.text
context?.save(nil)
}
func edit() {
stores!.name = nameTextField.text
stores!.phone = phoneTextField.text
context?.save(nil)
}
}
1. How to locate and View the SQLlite file.
You can find out the path of the .sqlite file using the below function.
func applicationDirectoryPath() -> String {
return NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).last! as! String
}
Then use any third party tools to view the content of the Sqlite database like SQLLite Manager (a firefox addon)
2. Displaying data in TableView.
In your tableViewController, instead of using stores variable Use NSFetchedResultController (frc) to retrieve and show the data in TableView. Check out this link for using NSFetchedResultController with TableViewController.

Storing with CoreData

my app is very simple: I have one tableview to store recipe *names and, for each recipe name, another tableview with several *ingredients for each recipe.
I already managed to save the name and the ingredients with CoreData, but here is the problem: when I press to add a NEW recipe name and enter the ingredients table view area, the ingredients saved for the previous recipe are there! How do I clear the table view to start a new one?
Also, my table views does not get updated immediately, I have to close the app and open it again. How do I fix it?
Note: If my question is too hard to understand, I can post some code! Thanks in advance, everyone =)
EDIT
code:
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)
}
}
}
and models:
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
}
In outline:
You need to amend your model: currently each Details object can have only one Ingredients. I suspect you need this relationship to be "to many" so a recipe can have many ingredients.
You need to add a var (of type Details?) to your IngredientListViewController. This will represent the chosen Recipe. (eg. var chosenRecipe : Details?)
In fetchIngredient, you need to add a predicate to the fetch, to limit the results to the chosen recipe. eg. fetch.predicate = NSPredicate(format:"ANY relationship == %#", chosenRecipe)
Before segueing to this VC, you need to set the chosenRecipe (probably in prepareForSegue, or didSelectRowAtIndexPath in the preceding table view).
To get your TV to update automatically, use the fetchedResultsController delegate methods. (Have you implemented these?)

UISearchController shows incorrect results

I have application with UITableViewController, two UIViewController and Core Data. I want to make UISearchController for the first time. I looked on the other questions by this theme, but I have working incorrect UISearchController. When I write into searchBar, it give incorrect results. And I don't know how show them in the table view cell. Please help me.
My code in the UITableViewController
var resultSearchController: UISearchController!
var searchPredicate: NSPredicate?
var filteredObjects: [Note]? = nil
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.leftBarButtonItem = self.editButtonItem()
fetchedResultsController = NSFetchedResultsController(fetchRequest: allEmployeesFetchRequest(), managedObjectContext: managedObjectContext!, sectionNameKeyPath: "mynote", cacheName: "mynote") // both mynote
fetchedResultsController?.delegate = self
fetchedResultsController?.performFetch(nil)
// UISearchController setup
self.resultSearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.delegate = self
controller.searchResultsUpdater = self
controller.hidesNavigationBarDuringPresentation = false
controller.dimsBackgroundDuringPresentation = true
controller.searchBar.sizeToFit()
controller.searchBar.delegate = self
self.tableView.tableHeaderView = controller.searchBar
return controller
})()
self.tableView.reloadData()
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
let searchText = self.resultSearchController.searchBar.text
println(searchText)
if let searchText = searchText {
searchPredicate = NSPredicate(format: "mynote contains[c] %#", searchText)
filteredObjects = self.fetchedResultsController?.fetchedObjects?.filter() {
return self.searchPredicate!.evaluateWithObject($0)
} as! [Note]?
self.tableView.reloadData()
println(searchPredicate)
}
}
and
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if searchPredicate == nil {
return fetchedResultsController?.sections?.count ?? 0
} else {
return 1
}
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searchPredicate == nil {
return fetchedResultsController?.sections?[section].numberOfObjects ?? 0 //0 default
} else {
return filteredObjects?.count ?? 0
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CellNote", forIndexPath: indexPath) as! UITableViewCell
if searchPredicate == nil {
if var cellContact = fetchedResultsController?.objectAtIndexPath(indexPath) as? Note {
cell.textLabel?.text = cellContact.mynote
cell.detailTextLabel?.text = cellContact.mytime
return cell
}
}
return cell
}
My core data class has
import Foundation
import CoreData
#objc(Note)
class Note: NSManagedObject {
#NSManaged var mynote: String
#NSManaged var mytime: String
}
I have a class that I use for multiple queries where I just pass a predicate, sometimes a sort and returns me an array of objects of the entity class.
class QueryDatabase: NSObject {
private var _managedObjectContext: NSManagedObjectContext?
init(managedObjectContext: NSManagedObjectContext){
super.init()
_managedObjectContext = managedObjectContext
}
func QueryTable(entityName: String, sortBy: String?, ascending: Bool, searchPredicate: NSPredicate?) -> AnyObject?{
if _managedObjectContext != nil {
let fetchRequest = NSFetchRequest(entityName: entityName)
if(sortBy != nil){
let sort = NSSortDescriptor(key: sortBy!, ascending: ascending)
fetchRequest.sortDescriptors = [sort]
}
if(searchPredicate != nil){
fetchRequest.predicate = searchPredicate!
}
var err: NSError?
if let fetchResults = _managedObjectContext!.executeFetchRequest(fetchRequest, error: &err){
return fetchResults
}else{
NSLog("ERROR:\(err?.description)")
}
}
return nil
}
}
I have used it in conjunction with a searchBar but in my case my predicate adds another modifier [cd] on its definition:
func findPricesBySearchCriteria(searchCriteria: String) -> [Prices]{
if(_managedObjectContext != nil){
let queryDatabase = QueryDatabase(managedObjectContext: _managedObjectContext!)
if let results = queryDatabase.QueryTable("Prices", sortBy: "item_description", ascending: true, searchPredicate: NSPredicate(format: "item_description contains[cd] %#", searchCriteria)) as? [Prices]{
arrSearch = results
}
}
return arrSearch
}
I found a solve, I changed my UITableViewController
class FirstTableViewController: UITableViewController, NSFetchedResultsControllerDelegate, UITableViewDataSource, UITableViewDelegate, UISearchControllerDelegate, UISearchResultsUpdating, UISearchBarDelegate
{
#IBAction func addNew(sender: UIBarButtonItem) {
}
let managedObjectContext: NSManagedObjectContext? = (UIApplication.sharedApplication().delegate as? AppDelegate)?.managedObjectContext
var fetchedResultsController: NSFetchedResultsController?
// MARK: - setting for search controller
var searchController: UISearchController!
var searchPredicate: NSPredicate!
var filteredObjects: [Note]? = nil
var filteredDataFromObjects = [String]()
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.leftBarButtonItem = self.editButtonItem()
fetchedResultsController = NSFetchedResultsController(fetchRequest: allEmployeesFetchRequest(), managedObjectContext: managedObjectContext!, sectionNameKeyPath: "mynote", cacheName: "mynote") // both mynote
fetchedResultsController?.delegate = self
fetchedResultsController?.performFetch(nil)
self.searchController = ({
var controllerS = UISearchController(searchResultsController: nil)
controllerS.delegate = self
controllerS.searchBar.delegate = self
controllerS.searchResultsUpdater = self
controllerS.searchBar.sizeToFit()
controllerS.dimsBackgroundDuringPresentation = false
controllerS.hidesNavigationBarDuringPresentation = false
// self.definesPresentationContext = true
self.tableView.tableHeaderView = controllerS.searchBar
return controllerS
})()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.tableView.reloadData()
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
filteredDataFromObjects.removeAll(keepCapacity: false)
var searchText = searchController.searchBar.text
if var searhText = searchText {
searchPredicate = NSPredicate(format: "mynote contains[c] %#", searchText)
filteredObjects = self.fetchedResultsController?.fetchedObjects?.filter() {
return self.searchPredicate.evaluateWithObject($0)
} as! [Note]?
self.tableView.reloadData()
println("Search Predicate \(searchPredicate)")
}
}
func allEmployeesFetchRequest() -> NSFetchRequest {
var fetchRequest = NSFetchRequest(entityName: "Note")
let sortDescriptor = NSSortDescriptor(key: "mytime", ascending: false) // change on date
fetchRequest.predicate = nil
fetchRequest.sortDescriptors = [sortDescriptor]
fetchRequest.fetchBatchSize = 20
return fetchRequest
}
//MARK: UITableView Data Source and Delegate Functions
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if searchPredicate == nil {
return fetchedResultsController?.sections?.count ?? 0 //after 0
} else {
return 1 ?? 0
}
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searchPredicate == nil {
return fetchedResultsController?.sections?[section].numberOfObjects ?? 0 //0 default
} else {
return filteredObjects?.count ?? 0
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CellNote", forIndexPath: indexPath) as! UITableViewCell
if searchPredicate == nil {
if var cellContact = fetchedResultsController?.objectAtIndexPath(indexPath) as? Note {
cell.textLabel?.text = cellContact.mynote
cell.detailTextLabel?.text = cellContact.mytime
}
} else {
if var textFromFiltered = filteredObjects?[indexPath.row] {
cell.textLabel?.text = textFromFiltered.mynote
cell.detailTextLabel?.text = textFromFiltered.mytime
}
}
return cell
}
//MARK: NSFetchedResultsController Delegate Functions
func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {
var tableView = UITableView()
if searchPredicate == nil {
tableView = self.tableView
} else {
tableView = (searchController.searchResultsUpdater as! FirstTableViewController).tableView
}
switch type {
case NSFetchedResultsChangeType.Insert:
tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: UITableViewRowAnimation.Fade)
break
case NSFetchedResultsChangeType.Delete:
tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: UITableViewRowAnimation.Fade)
break
case NSFetchedResultsChangeType.Move:
break
case NSFetchedResultsChangeType.Update:
break
default:
break
}
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
}
switch editingStyle {
case .Delete:
managedObjectContext?.deleteObject(fetchedResultsController?.objectAtIndexPath(indexPath) as! Note)
managedObjectContext?.save(nil)
case .Insert:
break
case .None:
break
}
}
func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
// I made test which controller is need to show
var tableView = UITableView()
if searchPredicate == nil {
tableView = self.tableView
} else {
tableView = (searchController.searchResultsUpdater as! FirstTableViewController).tableView
}
switch type {
case NSFetchedResultsChangeType.Insert:
tableView.insertRowsAtIndexPaths([AnyObject](), withRowAnimation: UITableViewRowAnimation.Fade)
break
case NSFetchedResultsChangeType.Delete:
tableView.deleteRowsAtIndexPaths(NSArray(object: indexPath!) as [AnyObject], withRowAnimation: UITableViewRowAnimation.Fade)
break
case NSFetchedResultsChangeType.Move:
tableView.deleteRowsAtIndexPaths(NSArray(object: indexPath!) as [AnyObject], withRowAnimation: UITableViewRowAnimation.Fade)
tableView.insertRowsAtIndexPaths(NSArray(object: newIndexPath!) as [AnyObject], withRowAnimation: UITableViewRowAnimation.Fade)
break
case NSFetchedResultsChangeType.Update:
tableView.cellForRowAtIndexPath(indexPath!)
break
default:
break
}
}
func controllerWillChangeContent(controller: NSFetchedResultsController) {
if searchPredicate == nil {
tableView.beginUpdates()
} else {
(searchController.searchResultsUpdater as? FirstTableViewController)?.tableView.beginUpdates()
}
}
func controllerDidChangeContent(controller: NSFetchedResultsController) {
if searchPredicate == nil {
tableView.endUpdates()
} else {
(searchController.searchResultsUpdater as? FirstTableViewController)?.tableView.endUpdates()
}
}
#IBAction func unwindToFirst(segue: UIStoryboardSegue) {
}
// for sent data in detail controller
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow() {
if searchPredicate == nil {
let object = self.fetchedResultsController!.objectAtIndexPath(indexPath) as! NSManagedObject
let controller = (segue.destinationViewController as! UINavigationController).topViewController as! DetailViewController //DetailViewController
controller.detailItem = object
} else {
let object = filteredObjects![indexPath.row] as Note
let controller = (segue.destinationViewController as! UINavigationController).topViewController as! DetailViewController
controller.detailItem = object
}
}
self.searchController.active = false
}
}
func configureCell(cell: UITableViewCell, atIndexPath indexPath: NSIndexPath) {
let object = self.fetchedResultsController!.objectAtIndexPath(indexPath) as! NSManagedObject
cell.textLabel!.text = object.valueForKey("mynote")!.description
}
// MARK: - UISearch functions
func searchBar(searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
updateSearchResultsForSearchController(searchController)
}
func didDismissSearchController(searchController: UISearchController) {
filteredObjects = nil
searchPredicate = nil
tableView.reloadData()
}
}

Adding sections, separated by dates, to UITableView in Swift CoreData

Can somebody tell me how to add sections with date using CoreData?
I have detailViewController which store all data from CoreDate, and AddTableViewController when we write some data.
Detail View Controller
import UIKit
import CoreData
class DetailViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, NSFetchedResultsControllerDelegate, AddTableViewControllerDelegate {
#IBOutlet weak var tableView: UITableView!
var detailTaskModel: Items!
var costsValues:Int!
#IBOutlet weak var sumLabel: UILabel!
// CoreData
let managedObjectContext = (UIApplication.sharedApplication().delegate as AppDelegate).managedObjectContext
var fetchedResultsController: NSFetchedResultsController = NSFetchedResultsController()
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
fetchedResultsController = getFetchedResultsController()
fetchedResultsController.delegate = self
fetchedResultsController.performFetch(nil)
costsValues = getAccountCountSum()
self.tableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// UITableViewDataSource
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return fetchedResultsController.sections![section].numberOfObjects
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell: DetailTableViewCell = tableView.dequeueReusableCellWithIdentifier("detailCell") as DetailTableViewCell
let thisCost = fetchedResultsController.objectAtIndexPath(indexPath) as Costs
cell.nazwaWydatkuLabel.text = thisCost.costsName
cell.wartośćLabel.text = "\(thisCost.costsValue) zł"
return cell
}
#IBAction func addCostButtonPressed(sender: UIBarButtonItem) {
self.performSegueWithIdentifier("addCostVC", sender: self)
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return fetchedResultsController.sections!.count
}
override func viewDidAppear(animated: Bool) {
costsValues = getAccountCountSum()
refreshTable()
self.tableView.reloadData()
}
// CoreData Functions
func taskFetchRequest() -> NSFetchRequest {
let fetchRequest = NSFetchRequest(entityName: "Costs")
let sortDescriptor = NSSortDescriptor(key: "costsDate", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
return fetchRequest
}
func getFetchedResultsController() -> NSFetchedResultsController {
fetchedResultsController = NSFetchedResultsController(fetchRequest: taskFetchRequest(), managedObjectContext: managedObjectContext!, sectionNameKeyPath: nil, cacheName: nil)
return fetchedResultsController
}
// NSFetchedResultsControllerDelegate
func controllerDidChangeContent(controller: NSFetchedResultsController) {
tableView.reloadData()
}
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
let thisCost = fetchedResultsController.objectAtIndexPath(indexPath) as Costs
managedObjectContext?.deleteObject(thisCost)
costsValues = getAccountCountSum()
refreshTable()
self.tableView.reloadData()
(UIApplication.sharedApplication().delegate as AppDelegate).saveContext()
}
// Sum count in all accouts
func getAccountCountSum() -> Int {
let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let managedContext : NSManagedObjectContext = appDelegate.managedObjectContext!
var fetchRequest = NSFetchRequest(entityName: "Costs")
fetchRequest.returnsObjectsAsFaults = false
var results: NSArray = managedContext.executeFetchRequest(fetchRequest, error: nil)!
var accountsSum: Int = 0
for res in results {
var accountCount = res.valueForKey("costsValue") as Int
accountsSum += accountCount
}
self.sumLabel.text = String("\(costsValues) zł")
return accountsSum
}
func refreshTable() {
getAccountCountSum()
self.tableView.reloadData()
}
}
AddTableViewController
#IBAction func saveButtonTapped(sender: UIBarButtonItem) {
// CoreData Access
let appDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
var managedObjectContext = appDelegate.managedObjectContext
let entityDescription = NSEntityDescription.entityForName("Costs", inManagedObjectContext: managedObjectContext!)
let cost = Costs(entity: entityDescription!, insertIntoManagedObjectContext: managedObjectContext!)
cost.costsName = cellThreeNoteTextField.text
cost.costsValue = (cellOnePriceTextField.text).toInt()!
cost.costsDate = datePicker.date
// Saving data
appDelegate.saveContext()
var request = NSFetchRequest(entityName: "Costs")
var error:NSError? = nil
var results:NSArray = managedObjectContext!.executeFetchRequest(request, error: &error)!
for res in results {
println(res)
}
delegate?.refreshTable()
self.navigationController?.popViewControllerAnimated(true)
}
Since you're using an NSFetchedResultsController, you create sections by passing a key on your managed objects to the sectionNameKeyPath argument. The sections property of the fetched results controller will then break up the results into sections based on the values of that key path in the fetched objects.
You'll also need to implement UITableView callbacks to get it to use the sections-- for example, sectionIndexTitlesForTableView and tableView:titleForHeaderInSection:. These are described in the NSFetchedResultsController documentation.

Resources