Почему в моем табличном представлении отображаются повторяющиеся ячейки после удаления дочернего элемента?
У меня есть viewcontroller с табличным представлением, и когда пользователь нажимает на ячейку, он переходит к VC2. Когда пользователь выполнил действие (и обновил значения в VC2), я использую self.dismiss(animated: true, completion: nil)
чтобы вернуться к контроллеру представления с табличным представлением, однако табличное представление (после того, как пользователь вернулся к табличному представлению) показывает дублированные строки, но дочерний элемент успешно удален в firebase, и создается новый дочерний элемент - однако табличное представление показывает дети, которые не удаляются дважды.
Это весь соответствующий код в VC1:
class PostMessageListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
@IBOutlet weak var table: UITableView!
var topicID:namePosts?
let currentUserID = Auth.auth().currentUser?.uid
var posts = [Post]()
lazy var refresher: UIRefreshControl = {
let refreshControl = UIRefreshControl()
refreshControl.tintColor = .white
refreshControl.addTarget(self, action: #selector(requestData), for: .valueChanged)
return refreshControl
}()
@objc
func requestData() {
self.table.reloadData()
refresher.endRefreshing()
}
func reloadData(){
table.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
self.table.separatorStyle = UITableViewCellSeparatorStyle.none
table.refreshControl = refresher
//DataManager.shared.firstVC = self
self.table.delegate = self
self.table.dataSource = self
let postCell = UINib(nibName: "PostTableViewCell", bundle: nil)
self.table.register(postCell, forCellReuseIdentifier: "cell")
self.posts.removeAll()
Database.database().reference().child("posts").child(postID!.name)
.observe(.childAdded) { (snap) in
if snap.exists() {
//declare some values here...
self.posts.append( //some values here)
self.posts.sort(by: {$0.createdAt > $1.createdAt})
self.table.reloadData()
})
}
else {
self.table.reloadData()
}
}
//observe if a post is deleted by user
Database.database().reference().child("posts").child("posts").observe(.childRemoved) { (snapshot) in
let postToDelete = self.indexOfPosts(snapshot: snapshot)
self.posts.remove(at: postToDelete)
self.table.reloadData()
//self.table.deleteRows(at: [NSIndexPath(row: questionToDelete, section: 1) as IndexPath], with: UITableViewRowAnimation.automatic)
//self.posts.remove(at: indexPath.row)
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.posts.count
}
func indexOfPosts(snapshot: DataSnapshot) -> Int {
var index = 0
for post in self.posts {
if (snapshot.key == post.postID) {
return index
}
index += 1
}
return -1
}
РЕДАКТИРОВАТЬ: Забыл сказать, но я использовал этот код в другом Viewcontroller, и там он отлично работает. Однако я просто скопировал свой код из этого в этот и удалил кучу вещей, которые мне не нужны, однако я не могу найти то, что мне не хватает в этом.
1 ответ
Это может быть не ответ, но может привести к ответу. Как отмечено в комментариях, для управления источником данных для tableView используются два массива. Один содержит данные, а другой использует технику индексирования - я считаю, что это может привести к проблемам, подобным описанному в вопросе.
Другая проблема заключается в том, что когда каждый дочерний элемент изначально добавляется, мы повторно сортируем массив и затем обновляем tableView - это может привести к задержкам и мерцанию. (мерцание = плохо)
Итак, давайте установим пару вещей. Сначала класс, который держит должности
PostClass {
var post_id = ""
var post_text = ""
var creation_date = ""
}
вторая структура Firebase, которая похожа
posts
post_id_0
text: "the first post"
timestamp: "20190220"
post_id_1
text: "the second post"
timestamp: "20190221"
затем небольшой трюк, чтобы заполнить источник данных и оставить ребенка добавленным наблюдателем. Это важно, так как вы не хотите обновлять tableView с каждым дочерним элементом, который может (будет) мерцать. Поэтому мы используем, чтобы события childAdded всегда предшествовали событиям.value, чтобы массив заполнялся, а затем.value обновит его один раз, а затем мы будем обновлять tableView каждый раз после. Вот некоторый код - много чего происходит, поэтому перешагните через него.
var postsArray = [String]()
var initialLoad = true
func ReadPosts() {
let postsRef = self.ref.child("posts").queryOrdered(byChild: "timestamp")
postsRef.observe(.childAdded, with: { snapshot in
let aPost = PostClass()
aPost.post_id = snapshot.key
aPost.post_text = snapshot.childSnapshot("text").value as! String
aPost.creation_date = snapshot.childSnapshot("timestamp").value as! String
self.postsArray.append(aPost)
//upon first load, don't reload the tableView until all children are loaded
if ( self.initialLoad == false ) {
self.postsTableView.reloadData()
}
})
//when a child is removed, the event will contain that child snapshot
// we locate the child node via it's key within the array and remove it
// then reload the tableView
postsRef.observe(.childRemoved, with: { snapshot in
let keyToRemove = snapshot.key
let i = self.postsArray.index(where: { $0.post_id == keyToRemove})
self.postsArray.remove(at: r)
self.postsTableView.reloadData()
})
//this event will fire *after* all of the child nodes were loaded
// in the .childAdded observer. So children are sorted, added and then
// the tableView is refreshed. Set initialLoad to false so the next childAdded
// after the initial load will refresh accordingly.
postsRef.observeSingleEvent(of: .value, with: { snapshot in
self.postsTableView.reloadData()
self.initialLoad = false
})
}
Что нужно отметить
Мы позволяем Firebase выполнять тяжелую работу и упорядочивать узлы по creation_date, чтобы они приходили в порядок.
Это будет вызвано, скажем, viewDidLoad, где мы установим initialLoad
класс var изначально true