Try to delete tableViewCell in swift XIB. but there is error cannot remove models
viewModel :
private var combinedItems: [combinedInboxModel] = []
func get(){} // Add combinedItems data
View Controller
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
viewModel.combinedItems.remove(at: indexPath.row) // No exact matches in call to instance method 'remove' in swift
tableView.deleteRows(at: [indexPath], with: .fade)
}
}
Why there is error remove here?
Related
I am trying to delete data in my Firebase database and then listen for .childRemoved so I can update the TableView. I originally tried to remove the object from the array using the code below but got an index out of range error in cellForRowAt in indexPath.row.
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let post = posts[indexPath.row]
posts.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
DataService.ds.REF_POSTS.child(post.postKey).removeValue()
DataService.ds.REF_USERS.child("\(uid!)").child("posts").child("\(post.postKey)").removeValue()
//print("deleted post \(deletedPost)")
} else if editingStyle == .insert {
}
}
So, I decided to try to delete the Firebase data first and then listen at .childRemoved with the following combination of code (I commented out the removal of array objects in the editingStyle code).
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let post = posts[indexPath.row]
//posts.remove(at: indexPath.row)
//tableView.deleteRows(at: [indexPath], with: .fade)
DataService.ds.REF_POSTS.child(post.postKey).removeValue()
DataService.ds.REF_USERS.child("\(uid!)").child("posts").child("\(post.postKey)").removeValue()
//print("deleted post \(deletedPost)")
} 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.
}
}
Then, I created a listener at .childRemoved in ViewDidLoad with the following.
newPost.observe(.childRemoved, with: { (snapshot) in
//print("CHILD REMOVED")
//print("\(self.posts)")
//print("\(snapshot.postKey)")
let postId = snapshot.key
if let indexNew = self.posts.firstIndex(where: {$0.postKey == postId}){
self.posts.remove(at: indexNew)
for n in 0...self.posts.count - 1 {
print("POST \(n) - \(self.posts[n])")
}
print("REMOVE INDEX - \(indexNew)")
print("POSTS AFTER REMOVAL - \(self.posts)")
self.feedTableView.reloadData()
}
}, withCancel: nil)
I am still receiving the index out of range error at cellForRowAt on indexPath.row. Am I on the right track with the second method? If so, what am I doing wrong to cause the out of range error?
EDIT: I am adding my cellForRowAt where I am getting the index out of range error and most updated commit editingStyle. I'm not sure how I am running over the array since the debug statements print
INDEX OF POST ARRAY - 2
POSTS: SocialSpirit.Post
POSTS AFTER DELETE [SocialSpirit.Post, SocialSpirit.Post, SocialSpirit.Post]
INDEX OF POST ARRAY - 0
Fatal error: Index out of range
And here is the code:
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let post = posts[indexPath.row]
DataService.ds.REF_POSTS.child(post.postKey).removeValue()
DataService.ds.REF_USERS.child("\(uid!)").child("posts").child("\(post.postKey)").removeValue()
posts.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
print("POSTS AFTER DELETE \(self.posts)")
//print("deleted post \(deletedPost)")
} 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 tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("INDEX OF POST ARRAY - \(indexPath.row)")
print("POSTS: \(posts[indexPath.row])") //Index out of range error here
let post = posts[indexPath.row]
if let cell = tableView.dequeueReusableCell(withIdentifier: "PostCell", for: indexPath) as? PostCell{
if let img = FeedViewController.imageCache.object(forKey: post.imageUrl as NSString) {
cell.configureCell(post: post, img: img)
} else {
cell.configureCell(post: post)
}
return cell
} else {
return PostCell()
}
}
The function this way removes the index error and correctly updates the tableView
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == .delete) {
let post = posts[indexPath.row]
DataService.ds.REF_USERS.child("\(uid!)").child("posts").child("\(post.postKey)").removeValue()
}
}
I have a dictionary that I have made called places and I made each cell in the tableViewController show each parts of the dictionary. I know the function to delete the rows in the controller, but when I run the app and do the action of deleting nothing happens.
// This is my entire TableViewController. I have another ViewController that appends the dictionary.
var places = [Dictionary<String,String>()]
var activePlace = -1
class TableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
if UserDefaults.standard.object(forKey: "places") != nil { //checks if the list is not empty
places = UserDefaults.standard.object(forKey: "places") as! [Dictionary<String, String>]
}
if places.count == 1 {
places.remove(at: 0)
places.append(["name":"Ex. Eiffel Tower", "lat": "48.858324", "lon": "2.294764"])
}
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, canEditRowAt
indexPath: IndexPath) -> Bool {
return true
}
override func numberOfSections(in 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 places.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
DispatchQueue.main.async {
self.tableView.reloadData()
}
cell.textLabel?.text = places[indexPath.row]["name"]
return cell
}
override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
activePlace = indexPath.row
return indexPath
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "newPlace" {
activePlace = -1
}
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
tableView.deleteRows(at: [indexPath], with: .bottom)
places.remove(at: indexPath.row)
UserDefaults.standard.setValue(places, forKey: "places")
}
}
I am expecting that when I make the action of swiping to the left that it would delete the row and the contents of the cell from the tableView. Then it would also delete from the dictionary.
It's very complicate to delete table view cell sometimes. Your code is correct but you just need to remove a line. Instead of calling tableview.deleteRows you just delete the item of your dictionary and reload the table view.
Enable the table rows to editable using canEditRowAt function....
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
places.remove(at: indexPath.row)
UserDefaults.standard.setValue(places, forKey: "places")
tableView.reloadData()
}
}
Move deleteRows(at:with:) after remove(at:) in tableView(_: commit: forRowAt:) method, i.e.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
places.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .bottom) //here...
UserDefaults.standard.setValue(places, forKey: "places")
}
}
The main issue is the wrong declaration of the data source array. The pair of parentheses must be behind the brackets
var places = [Dictionary<String,String>]()
In the method tableView(_:commit:forRowAt:the order is wrong. First remove the row from the data source array then delete the row.
Two Don'ts
Do not use setValue:forKey with UserDefaults to save a single value.
Do not declare the data source array outside of the class.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
places.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .bottom)
UserDefaults.standard.set(places, forKey: "places")
}
}
Solved:
DispatchQueue.main.async was creating an endless loop of constantly reloading the data. By removing that the two functions of editing were allowed to run. I was able to perform the deleting action.
How can I get the marked key then remove it from UITableView swift 3 ?
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
self.grocery.remove(at: indexPath.row)
self.tableView.deleteRows(at: [indexPath], with: .automatic)
}
}
To remove you can simply set the value as nil to remove it from firebase. After you've called the remove function then make sure to remove the row from the tableView.
func removeObjectFromFireBase(userKey: String, removeThisGrocery: String) {
let ref = Database.database().reference()
let groceryRef = ref.child("Users").child(userKey).child("Grocery")
groceryRef.child(removeThisObject).setValue(nil)
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let deleteAction = UITableViewRowAction(style: .destructive, title: "DELETE") { (rowAction, indexPath) in
//Call the removeFromFirebase function with the required parameters
removeObjectFromFirebase(userKey: userKey, removeThisGrocery: groceryKey)
tableView.deleteRows(at: [indexPath], with: .automatic)
}
deleteAction.backgroundColor = UIColor.red
return [deleteAction, addAction]
}
You can remove any data from Firebase as following:
let ref = Database.database().reference()
let groceryRef = ref.child("Users").child(userKey).child("Grocery")
groceryRef.removeValue()
So I'm trying to delete data from a tableView, which It will delete the cell at the row, but it won't delete the information from coreData, causing it to load again when I call a .reloadData(). I'm really new to coredata and I don't know how to select a specific Recipe item that I make.
Here's where I handle the delete:
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == UITableViewCellEditingStyle.delete) {
// handle delete (by removing the data from your array and updating the tableview)
recipes.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: UITableViewRowAnimation.automatic)
}
}
Here's how I am creating the items in coreData, if this helps. Also, I have a Git repository here if anyone's willing to look that deep
#IBAction func createNewRecipeButton(_ sender: Any) {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let newRecipe = Recipe(context: context)
newRecipe.title = recipeTitleTextBox.text
newRecipe.instructions = recipeInstructionsTextBox.text
newRecipe.time = recipeTimeTextBox.text
(UIApplication.shared.delegate as! AppDelegate).saveContext()
navigationController!.popToRootViewController(animated: true)
}
Your current removal method merely deletes the recipe from the storage array. You need to tell the context to get rid of it as well…
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == UITableViewCellEditingStyle.delete) {
let recipe = recipes.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: UITableViewRowAnimation.automatic)
guard let moc = recipe.managedObjectContext else { return }
moc.delete(recipe)
moc.processPendingChanges()
}
}
You might also want to look into using a NSFetchedResultsController. There are several tutorials around.
I have a table of names and I am making a swipe and delete function for them which removes them from a names variable which is an array.
I selected the functions which most closely resembled the tutorial inside xcode and filled them out, but my app crashes randomly when I click the delete button. Here is my code for the delete button:
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let deleteAction = UITableViewRowAction(style: .destructive, title: "Delete") { (rowAction: UITableViewRowAction, indexPath: IndexPath) -> Void in
print("Deleted")
self.catNames.remove(at: indexPath.row)
self.tableView.deleteRows(at: [indexPath], with: UITableViewRowAnimation.automatic)
self.tableView.reloadData()
}
}
I'm new to coding and learning swift, I am following a tutorial for swift 2 and working with swift 3 so there are a few issues I have when following along, this being one I'm properly stuck on.
Works for Swift 3 and Swift 4
Use the UITableViewDataSource tableView(:commit:forRowAt:) method, see also this answer here:
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
print("Deleted")
self.catNames.remove(at: indexPath.row)
self.tableView.deleteRows(at: [indexPath], with: .automatic)
}
}
First you need to add this function
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
then your function is ok but no need of tableview reload data just call tableview.beingUpdates and tableview.endUpdates
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
print("Deleted")
self.catNames.remove(at: indexPath.row)
self.tableView.beginUpdates()
self.tableView.deleteRows(at: [indexPath], with: .automatic)
self.tableView.endUpdates()
}
}
In Swift 4 Try This
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == .delete) {
arrStudentName.remove(at: indexPath.row)
tableView.beginUpdates()
tableView.deleteRows(at: [indexPath], with: .middle)
tableView.endUpdates()
}
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
self.animals.remove(at: indexPath.row)
self.dataDisplayTbleView.reloadData()
}
}
**
for swift 3
**
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
print("Deleted")
self.catNames.remove(at: indexPath.row)
self.tableView.deleteRows(at: [indexPath], with: .automatic)
}
}
My answer for you
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool
{
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath)
{
if editingStyle == .delete
{
array.remove(at: indexPath.row)
tblDltRow.reloadData()
}
}
You need to refresh the table
You don't have to reload the tableView.
Before you use tableView.deleteRows(at: [IndexPath], with: UITableView.RowAnimation) you must substrate the number of rows you want delete in numberOfRowsInSection
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return //Your current number of rows - The number of rows to delete
}
Normally the number of rows is the number of Items from an array (yourArray.count), that means you can delete the items from this array before you delete the cells and you get the same result.
self.tableView.beginUpdates()
self.tableView.deleteRows(at: [ IndexPath(row: index, section: section) ], with: .fade)
self.remove_object_from_model()
self.tableView.endUpdates()
For Swift 5
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
print("Deleted")
self.catNames.remove(at: indexPath.row)
self.tableView.deleteRows(at: [indexPath], with: .automatic)
}
}