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 ...
}
Related
I'm using Swift 3 in Xcode 8 beta 6, targeting iOS 10.0. I am implementing a simple UISearchController in a UITableView backed with an NSFetchedResultsController. I have two properties
var patients = [Patient]() // Assigned to fetchedResultsController.fetchedObjects when the fetch is performed, and when the moc is updated.
var searchResults = [Patient]()
In my updateSearchResults(for searchController: UISearchController) method, I do this:
func updateSearchResults(for searchController: UISearchController) {
if let searchText = searchController.searchBar.text {
self.searchResults = people.filter {
return $0.lastName!.localizedCaseInsensitiveContains(searchText)
}
Using breakpoints, I've identified that the code gets as far as the filter method, but doesn't enter it, failing with:
fatal error: NSArray element failed to match the Swift Array Element type
I've looked at a bunch of the other SO questions involving this error, but none have helped. I've also tried explicitly casting people in the updateSearchResults method, but no luck. Thoughts?
UPDATE Complete code for tableViewController and Patient subclass:
import UIKit
import CoreData
class PatientsListViewController: UITableViewController, NSFetchedResultsControllerDelegate, UISearchResultsUpdating {
enum SegueIdentifier: String {
case showPatientDetail
}
//MARK: Properties
var managedObjectContext: NSManagedObjectContext!
var fetchedResultController: NSFetchedResultsController<Patient>!
var searchController: UISearchController!
var searchResults: [Patient] = []
var patients: [Patient] = []
override func viewDidLoad() {
super.viewDidLoad()
let fetchRequest: NSFetchRequest<Patient> = Patient.fetchRequest()
let sortDescriptor = NSSortDescriptor(key: "lastName", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
fetchedResultController = NSFetchedResultsController<Patient>(fetchRequest: fetchRequest, managedObjectContext: managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
fetchedResultController.delegate = self
do{
try fetchedResultController.performFetch()
patients = fetchedResultController.fetchedObjects!
}catch{
print(error)
}
//Add Search bar to the table header
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.
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
guard let numberOfSections = fetchedResultController.sections?.count else {
return 0
}
return numberOfSections
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// let section = fetchedResultController.sections![section]
// let numberOfRows = section.numberOfObjects
if searchController.isActive {
return searchResults.count
} else {
return fetchedResultController.sections![section].numberOfObjects
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: PatientCell.reuseIdentifier, for: indexPath) as! PatientCell
let patient = (searchController.isActive) ? searchResults[indexPath.row] : fetchedResultController.object(at: indexPath)
cell.configure(with: patient)
return cell
}
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
if searchController.isActive{
return false
}else{
return true
}
}
override func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let deleteAction = UITableViewRowAction(style: .destructive, title: "Delete") { (action, indexPath) -> Void in
let patientToDelete = self.fetchedResultController.object(at: indexPath)
self.managedObjectContext.delete(patientToDelete)
do{
try self.managedObjectContext.save()
}catch{
print(error)
}
}
return [deleteAction]
}
// MARK: - FetchedResultsController delegate
// Notify the tableView that updates will begin
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.beginUpdates()
}
//Cover all cases of row changes like move, delete, insert, update
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch type{
case .insert:
if let newIndexPath = newIndexPath{
tableView.insertRows(at: [newIndexPath], with: .fade)
}
case .delete:
if let indexPath = indexPath{
tableView.deleteRows(at: [indexPath], with: .fade)
}
case .update:
if let indexPath = indexPath{
tableView.reloadRows(at: [indexPath], with: .fade)
}
case .move:
break
}
patients = controller.fetchedObjects as! [Patient]
}
// Notify the tableView that updates are done
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.endUpdates()
}
//Pass Patient to PatientDetailViewController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let identifier = segue.identifier.flatMap(SegueIdentifier.init) else { return }
switch identifier {
case .showPatientDetail:
guard let indexPath = tableView.indexPathForSelectedRow else {
fatalError("No row selected in tableView")
}
let destinationController = segue.destination as! PatientDetailViewController
destinationController.patient = (searchController.isActive) ? searchResults[indexPath.row] : fetchedResultController.object(at: indexPath)
}
}
//Implement Search Bar
func filterContent(for searchText:String) {
searchResults = patients.filter( { patient -> Bool in
let nameMatch = patient.lastName?.localizedCaseInsensitiveContains(searchText)
return nameMatch != nil
})
}
func updateSearchResults(for searchController: UISearchController) {
if let searchText = searchController.searchBar.text {
filterContent(for: searchText)
tableView.reloadData()
}
}
}
PATIENT:
#objc(Patient)
public class Patient: NSManagedObject {
}
extension Patient {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Patient> {
return NSFetchRequest<Patient>(entityName: "Patient");
}
#NSManaged public var address: String?
#NSManaged public var dateOfBirth: String?
#NSManaged public var firstName: String?
#NSManaged public var gender: String?
#NSManaged public var lastName: String?
}
I am creating a simple iPhone app using UITableView. I am using the default MasterDetail application template. Right now (in Edit mode) when I press any of the table cells nothing happens. However, when I am in normal mode the detail segue is initiated. How to override the Edit mode so that I initiate a custom segue to go to a different UIViewController.
P.S.: I still want to preserve the inherit delete functionality.
This is my code in my MasterViewController:
class MasterViewController: UITableViewController {
let kFileName: String = "/resolutionData.plist"
var resolutions = [Dictionary<String,String>]()
var achievedResolutions = [Dictionary<String,String>]()
// TO DO create a class to get this array
let iconArray = ["Fish","Fly","Heart","HelpingHand","Melon","Star","Tentacles","Volunteering"]
override func awakeFromNib() {
super.awakeFromNib()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.navigationItem.leftBarButtonItem = self.editButtonItem()
let addButton = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: "insertNewObject:")
self.navigationItem.rightBarButtonItem = addButton
//extract the path
NSLog(dataFilePath())
/**
* Check where is the sandbox of the application
* and if there is read from the data file and story it to "objects" array
*/
if NSFileManager.defaultManager().fileExistsAtPath(dataFilePath()){
var temp = NSArray(contentsOfFile: dataFilePath()) as! [Dictionary<String,String>]
for res in temp{
if res["isAchieved"] == "Y"{
achievedResolutions.append(res)
}else{
resolutions.append(res)
}
}
//... if there is not - create it
} else {
let data = [["name":"Resolution name test","startingDate":"24-11-15","achievingDate":"01-01-2016","icon":iconArray[0],"isAchieved":"N"] as NSDictionary] as NSArray
//if the file does not exist...
if !NSFileManager.defaultManager().fileExistsAtPath(dataFilePath()){
//... create it
NSFileManager.defaultManager().createFileAtPath(dataFilePath(), contents: nil, attributes: nil)
}
//write to it
data.writeToFile(dataFilePath(), atomically: true)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func insertNewObject(sender: AnyObject) {
performSegueWithIdentifier("editDetails", sender: sender)
}
func saveDateToFile(){
let data = resolutions as NSArray
data.writeToFile(dataFilePath(), atomically: true)
}
func notifyTableViewForNewInsertion(){
let indexPath = NSIndexPath(forRow: 0, inSection: 0)
self.tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
}
// MARK: - Segues
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow() {
let object = resolutions[indexPath.row] as Dictionary
(segue.destinationViewController as! DetailViewController).detailItem = object
}
}
}
// MARK: - Table View
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section == 0 {
return "Active"
}else{
return "Achieved"
}
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return resolutions.count
}else{
// TODO replace that with an actual array count
return achievedResolutions.count
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
if indexPath.section == 0{
let object: AnyObject? = resolutions[indexPath.row] ["name"]
cell.textLabel!.text = object as? String
cell.detailTextLabel!.text = resolutions[indexPath.row]["achievingDate"]
cell.imageView!.image = UIImage(named: resolutions[indexPath.row]["icon"]!)
} else {
let object: AnyObject? = achievedResolutions[indexPath.row] ["name"]
cell.textLabel!.text = object as? String
cell.detailTextLabel!.text = resolutions[indexPath.row]["achievingDate"]
cell.imageView!.image = UIImage(named: resolutions[indexPath.row]["icon"]!)
}
return cell
}
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
resolutions.removeAtIndex(indexPath.row)
saveDateToFile()
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.
}
}
func dataFilePath() -> String{
let paths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
//get the first path and convert it to str
let docDicrectyory: String = paths[0] as! String
return "\(docDicrectyory)\(kFileName)"
}
}
Instead of performing your segue directly from the storyboard, add a UITableViewDelegate method:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if tableView.editing {
performSegueWithIdentifier("id_Segue_Editing_VC", sender: tableView.cellForRowAtIndexPath(indexPath))
} else {
performSegueWithIdentifier("id_Segue_Standard_VC", sender: tableView.cellForRowAtIndexPath(indexPath))
}
}
Set the sender to the selected cell -- this matches the default UIKit behavior.
Then in your prepareForSegue method you can add custom value to your view controllers according to the segue identifier.
Override willBeginEditingRowAtIndexPath: function. this willbe call before start editing. there you can initialize a global variable.
#property(nonatomic, String) BOOL editing
and in the
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if (editing) {
if([[segue identifier] isEqualToString:#"identifierOne"]){
}else if([[segue identifier] isEqualToString:#"identifierTwo"]){
}
}
}
There is a table view property for that:
self.tableView.allowsSelectionDuringEditing = YES;
import UIKit
class MasterTableViewController: UITableViewController, PFLogInViewControllerDelegate, PFSignUpViewControllerDelegate, UISearchBarDelegate, UISearchDisplayDelegate {
#IBOutlet var searchBar: UISearchBar!
// creating array for holding ojects
var noteObjects: NSMutableArray! = NSMutableArray()
var v = 0
var searchActive : Bool = false
var data:[PFObject]!
var filtered:[PFObject]!
override func viewDidLoad() {
super.viewDidLoad()
searchBar.delegate = self
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if v == 0 {
self.fetchAllObjectsFromLocalDataStore()
//self.fetchAllObjects()
}
}
// fetching data from local datastrore and from parse
func fetchAllObjectsFromLocalDataStore(){
let query: PFQuery = PFQuery(className: "className")
query.orderByDescending("createdAt")
query.fromLocalDatastore()
query.findObjectsInBackgroundWithBlock { (var objects, error) -> Void in
self.search()
if (error == nil) {
let temp: NSArray = objects as! NSArray
self.noteObjects = temp.mutableCopy() as! NSMutableArray
self.search()
self.tableView.reloadData()
}else {
print(error!.userInfo)
}
}
}
func fetchAllObjects(){
let query: PFQuery = PFQuery(className: "className")
query.orderByDescending("createdAt")
search()
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if (error == nil) {
PFObject.pinAllInBackground(objects, block: nil )
self.fetchAllObjectsFromLocalDataStore()
// self.tableView.reloadData()
} else {
print(error?.userInfo)
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return self.noteObjects.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! MasterTableViewCell
let object : PFObject = self.noteObjects.objectAtIndex(indexPath.row) as! PFObject
cell.MasterTitleLabel?.text = object["Title"] as? String
cell.MasterTextLabel.text = object["Fstory"] as? String
cell.MasterTimeLabel.text = object["Time"] as? String
cell.MasterLocationLabel.text = object["Location"] as? String
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("openStory", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let upcoming: AddNoteTableViewController = segue.destinationViewController as! AddNoteTableViewController
if (segue.identifier == "openStory"){
let indexPath = self.tableView.indexPathForSelectedRow!
let object: PFObject = self.noteObjects.objectAtIndex(indexPath.row) as! PFObject
upcoming.object = object
self.tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
}
#IBAction func btnReload(sender: AnyObject) {
fetchAllObjects()
}
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if (editingStyle == UITableViewCellEditingStyle.Delete ){
let object : PFObject = self.noteObjects.objectAtIndex(indexPath.row) as! PFObject
// the below for deleting the selected cell's object from server's database
// object.deleteInBackground()
//the below for deleting the selected cell's object from localstorage
object.unpinInBackground()
self.noteObjects.removeObjectAtIndex(indexPath.row)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
}
}
func search(searchText: String? = nil){
let query = PFQuery(className: "className")
if(searchText != nil){
query.whereKey("Title", containsString: searchText)
}
query.findObjectsInBackgroundWithBlock { (results, error) -> Void in
self.data = results! as [PFObject]
self.tableView.reloadData()
}
}
func searchBarTextDidBeginEditing(searchBar: UISearchBar) {
searchActive = true;
}
func searchBarTextDidEndEditing(searchBar: UISearchBar) {
searchActive = false;
}
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
searchActive = false;
}
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
searchActive = false;
}
}
the above code is for retrieving parse's objects and for implementing the search bar so that i can search my objects via search function but i don't what am missing or how to it properly if anybody knows than please help me
you can try something like this using UIsearchBar
class TableViewController: UITableViewController, UISearchBarDelegate {
#IBOutlet var searchBar: UISearchBar!
var userList:NSMutableArray = NSMutableArray()
var noteObjects: NSMutableArray = NSMutableArray()
override func viewDidLoad() {
super.viewDidLoad()
searchBar.delegate = self
self.fetchAllObjectsFromLocalDataStore()
}
func loadUsers(name:String){
var findUsers:PFQuery = PFUser.query()!
if !name.isEmpty{
findUsers.whereKey("username", containsString: name)
findUsers.whereKey("username", containsString: name .lowercaseString)
let user = PFUser.currentUser()
if let user = PFUser.currentUser() {
findUsers.whereKey("institute", equalTo: user["institute"])
}
}
findUsers.fromLocalDatastore()
findUsers.findObjectsInBackgroundWithBlock { ( objects, error) -> Void in
if (error == nil) {
self.userList = NSMutableArray(array: objects!)
self.tableView.reloadData()
}else {
print(error!.userInfo)
}
}}
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
loadUsers(searchText)
self.searchBar.setShowsCancelButton(true, animated: true)
}
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
loadUsers("")
self.searchBar.setShowsCancelButton(false, animated: true)
self.searchBar.endEditing(true)
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
if searchBar.text == "" {
return noteObjects.count
} else {
return userList.count }
//self.noteObjects.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! UsersTableViewCell
if searchBar.text == "" {
let object : PFObject = self.noteObjects.objectAtIndex(indexPath.row) as! PFObject
let photo: PFFile = object["photo"] as! PFFile
photo.getDataInBackgroundWithBlock{
(imageData:NSData?, error:NSError?)-> Void in
if (error == nil){
let image:UIImage = UIImage(data: imageData!)!
cell.imgViewUser.image = image
}
else if error != nil{
print("error")
}
}
cell.lblUserInterest.text = object["interest"] as? String
//cell.imgViewUser.image = object["photo"] as? PFFile
cell.lblUsername.text = object["username"] as? String
return cell
} else {
let object : PFObject = self.userList.objectAtIndex(indexPath.row) as! PFObject
let photo: PFFile = object["photo"] as! PFFile
photo.getDataInBackgroundWithBlock{
(imageData:NSData?, error:NSError?)-> Void in
if (error == nil){
let image:UIImage = UIImage(data: imageData!)!
cell.imgViewUser.image = image
}
else if error != nil{
print("error")
}}
cell.lblUserInterest.text = object["interest"] as? String
//cell.imgViewUser.image = object["photo"] as? PFFile
cell.lblUsername.text = object["username"] as? String
return cell
}
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
var object :AnyObject?
}
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if (editingStyle == UITableViewCellEditingStyle.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
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
}
}