InsertRow with NSFetchedResultsControllerdelegate - uitableview

This should be trivial but I can't get it working.
I want to move a row in a UITableView in a NSFetchedResultsControllerDelegate.
I can easily delete rows, but inserting doesn't seem to work (NSInternalInconsistencyException).
I am trying
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let destinationindex = IndexPath(item: 0, section: 0)
tableView.deleteRows(at: [indexPath], with: .fade)
tableView.insertRows(at: [destinationindex], with: .fade)
}
I have also tried
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let delobject = fetchedResultsController?.object(at: indexPath)
fetchedResultsController?.managedObjectContext.delete(delobject!)
fetchedResultsController?.managedObjectContext.insert(delobject!)
}
Of course a simple delete works; for example swipe to delete:
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
if (indexPath.row != 0) {
let delobject = fetchedResultsController?.object(at: indexPath)
fetchedResultsController?.managedObjectContext.delete(delobject!)
}
}
}

Your data model must fit the tableView so number of items in data equals number of rows in tableView.
A FetchedResultsController based TableView reflects the results of a CoreData fetch request. Inserting a row in the UITableView means inserting a new entry in CoreData.
And that is what you want to do: Create a new entry in Core Data (Like you do already do when deleting a row).

Related

How to add swipe to remove table cells from UITableViewController in Swift?

There aren't very many tutorials for this for Swift 5.1 and I am pretty novice. I am wondering how to make the tableView delete override func work for my code. It gives me an error on objects because they are an unresolved identifier. Also what would go in the insert?
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")
let mainImageView = cell?.viewWithTag(2) as! UIImageView
mainImageView.image = posts[indexPath.row].mainImage
let mainLabel = cell?.viewWithTag(1) as! UILabel
mainLabel.text = posts[indexPath.row].name
return cell!
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
objects.remove(at: indexPath.row) // Here I get an error because objects is an unresolved identifier.
tableView.deleteRows(at: [indexPath], with: .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. I am also confused as to what goes here.
}
}
All of the information you will need can be found here.
This method allows the delegate to customize the editing style of the cell located atindexPath. If the delegate does not implement this method and the UITableViewCell object is editable (that is, it has its isEditing property set to true), the cell has the UITableViewCell.EditingStyle.delete style set for it.
This is what you are missing:
https://developer.apple.com/documentation/uikit/uitableviewdelegate/1614869-tableview
override func tableView(_ tableView: UITableView,
editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
return .delete
}
Here you are using an object to remove data for the index path which is wrong you are using "posts" named array to show values on the table view. So, this "posts" array contains objects which are visible on the table view.
So, here when you want to remove object then this means you have to remove objects from the "posts" array. Below the attached function with some changes, you can check this it will work for you.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
posts.remove(at: indexPath.row) // Here I get an error because objects is an unresolved identifier.
tableView.deleteRows(at: [indexPath], with: .fade)
} else if editingStyle == .insert {
posts.append("Here insert what you want") //inside append you can append/insert values to the array
tableView.reloadData()
}
}

How to create info button in UITableViewCell?

How can i create info button on the right side for editing the content along with the deletion control(left side) whenever i click edit button?
(something like this)
`
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
objects.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .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.
}
}`
Use accessoryType.
In cellForRowAt method, add
cell.editingAccessoryType = .detailButton
and Give action to it by
override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
print("Accessory btn tapped")
}

TableView row needs to de deleted twice before isn't removes form the UI?

I have got to the point where I can add cells that all have different labels. But, when the row is swiped to be deleted it will only delete the text associated with the cell and not the cell itself. I've tried to add SavedMessages.deleteRows(at: [indexPath], with: .automatic) inside the if statement but every time the app tries to delete the row, the app crashes.
Here is the code:-
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath){
if editingStyle == UITableViewCellEditingStyle.delete {
let delete: NSFetchRequest<Messages> = Messages.fetchRequest()
do {
var deleteMessage = try
PersistenceServce.context.fetch(delete)
PersistenceServce.context.delete(messages.remove(at: indexPath.row))
PersistenceServce.saveContext()
self.messages = deleteMessage
SavedMessages.reloadData()
} catch {}
}
}
Thanks in advance!
Maybe you are not using
tableView.cellForRow(at: indexPath)?.removeFromSuperview()
I created an example where removing data as well as the cell is getting removed perfectly fine.
var data = [1,2,3,4,5]
override func tableView(_ tableView: UITableView, numberOfRowsInSection
section: Int) -> Int
{
return data.count
}
override func tableView(_ tableView: UITableView,
commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == UITableViewCellEditingStyle.delete
{
data.remove(at: indexPath.row) //removing the value from data source.
tableView.cellForRow(at: indexPath)?.removeFromSuperview() //removing the cell
tableView.reloadData() //reloading Table data.
}
}

When i remove cell from TableView on button action the app is crashed - iOS Swift

im trying to remove a cell from a TableViewController. Each time i swipe and press the delete button a crash occur.
This is the crash log:
Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: 'Invalid update: invalid
number of rows in section 0. The number of rows contained in an
existing section after the update (10) must be equal to the number of
rows contained in that section before the update (10), plus or minus
the number of rows inserted or deleted from that section (0 inserted,
1 deleted) and plus or minus the number of rows moved into or out of
that section (0 moved in, 0 moved out).'
and this is my UITableView functions.
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let myCell = tableView.dequeueReusableCellWithIdentifier(cellID, forIndexPath: indexPath) as! PredefinedServicesCell
//Cell configuration.
myCell.selectionStyle = .None
myCell.containerView.layer.borderColor = UIColor(hex: 0x3399CC).CGColor
myCell.containerView.layer.borderWidth = 1
myCell.containerView.layer.cornerRadius = 10
myCell.containerView.clipsToBounds = true
myCell.servicePrice.text = "\(indexPath.row)"
myCell.serviceCurrency.text = "KWD"
myCell.serviceTitle.text = "Building iOS Application"
myCell.serviceDescription.text = "This is a service description This is a service description This is a service description This is a service description This is a service description This is a service description This is a service description This is a service description This is a service description"
return myCell
}
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
}
}
what might the reason be?
Please dynamically add your numberOfRowsInSection. It's not static.
use array insted of return 10. and remove objec when you delete in editing style.
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return yourArray.count
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
// remove object from array here
}
}
reason behind error: in your case
firs your no of table cell count is 10.
When you are delete cell your cell is deleted but count is 10. It's worng otherwise decrease your count to 9.
Keep a record of number of deleted cells and delete the same amount from array(used to populate table view) as well.
Once you remove the rows from the tableview, you must have to update your numberOfRowsInSection value also. That's why you are getting crash.
let rows=10
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return rows
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let myCell = tableView.dequeueReusableCellWithIdentifier(cellID, forIndexPath: indexPath) as! PredefinedServicesCell
//Cell configuration.
myCell.selectionStyle = .None
myCell.containerView.layer.borderColor = UIColor(hex: 0x3399CC).CGColor
myCell.containerView.layer.borderWidth = 1
myCell.containerView.layer.cornerRadius = 10
myCell.containerView.clipsToBounds = true
myCell.servicePrice.text = "\(indexPath.row)"
myCell.serviceCurrency.text = "KWD"
myCell.serviceTitle.text = "Building iOS Application"
myCell.serviceDescription.text = "This is a service description This is a service description This is a service description This is a service description This is a service description This is a service description This is a service description This is a service description This is a service description"
return myCell
}
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
rows-=1
}
}
Following the crash log, you should update number of rows after editing
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
You cannot return the number of cell as 10, after deleting the cell. It should be less the number of cell before deletion.
You have to update your data source to match the number of row/section of your tableview, and use beginUpdate & endUpdate to update your table view display. Like so:
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
tableView.beginUpdate() //optional
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
//TODO ... add your code to update data source here
tableView.endUpdate() //optional
}
}
You cannot declare a static value to your numberOfRowsInSection and then change it (10 - 1) by removing a row with deleteRowsAtIndexPaths, numberOfRowsInSection remain 10 and your rows become 9 so you have this kind of issue, make these changes to have a dynamic value:
var totalRows: Int = 10
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.totalRows
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
if self.totalRows > 0 { // This limit your deletion to avoid a crash
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
self.totalRows -= 1
}
}
}
Usually It's better to handle a datasource array to maintain your informations to show in each cell, so think to use an array to do it so you can use it's count property and remove it's element when you delete a cell.
Your numberOfRowsInSection should be dynamic not static. So, remove data in the dynamic array with yourdataName.remove(at:indexPath.row)
Example;
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return yourdataName.count;
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == UITableViewCellEditingStyle.delete {
yourdataName.remove(at:indexPath.row)
tableView.deleteRows(at: [indexPath], with: UITableViewRowAnimation.automatic)
}
}

parse.com swift - unpin and delete object in tableView cell

im making an app that currently saves a parse object to the local datastore, and to parse.com. I then run a query, save each object into an array so that i can display it in a table view. everything up to this point works well thanks to my past posted questions here and here.
so now the info is being displayed correctly as i had hoped. but now, i would like to be able to delete the entries as well. I would like to swipe the table and when delete is tapped, the object is unpinned from the local datastore, and that same item is removed from the cloud as well.
here is my current code for this :
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// var lighthouse = self.lighthouses[indexPath.row]
var data = self.arrayToPopulateCells[indexPath.row]
let cell = tableView.dequeueReusableCellWithIdentifier("locationCell") as! lighthouseCell
let row = indexPath.row
cell.cellName.text = data.name
cell.cellPlace.text = data.locality
cell.cellDate.text = "\(data.date)"
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.selectedLighthouse = self.arrayToPopulateCells[indexPath.row]
self.performSegueWithIdentifier("lighthouseDetailViewSegue", sender: self)
}
func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if (editingStyle == UITableViewCellEditingStyle.Delete) {
self.arrayToPopulateCells.removeAtIndex(indexPath.row)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
}
}
where "arrayToPopulateCells" is where the parse objects get stored during my query.
obviously im able to remove the cell at indexpath.row, but how to i take the information in that cell, and find the matching parse object to unpin and delete?
Using this delegate u can get reference of lighthouseCell and using that u can get variable and
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if (editingStyle == UITableViewCellEditingStyle.Delete) {
var selectedCell = tableView.cellForRowAtIndexPath(indexPath) as lighthouseCell
gameScore.removeObjectForKey("playerName")
self.arrayToPopulateCells.removeAtIndex(indexPath.row)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
}
}
using selectedCell and then
selectedCell.data.removeObjectForKey("keyName")
and for delete parse data we have Delete Object for particular key
Assuming the objects in your array are actual PFObject instances, then just call deleteInBackground before you remove them from your data array.
So:
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if (editingStyle == UITableViewCellEditingStyle.Delete) {
self.arrayToPopulateCells[indexPath.row].deleteInBackground() // Delete from cloud
self.arrayToPopulateCells.removeAtIndex(indexPath.row)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
}
}

Resources