I want to refresh my conversations (table view cells) after deleting a conversation through a swipe action sheet.
I tried to reload the table view after deleting the data but it doesn't work. Also with a async.
// Swipe Action Sheet solution
func tableView(_ tableView: UITableView,
trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?
{
// Write action code for the Flag
let deleteAction = UIContextualAction(style: .normal, title: "Löschen", handler: { (ac:UIContextualAction, view:UIView, success:(Bool) -> Void) in
let ref = Database.database().reference()
ref.child("users").child((self.currentUser?.uid)!).child("conversations").child((self.items[indexPath.row].user.uid)!).removeValue()
DispatchQueue.main.async {
self.tableView.reloadData()
}
success(true)
})
deleteAction.backgroundColor = .red
return UISwipeActionsConfiguration(actions: [deleteAction])
}
Thanks in advance for your help!
You have to reload data in completion block of removeValue method if there isn't any error. Also before you reload data you have to remove item from items array
.removeValue { error,_ in
if error == nil {
self.items.remove(at: indexPath.row)
self.tableView.reloadData() // self.tableView.deleteRows(at: [indexPath], with: .automatic)
}
success(error == nil)
}
To delete row using swipe, you can use
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
items.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
}
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
And because you not reload table view, that will look much better
Related
please help, I have an array with names and when I select a cell I add the selected name to the second section of a table view and delete the name from that first section (all fine here) but if I don't want the name in second section for some reason, I want to be able to swipe the cell, remove the name and add it again to the firs section of the table view. When I do that with my code the number of rows fails because I deleted a row. I can't figure it out.
here is my code.
import UIKit
class QuestionsVC: UIViewController {
#IBOutlet weak var namesTableView: UITableView!
var array1 = ["Jill","Clark","Rose","Peter","Louis"]
var array2 = [String]()
override func viewDidLoad() {
super.viewDidLoad()
namesTableView.dataSource = self
namesTableView.delegate = self
}
}
extension QuestionsVC: UITableViewDataSource, UITableViewDelegate {
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return array1.count
}
return array2.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")!
cell.textLabel?.text = array1[indexPath.row]
return cell
}
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
cell?.textLabel?.text = array2[indexPath.row]
return cell!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
array2.append(array1[indexPath.row])
if let index = array1.firstIndex(of: array1[indexPath.row]) {
array1.remove(at: index)
}
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
if indexPath.section == 0 {
return false
}else{
return true
}
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let deleteAction = UITableViewRowAction(style: .destructive, title: "Delete") { (_, indexPath) in
self.array2.remove(at: indexPath.row)
self.array1 += [self.array2[indexPath.row]]
tableView.deleteRows(at: [indexPath], with: .fade)
}
return [deleteAction]
}
}
You have to delete the row in section 1 and insert the row in section 0 simultaneously
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let deleteAction = UITableViewRowAction(style: .destructive, title: "Delete") { (_, indexPath) in
let item = self.array2.remove(at: indexPath.row)
let insertionIndex = self.array1.count
self.array1.append(item)
tableView.beginUpdates()
tableView.deleteRows(at: [indexPath], with: .fade)
tableView.insertRows(at: [IndexPath(row: insertionIndex, section: 0)], with: .automatic)
tableView.endUpdates()
}
return [deleteAction]
}
}
replace the line
tableView.deleteRows(at: [indexPath], with: .fade)
with
tableView.reloadData()
This is my sectionArray
var sectionArray = [Sections]()
This is how I figure out how many rows in section
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sectionArray[section].items.count
}
and this is my code to remove a row by sliding.
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == UITableViewCell.EditingStyle.delete {
sectionArray[indexPath.section].remove(at: indexPath.row)
if sectionArray[indexPath.section].title.count == 0 {
sectionArray.remove(at: indexPath.row)
}
tableView.deleteRows(at: [indexPath], with: UITableView.RowAnimation.automatic)
}
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let delete = UITableViewRowAction(style: .normal, title: "Delete") { action, index in
//self.isEditing = false
print("delete button tapped")
}
delete.backgroundColor = UIColor.red
return [delete]
}
Theoretically this delete stuff should work but I am getting error No exact matches in call to subscript at
sectionArray[indexPath.section].remove(at: indexPath.row)
/// This line
sectionArray.remove(at: indexPath.row)
/// should be like this
sectionArray.remove(at: indexPath.section)
/// This line will remove the section from the table
tableView.deleteSections(.init(integer: indexPath.section), with: .automatic)
You are using row to remove a section
Edit
Replace all the code you provided us with this
/*
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == UITableViewCell.EditingStyle.delete {
sectionArray[indexPath.section].remove(at: indexPath.row)
if sectionArray[indexPath.section].title.count == 0 {
sectionArray.remove(at: indexPath.row)
}
tableView.deleteRows(at: [indexPath], with: UITableView.RowAnimation.automatic)
}
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let delete = UITableViewRowAction(style: .normal, title: "Delete") { action, index in
//self.isEditing = false
print("delete button tapped")
}
delete.backgroundColor = UIColor.red
return [delete]
}
*/
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let delete = UITableViewRowAction(style: .normal, title: "Delete") { action, index in
//self.isEditing = false
sectionArray[indexPath.section].remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: UITableView.RowAnimation.automatic)
if sectionArray[indexPath.section].items.isEmpty {
sectionArray.remove(at: indexPath.section)
tableView.deleteSections(.init(integer: indexPath.section), with: .automatic)
}
}
delete.backgroundColor = UIColor.red
return [delete]
}
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()
}
}
In my table view, after I delete a record, I want it to disable editing if the last record was deleted, but even after calling setEditing the state doesn't change. Here's a simplified example that still shows the behavior:
class TestViewController: UITableViewController {
var items = [String]()
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.rightBarButtonItems = [
UIBarButtonItem(title: "Add", style: .plain, target: self, action: #selector(add)),
UIBarButtonItem(title: "Remove", style: .plain, target: self, action: #selector(remove))
]
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = self.items[indexPath.row]
return cell
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
self.items.remove(at: indexPath.row)
if self.items.count == 0 {
// This is getting executed, but not changing the table's state
self.tableView.setEditing(false, animated: false)
}
self.tableView.reloadData()
}
#objc func add () {
self.items.append("foo")
self.tableView.reloadData()
}
#objc func remove () {
self.tableView.setEditing(!self.tableView.isEditing, animated: true)
}
}
Clicking the "Remove" button toggles editing on and off without any problems, but if I delete the last one and then add a new one, it still shows up in editing mode. I tried stepping through the code, and it's definitely hitting the correct line, but even after executing it tableView.isEditing is still true. What am I doing wrong here?
I have checked your code, as per Hikaru Watanabe's comment , it is not working.
So i have made change in commit editingstyle method and it is working fine,
please check below code
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
self.items.remove(at: indexPath.row)
self.tableView.deleteRows(at: [indexPath], with: .left)
if self.items.count == 0 {
// This is getting executed, but not changing the table's state
self.tableView.setEditing(false, animated: false)
}
//self.tableView.reloadData()
}
Updated After John's Comment -
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
self.items.remove(at: indexPath.row)
if self.items.count == 0 {
// This is getting executed, but not changing the table's state
self.tableView.deleteRows(at: [indexPath], with: .none)
self.tableView.setEditing(false, animated: false)
}else{
self.tableView.reloadData()
}
}
You should use this method:
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return editingEnable
}
Just set true or false this variable "editingEnable" on your condition.
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()