Swift filter UITableView with out search bar - uitableview

I have a UITableView that I want to filter based on a selection from slide panel view controller. This is the function that gets the returned value form the panel.
func itemSelected(type: Item) {
self.selectedItem = Item.title
delegate?.collapseSidePanels?()
}
Table view code.
var myData: Array<AnyObject> = []
var selectedItem:Array<AnyObject> = []
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellID: NSString = "Cell"
var Cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier(cellID as String) as! UITableViewCell
var data: NSManagedObject = myData[indexPath.row] as! NSManagedObject
if tableView == selectedItem {
data = self.selectedItem[indexPath.row] as! NSManagedObject
} else
{
data = myData[indexPath.row] as! NSManagedObject
}
Cell.textLabel?.text = data.valueForKeyPath("itemname") as? String
var tt = data.valueForKeyPath("itemtype") as! String
Cell.detailTextLabel?.text = ("Item Type: \(tt)")
return Cell
}
I need to filter on the itemtype.
edit - Will not filter still so here is the full code for the tableViewController.
import UIKit
import CoreData
import Foundation
#objc
protocol tableViewControllerDelegate {
optional func toggleLeftPanel()
optional func toggleRightPanel()
optional func collapseSidePanels()
}
class tableViewController: UITableViewController, NSFetchedResultsControllerDelegate, SidePanelViewControllerDelegate {
var delegate: tableViewControllerDelegate?
var myData: Array<AnyObject> = []
var myFilteredData: Array<AnyObject> = []
#IBAction func leftTapped(sender: AnyObject) {
delegate?.toggleLeftPanel?()
}
// Use this to change table view to edit mode
// and to Change the title when clicked on.
// Make sure to have sender set as UIBarButtonItem
// or you can not change the title of the button.
var condition: Bool = true
#IBAction func buttonEdit(sender: UIBarButtonItem) {
if(condition == true) {
tableView.editing = true
sender.title = "Done"
condition = false
} else {
tableView.editing = false
sender.title = "Edit"
condition = true
}
}
let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
var fetchedResultController: NSFetchedResultsController = NSFetchedResultsController()
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(animated: Bool) {
// This is neeed when using panel view controller to show the bottom navbar.
self.navigationController?.setToolbarHidden(false, animated: true)
// ref app del
let appDel: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
// Ref data
let context: NSManagedObjectContext = appDel.managedObjectContext!
let freq = NSFetchRequest(entityName: "Products")
myData = context.executeFetchRequest(freq, error: nil)!
}
override func viewDidAppear(animated: Bool) {
}
// 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.
if (self.myFilteredData.count != 0) {
return self.myFilteredData.count
} else {
return self.myData.count
}
}
func getFetchedResultController() -> NSFetchedResultsController {
fetchedResultController = NSFetchedResultsController(fetchRequest: NSFetchRequest(), managedObjectContext: managedObjectContext!, sectionNameKeyPath: nil, cacheName: nil)
return fetchedResultController
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellID: String = "Cell"
var cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier(cellID as String) as! UITableViewCell
var data: NSManagedObject
if (self.myFilteredData.count != 0){
data = myFilteredData[indexPath.row] as! NSManagedObject
cell.textLabel?.text = data.valueForKeyPath("productname") as? String
var tt = data.valueForKeyPath("itemtype") as! String
cell.detailTextLabel?.text = ("Item J Type: \(tt)")
} else {
data = myData[indexPath.row] as! NSManagedObject
cell.textLabel?.text = data.valueForKeyPath("productname") as? String
var tt = data.valueForKeyPath("itemtype") as! String
cell.detailTextLabel?.text = ("Item Type: \(tt)")
}
return cell
}
override func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
override func tableView(tableView: UITableView, moveRowAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) {
let item: AnyObject = myData[sourceIndexPath.row]
myData.removeAtIndex(sourceIndexPath.row)
myData.insert(item, atIndex: destinationIndexPath.row)
}
// called when a row deletion action is confirmed
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
switch editingStyle {
case .Delete:
// remove the deleted item from the model
let appDel:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context:NSManagedObjectContext = appDel.managedObjectContext!
context.deleteObject(myData[indexPath.row] as! NSManagedObject)
myData.removeAtIndex(indexPath.row)
context.save(nil)
// remove the deleted item from the `UITableView`
self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
default:
return
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "showProduct"){
let selectedIndexPath:NSIndexPath = self.tableView.indexPathForSelectedRow()!
let genView:genViewController = segue.destinationViewController as! genViewController
genView.row = selectedIndexPath.row
}
else if (segue.identifier == "addProduct"){
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func itemSelected(item: Type) {
var selectedType = item.title
delegate?.collapseSidePanels?()
for (key, value) in enumerate(self.myData) {
if (value.valueForKeyPath("itemtype") !== "selectedType") {
self.myFilteredData.append(value)
dump(myFilteredData)
} else {
// do nothing with it
}
}
tableView.reloadData()
}
}

Depending on however you want the data filtered, you could loop through myData in itemSelected(), find the elements that you want in your filtered list and save them in a new array (myFilteredData).
var myFilteredData: Array<AnyObject> = []
func itemSelected(type: Item) {
self.selectedItem = Item.title
delegate?.collapseSidePanels?()
for (key, value) in enumerate(self.myData) {
if (value.valueForKeyPath("itemtype") == "yourCondition") {
self.myFilteredData.append(value)
} else {
// do nothing with it
}
}
tableView.reloadData() // use tableView.reloadSections with rowAnimation for better effect.
}
You would then reload the tableview with tableView.reloadSections(_ sections: NSIndexSet,
withRowAnimation animation: UITableViewRowAnimation), which will trigger the cellForRowAtIndexPath function. Here, you would need to decide if you want to use myData or myFilteredData for the cell's labels.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
...
var data:NSManagedObject
if (self.myFilteredData.count != 0) {
data = myFilteredData[indexPath.row] as! NSManagedObject
} else {
data = myData[indexPath.row] as! NSManagedObject
}
...
}
Also, don't forget to modify the numberOfRowsInSection function to return the size of the array you are populating the tableView with.
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (self.myFilteredData.count != 0) {
return self.myFilteredData.count
} else {
return self.myData.count
}
}

Related

Value of type "Entity" has no member 'text'

I am making a basic note taking app. I cannot seem to change the text of my attributes in my code. Any advice?
import UIKit
import CoreData
class NoteInputTableViewController: UITableViewController, UINavigationControllerDelegate {
var listItem: List?
let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
#IBOutlet weak var inputNoteLabel: UITextField!
func navigationController(navigationController: UINavigationController, willShowViewController viewController: UIViewController, animated: Bool) {
if let item = listItem {
listItem.text = inputNoteLabel.text
} else {
let newItem = NSEntityDescription.insertNewObjectForEntityForName("List", inManagedObjectContext: managedObjectContext) as! List
newItem.text = inputNoteLabel.text
}
if let controller = viewController as? NoteHomeTableViewController {
controller.items = [items!]
}
}
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.delegate = self
} // viewDidLoad end
}
In my navigationController I am attempting to change the text of my listItem, but cannot seem to do so.
Below is my List+CoreDataProperties.swift file:
import Foundation
import CoreData
extension List {
#NSManaged var note: String?
#NSManaged var completionDate: NSDate?
}
And below is my NoteHomeTableViewController.swift file:
import UIKit
import CoreData
class NoteHomeTableViewController: UITableViewController, MyCellDelegate { // will need a delegate
var items: [List] = []
let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
#IBAction func composeButtonPressed(sender: UIBarButtonItem) {
performSegueWithIdentifier("CellDetailSegue", sender: nil)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let destination = segue.destinationViewController as! NoteInputTableViewController
if let ip = sender as? NSIndexPath {
destination.listItem = items[ip.row]
}
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("MyCell") as! MyCell
cell.indexPath = indexPath
cell.item = items[indexPath.row]
cell.delegate = self
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
performSegueWithIdentifier("CellDetailSegue", sender: indexPath)
}
func myCell(myCell: MyCell, didPressBackItem: List,
atIndexPath indexPath: NSIndexPath) {
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
let item = items[indexPath.row]
managedObjectContext.deleteObject(item)
do {
try managedObjectContext.save()
} catch {
print("error saving")
}
fetchItems()
tableView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
} // viewDidLoad end
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(true)
fetchItems()
tableView.reloadData()
} // viewDidAppear end
func fetchItems() {
let request = NSFetchRequest(entityName: "List")
items = []
do {
let response = try managedObjectContext.executeFetchRequest(request) as! [List]
for item in response {
items.append(item)
}
} catch {
print("Error fetching data!")
}
} // fetch end
} // class end
I don't see where you initialize listItem.
I suggest
} else {
let newItem = NSEntityDescription.insertNewObjectForEntityForName("List", inManagedObjectContext: managedObjectContext) as! List
newItem.text = inputNoteLabel.text
}
should probably become
} else {
let newItem = NSEntityDescription.insertNewObjectForEntityForName("List", inManagedObjectContext: managedObjectContext) as! List
listItem = newItem
newItem.text = inputNoteLabel.text
}
Use the unwrapped item after the optional binding and assign the new initialized NSManagedObject to listItem
...
if let item = listItem {
item.text = inputNoteLabel.text
} else {
listItem = NSEntityDescription.insertNewObjectForEntityForName("List", inManagedObjectContext: managedObjectContext) as! List
listItem!.text = inputNoteLabel.text
...
or easier
...
if listItem == nil {
listItem = NSEntityDescription.insertNewObjectForEntityForName("List", inManagedObjectContext: managedObjectContext) as! List
}
listItem!.text = inputNoteLabel.text
...

Changing NSManagedObject values in new viewcontroller

I have a list of customers in a UITableView managed by an NSFetchedResultsController, class is called CustomersViewController. When I select a customer, a new view controller CustomerDetailViewController is loaded which displays their details and then a list of radiators related to them in another UITableView managed by an NSFetchedResultsController. The only editing I need on the tables is Deletion and this works fine in both tables managed by NSFetchedResultsController.
I want to be able to edit the customers details, so I have an edit button in the NavigationBar that segues to EditCustomerViewController from CustomerDetailViewController. As with previous segues the managedObjectContext and the managedObject (the selected customer) is passed through successfully and I can access all the objects values in the EditCustomerViewController, what I can't seem to do is edit them without getting these errors:
2016-02-18 12:30:08.349 Radiator Calculator[13825:2113477] *** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3512.29.5/UITableView.m:1720
2016-02-18 12:30:08.351 Radiator Calculator[13825:2113477] CoreData: error: Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (2) must be equal to the number of rows contained in that section before the update (2), plus or minus the number of rows inserted or deleted from that section (1 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out). with userInfo (null)
From this error I am guessing the issue lies with the NSFetchedResultsController not liking me changing the value in the EditCustomerViewController two viewcontrollers ahead of where it was instantiated. Given that there is no table in this view controller I haven't set it up.
The code for the three viewcontrollers in question are:
Code for CustomersViewController:
import UIKit
import CoreData
class CustomersViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, NSFetchedResultsControllerDelegate {
var context: NSManagedObjectContext!
#IBOutlet weak var customerList: UITableView!
var selectedCustomer: NSManagedObject!
// MARK: - viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
print("Customers VC")
let appDel: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
context = appDel.managedObjectContext
do {
try fetchedResultsController.performFetch()
} catch {
print("Error occured with FRC")
}
}
override func viewWillAppear(animated: Bool) {
//reload todo list data array
customerList.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//MARK: - Table data functions
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if let sections = fetchedResultsController.sections {
return sections.count
}
return 0
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let sections = fetchedResultsController.sections {
let currentSection = sections[section]
return currentSection.numberOfObjects
}
return 0
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Customer Cell")
let customer = fetchedResultsController.objectAtIndexPath(indexPath)
print(customer)
cell.textLabel?.text = customer.name
return cell
}
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == UITableViewCellEditingStyle.Delete {
let customer = fetchedResultsController.objectAtIndexPath(indexPath) as! NSManagedObject
context.deleteObject(customer)
do {
try context.save()
} catch let error as NSError {
print("Error saving context after delete \(error.localizedDescription)")
}
}
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedCustomer = fetchedResultsController.objectAtIndexPath(indexPath) as! NSManagedObject
self.performSegueWithIdentifier("customerDetailSegue", sender: self)
}
// MARK: - Segue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
print("seg fired")
if segue.identifier == "addCustomerSegue" {
if let addCustomerViewController = segue.destinationViewController as? AddCustomerViewController {
addCustomerViewController.context = context
}
}
if segue.identifier == "customerDetailSegue"{
if let customerDetailViewController = segue.destinationViewController as? CustomerDetailViewController {
customerDetailViewController.context = context
customerDetailViewController.customer = selectedCustomer
}
}
}
// set up frc
lazy var fetchedResultsController: NSFetchedResultsController = {
let customerFetchRequest = NSFetchRequest(entityName: "Customers")
let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
customerFetchRequest.sortDescriptors = [sortDescriptor]
let frc = NSFetchedResultsController(fetchRequest: customerFetchRequest, managedObjectContext: self.context, sectionNameKeyPath: nil, cacheName: nil)
frc.delegate = self
return frc
}()
//MARK: NSFetchedResultsControllerDelegate methods
func controllerWillChangeContent(controller: NSFetchedResultsController) {
self.customerList.beginUpdates()
}
func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
switch type{
case NSFetchedResultsChangeType.Insert:
//note that for insert we insert a row at _newIndexPath_
if let insertIndexPath = newIndexPath {
self.customerList.insertRowsAtIndexPaths([insertIndexPath], withRowAnimation: UITableViewRowAnimation.Fade)
}
case NSFetchedResultsChangeType.Delete:
//note that for delete we delete the row at _indexPath_
if let deleteIndexPath = indexPath {
self.customerList.deleteRowsAtIndexPaths([deleteIndexPath], withRowAnimation: UITableViewRowAnimation.Fade)
}
case NSFetchedResultsChangeType.Update:
//note that for update we update the row at _indexPath_
if let updateIndexPath = indexPath {
let cell = self.customerList.cellForRowAtIndexPath(updateIndexPath)
let customer = fetchedResultsController.objectAtIndexPath(updateIndexPath)
cell!.textLabel?.text = customer.name
}
case NSFetchedResultsChangeType.Move:
//note that for Move we delete the row at _indexPath_
if let deleteIndexPath = indexPath {
self.customerList.insertRowsAtIndexPaths([deleteIndexPath], withRowAnimation: UITableViewRowAnimation.Fade)
}
//note that for move we insert a row at _newIndexPath_
if let insertIndexPath = newIndexPath {
self.customerList.insertRowsAtIndexPaths([insertIndexPath], withRowAnimation: UITableViewRowAnimation.Fade)
}
}
}
func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {
//note needed as only have one section
}
func controller(controller: NSFetchedResultsController, sectionIndexTitleForSectionName sectionName: String) -> String? {
return sectionName
}
func controllerDidChangeContent(controller: NSFetchedResultsController) {
self.customerList.endUpdates()
}
}
and CustomerDetailViewController
import UIKit
import CoreData
class CustomerDetailViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, NSFetchedResultsControllerDelegate {
var context: NSManagedObjectContext!
var customer: NSManagedObject!
#IBOutlet weak var customerName: UILabel!
#IBOutlet weak var street: UILabel!
#IBOutlet weak var town: UILabel!
#IBOutlet weak var postCode: UILabel!
#IBOutlet weak var radiatorList: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
radiatorList.allowsSelection = false
if let name = customer.valueForKey("name") as? String {
customerName.text = name
}
if let addressLine1 = customer.valueForKey("address_line_1") as? String {
street.text = addressLine1
}
if let townName = customer.valueForKey("town") as? String {
town.text = townName
}
if let postcode = customer.valueForKey("postcode") as? String {
postCode.text = postcode
}
// set up FRC
do {
try fetchedResultsController.performFetch()
} catch {
print("Error occured with FRC")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if let sections = fetchedResultsController.sections {
return sections.count
}
return 0
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let sections = fetchedResultsController.sections {
let currentSection = sections[section]
return currentSection.numberOfObjects
}
return 0
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Radiator Cell", forIndexPath: indexPath) as! RadiatorCell
let radiator = fetchedResultsController.objectAtIndexPath(indexPath) as? NSManagedObject
if let radiatorName = radiator?.valueForKey("radiatorName") as? String {
cell.radNameLabel.text = String(radiatorName)
}
if let radiatorPowerWatts = radiator?.valueForKey("radiatorPowerWatts") as? Double{
print(radiatorPowerWatts)
cell.radPowerWattsLabel.text = "\(ceil(radiatorPowerWatts)) Watts"
cell.radPowerBtusLabel.text = "\(ceil(radiatorPowerWatts / 0.293)) BTUs"
}
return cell
}
/*
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
}
*/
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == UITableViewCellEditingStyle.Delete {
let radiator = fetchedResultsController.objectAtIndexPath(indexPath) as! NSManagedObject
context.deleteObject(radiator)
do {
try context.save()
} catch let error as NSError {
print("Error saving context after delete \(error.localizedDescription)")
}
}
}
// set up frc
lazy var fetchedResultsController: NSFetchedResultsController = {
let radiatorFetchRequest = NSFetchRequest(entityName: "Radiators")
let sortDescriptor = NSSortDescriptor(key: "radiatorName", ascending: true)
radiatorFetchRequest.predicate = NSPredicate(format: "customer = %#", self.customer)
radiatorFetchRequest.sortDescriptors = [sortDescriptor]
let frc = NSFetchedResultsController(fetchRequest: radiatorFetchRequest, managedObjectContext: self.context, sectionNameKeyPath: nil, cacheName: nil)
frc.delegate = self
return frc
}()
//MARK: NSFetchedResultsControllerDelegate methods
func controllerWillChangeContent(controller: NSFetchedResultsController) {
self.radiatorList.beginUpdates()
}
func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
switch type{
case NSFetchedResultsChangeType.Insert:
//note that for insert we insert a row at _newIndexPath_
if let insertIndexPath = newIndexPath {
self.radiatorList.insertRowsAtIndexPaths([insertIndexPath], withRowAnimation: UITableViewRowAnimation.Fade)
}
case NSFetchedResultsChangeType.Delete:
//note that for delete we delete the row at _indexPath_
if let deleteIndexPath = indexPath {
self.radiatorList.deleteRowsAtIndexPaths([deleteIndexPath], withRowAnimation: UITableViewRowAnimation.Fade)
}
case NSFetchedResultsChangeType.Update:
//note that for update we update the row at _indexPath_
if let updateIndexPath = indexPath {
let cell = self.radiatorList.cellForRowAtIndexPath(updateIndexPath)
let radiator = fetchedResultsController.objectAtIndexPath(updateIndexPath)
cell!.textLabel?.text = radiator.name
}
case NSFetchedResultsChangeType.Move:
//note that for Move we delete the row at _indexPath_
if let deleteIndexPath = indexPath {
self.radiatorList.insertRowsAtIndexPaths([deleteIndexPath], withRowAnimation: UITableViewRowAnimation.Fade)
}
//note that for move we insert a row at _newIndexPath_
if let insertIndexPath = newIndexPath {
self.radiatorList.insertRowsAtIndexPaths([insertIndexPath], withRowAnimation: UITableViewRowAnimation.Fade)
}
}
}
func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {
//note needed as only have one section
}
func controller(controller: NSFetchedResultsController, sectionIndexTitleForSectionName sectionName: String) -> String? {
return sectionName
}
func controllerDidChangeContent(controller: NSFetchedResultsController) {
self.radiatorList.endUpdates()
}
// 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?) {
if segue.identifier == "mapSegue"{
if let mapVC = segue.destinationViewController as? CustomerMapViewController{
var addressString = String()
if let addressLine1 = customer.valueForKey("address_line_1") as? String {
let s = addressLine1
addressString += "\(s), "
}
if let townName = customer.valueForKey("town") as? String {
let t = townName
addressString += "\(t), "
}
if let postcode = customer.valueForKey("postcode") as? String {
let p = postcode
addressString += "\(p)"
}
mapVC.address = addressString
}
}
if segue.identifier == "editCustomerSegue" {
if let editVC = segue.destinationViewController as? EditCustomerViewController {
editVC.context = context
editVC.customer = customer
}
}
}
}
and
import UIKit
import CoreData
class EditCustomerViewController: UIViewController {
var context: NSManagedObjectContext!
var customer: NSManagedObject!
override func viewDidLoad() {
super.viewDidLoad()
print("Edit customer view controller")
if let name = customer.valueForKey("name") as? String {
//this works
print(name)
}
customer.setValue("Hardcoded name change", forKey: "name")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// 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.
}
*/
}
The application keeps running until I navigate back to the CustomersViewController when it finally crashes and I can briefly see that the customer name has changed in the list - it hasn't changed in the CustomerDetailViewController however.
Any help would be great, apologies for any lack of "swifty-ness" (I read that's a thing) - this is my first larger app in swift and iOS so I'm still learning as I go.

Search bar tableview array index out of range

I'm trying to create a search bar in my Tableview, but it's not giving me a result of filtration, Table is empty
fatal error: Array index out of range
I thing that something goes wrong in Tableview update fuction or filterContent, but can't understand what. Could somebody explain it to me?
var find: [List] = [] //my core data file
var searchResult: [List] = [] //my core data file
let context: NSManagedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
var detailViewController: MainVC? = nil
var frc : NSFetchedResultsController = NSFetchedResultsController()
var searchController: UISearchController!
func getFetchResultsController() ->NSFetchedResultsController {
frc = NSFetchedResultsController(fetchRequest: listFetchRequest(), managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
return frc
}
func listFetchRequest() -> NSFetchRequest{
let fetchRequest = NSFetchRequest (entityName: "List")
let sortDescriptor = NSSortDescriptor(key: "item", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
return fetchRequest
}
override func viewDidLoad() {
super.viewDidLoad()
frc = getFetchResultsController()
frc.delegate = self
do {
try frc.performFetch()
} catch {}
if let split = self.splitViewController {
let controllers = split.viewControllers
self.detailViewController = (controllers[controllers.count-1] as! UINavigationController).topViewController as? MainVC
}
searchController = UISearchController(searchResultsController: nil)
tableView.tableHeaderView = searchController.searchBar
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func controllerDidChangeContent(controller: NSFetchedResultsController) {
tableView.reloadData()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
let numberOfSections = frc.sections?.count
return numberOfSections!
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searchController.active {
return searchResult.count
} else {
//return find.count
let numberOfRowsInSection = frc.sections?[section].numberOfObjects
return numberOfRowsInSection!
}
// #warning Incomplete implementation, return the number of rows
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
let search = (searchController.active) ? searchResult[indexPath.row] : frc.objectAtIndexPath(indexPath) as! List
print(search)
//Configure the cell..
cell.textLabel!.text = search.item
let qty = search.qty
let address = search.address
cell.detailTextLabel?.text = "Dukan Sayi: \(qty)\(address)"
return cell
}
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
if searchController.active {
return false
} else {
return true
}
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
let managedObject : NSManagedObject = frc.objectAtIndexPath(indexPath) as! NSManagedObject
context.deleteObject(managedObject)
do {
try context.save()
} catch {}
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
if let searchText = searchController.searchBar.text {
filterContent(searchText)
print(searchText)
tableView.reloadData()
}
}
func filterContent(searchText: String) {
searchResult = find!.filter({ ( a: List) -> Bool in
let nameMatch = a.item.rangeOfString(searchText, options: NSStringCompareOptions.CaseInsensitiveSearch)
return nameMatch != nil
})
}

Notes will not delete

I have a note part of my app which I am able to create note and edit notes but when it comes to deleting notes I have an issue. I can delete them from the table view itself and all notes but when I reload the app they're back as if they're sticking in the dictionary. Could you pleas etell me a way to delete them permanently. I have three controllers, only two being relevant. Here are the relevant ones:
MasterViewController.swift
import UIKit
class MasterViewController: UITableViewController {
#IBOutlet weak var menuButton: UIBarButtonItem!
override func awakeFromNib() {
super.awakeFromNib()
}
override func viewDidLoad() {
super.viewDidLoad()
Note.loadnotes()
noteTable = self.tableView
// Side Menu
if self.revealViewController() != nil {
menuButton.target = self.revealViewController()
menuButton.action = "revealToggle:"
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
// Do any additional setup after loading the view, typically from a nib.
func insertNewObject(sender: AnyObject) {
allNotes.insert(Note(), atIndex: 0)
let indexPath = NSIndexPath(forRow: 0, inSection: 0)
self.tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
self.performSegueWithIdentifier("showDetail", sender: self)
}
// MARK: - Segues
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow() {
let object: Note = allNotes[indexPath.row] as Note
currentNoteIndex = indexPath.row
}
else {
currentNoteIndex = 0
}
}
}
// MARK: - Table View
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return allNotes.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
let object: Note = allNotes[indexPath.row]
as Note; cell.textLabel!.text = object.note
return cell
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
allNotes.removeAtIndex(indexPath.row)
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.
}
}
}
note.swift
import UIKit
var allNotes:[Note] = []
var currentNoteIndex:NSInteger = -1
var noteTable:UITableView?
let KAllNotes:String = "notes"
class Note: NSObject {
var date:String
var note:String
override init() {
date = NSDate().description
note = ""
}
func dictionary() -> NSDictionary {
return ["note":note, "date":date]
}
class func saveNotes() {
var aDictionaries:[NSDictionary] = []
for (var i:NSInteger = 0; i < allNotes.count; i++) {
aDictionaries.append(allNotes[i].dictionary())
}
NSUserDefaults.standardUserDefaults().setObject(aDictionaries, forKey: KAllNotes)
// aDictionaries.writeToFile(filePath(), atomically: true)
}
class func loadnotes() {
var defaults:NSUserDefaults = NSUserDefaults.standardUserDefaults()
var savedData:[NSDictionary]? = defaults.objectForKey(KAllNotes) as? [NSDictionary]
// var savedData:NSArray? = NSArray(contentsOfFile: filePath())
if let data:[NSDictionary] = savedData {
for (var i:NSInteger = 0; i < data.count; i++) {
var n:Note = Note()
n.setValuesForKeysWithDictionary(data[i] as [NSObject : AnyObject])
allNotes.append(n)
}
}
}
class func filePath() -> String {
var d:[String]? = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true) as? [String]
if let directories:[String] = d {
var docsDirectory:String = directories[0]
var path:String = docsDirectory.stringByAppendingPathComponent("\(KAllNotes).notes")
return path;
}
return ""
}
}
Thanks in advance
Sam
In the master view controller use this altered function:
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
allNotes.removeAtIndex(indexPath.row)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
Note.saveNotes()
} 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.
}
}
This will force the dictionary to be re-saved. using your save notes function in notes.swift

searchDisplayController, UITableView, Core Data and Swift

Trying to search in the table with searchDisplayController. Data filtering configured, the search dialog works. Now I want to work with a method prepareForSegue for send current value indexPath to a new UIViewController:
import UIKit
import CoreData
class MainTableViewController: UITableViewController {
var results:AddrBook[]=[]
var searchResults:AddrBook[]=[]
init(style: UITableViewStyle) {
super.init(style: style)
}
init(coder aDecoder: NSCoder!) {
super.init(coder: aDecoder)
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func viewDidAppear(animated: Bool) {
let request = NSFetchRequest(entityName: "Person")
request.returnsObjectsAsFaults = false
let appDelegate:AppDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
let context:NSManagedObjectContext = appDelegate.managedObjectContext
results = context.executeFetchRequest(request, error: nil) as AddrBook[]
self.tableView.reloadData()
}
override func numberOfSectionsInTableView(tableView: UITableView?) -> Int {
return 1
}
override func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
if tableView == self.searchDisplayController.searchResultsTableView {
return searchResults.count
} else {
return results.count
}
}
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell! {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell") as? UITableViewCell
if !cell {
cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: "Cell")
}
if tableView == self.searchDisplayController.searchResultsTableView {
cell!.textLabel.text = searchResults[indexPath.row].lastname + " " + searchResults[indexPath.row].firstname
cell!.detailTextLabel.text = searchResults[indexPath.row].phonenumber
} else {
cell!.textLabel.text = results[indexPath.row].lastname + " " + results[indexPath.row].firstname
cell!.detailTextLabel.text = results[indexPath.row].phonenumber
}
return cell
}
override func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
if tableView == self.searchDisplayController.searchResultsTableView {
self.performSegueWithIdentifier("editPerson", sender : self)
}
}
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject?) {
var indexPath = NSIndexPath()
if self.tableView == self.searchDisplayController.searchResultsTableView {
NSLog("Trying recieve indexPath from Search")
indexPath = self.searchDisplayController.searchResultsTableView.indexPathForSelectedRow()
NSLog("indexPath from Search")
}
else {
indexPath = self.tableView.indexPathForSelectedRow()
NSLog("IndexPath from main table")
}
let destViewController:DetailViewController! = segue.destinationViewController as DetailViewController
if segue.identifier == "editPerson" {
destViewController.receivedPerson = results
destViewController.indexPath = indexPath
NSLog("Selected person ID: \(results[indexPath.row].idperson)")
}
}
override func tableView(tableView: UITableView?, canEditRowAtIndexPath indexPath: NSIndexPath?) -> Bool {
return true
}
override func tableView(tableView: UITableView!, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath!) {
let request = NSFetchRequest(entityName: "Person")
request.returnsObjectsAsFaults = false
let appDelegate:AppDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
let context:NSManagedObjectContext = appDelegate.managedObjectContext
if editingStyle == .Delete {
context.deleteObject(results[indexPath.row])
context.save(nil)
}
results.removeAtIndex(indexPath.row)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
}
func filterContentForSearchText (searchText: String) {
searchResults = results.filter{
($0.lastname as NSString).localizedCaseInsensitiveContainsString("\(searchText)")
}
}
func searchDisplayController(controller: UISearchDisplayController!, shouldReloadTableForSearchString searchString: String!) -> Bool {
self.filterContentForSearchText (searchString)
return true
}
}
In the functions prepareForSegue condition self.tableView == self.searchDisplayController.searchResultsTableView is not fulfilled.
Always assigns indexPath = self.tableView.indexPathForSelectedRow() instead indexPath = self.searchDisplayController.searchResultsTableView.indexPathForSelectedRow(). This causes an error in case of selecting a row in the search result.
Link to project on Dropbox: https://www.dropbox.com/s/bqv46nkoa4s3ibg/lesson-12-2-swift%203.zip
self.tableView is the main table view, so the condition
if self.tableView == self.searchDisplayController.searchResultsTableView
will never be true. But you can check if the search is active or not:
if self.searchDisplayController.active {
// index path from search table ...
} else {
// index path from main table ...
}

Resources