Данные Firebase мерцают при добавлении новых значений - PullRequest
0 голосов
/ 07 ноября 2018

Я создаю социальное приложение, в которое извлекаю некоторые данные и сбрасываю их в представление коллекции. Я сбрасываю все сообщения из базы данных в массив сообщений. Я также получаю информацию о пользователях, которые разместили конкретное изображение. Обе базы данных представляют 2 разные модели. Ниже приводится моя модель данных:

posts
  |- <post_id>
         |- caption
         |- ImageURL
         |- views
         |- spot
             |- spot_id
                  |- sender<user_id>
                  |- spotted(value)
                  |- timestamp
         |- author(<user_id>)

users
  |- <user_id>
         |- name

Ниже приведен способ извлечения данных записей в collectionVC и сохранения всех в массиве записей:

func initialiseAllPostsContent(){
    FBDataservice.ds.REF_CURR_USER.child("connections/following").observe(.childAdded) { (snapshot) in
        if let snapshot = snapshot.value as? String {
            self.followerKeys.append(snapshot)
        }
    }
    if uid != nil {
        self.followerKeys.append(uid!)
    }
    FBDataservice.ds.REF_POSTS.queryOrdered(byChild: "timestamp").observe(.childAdded, with: { (snapshot) in
        print("post key is ", snapshot.key)
        if let postDict = snapshot.value as? Dictionary<String, Any> {
            let key = snapshot.key
            if let postAuthor = postDict["author"] as? String {
                for user in self.followerKeys {
                    if postAuthor == user {
                        let post = Posts(postId: key, postData: postDict)
                        self.posts.append(post)
                    }
                }
            }
        }
    })
    reloadCollectionViewData()
}

func reloadCollectionViewData() {
    FBDataservice.ds.REF_POSTS.queryOrdered(byChild: "timestamp").observe(.value) { (snapshot) in
        self.collectionView.reloadData()
    }
}

//I am updating the views on the post after a method is successfull. As soon as this is called, and then if like is pressed, views flicker
func updateViews(postid: String, views: Int) {
    let viewref = FBDataservice.ds.REF_POSTS.child(postid)
    let newviews = views + 1
    viewref.updateChildValues(["views":newviews])
}

// fetching the user data from the post data

func getAllPosts(pid: String, completion: @escaping ((String) -> ())) {
    FBDataservice.ds.REF_POSTS.child(pid).observeSingleEvent(of: .value) { (snapshot) in
        if let snapshot = snapshot.value as? Dictionary<String, Any> {
            if let userid = snapshot["author"] as? String {
                completion(userid)
            }
        }
    }
}

func getpostAuthorData(authorId : String, completion: @escaping (User) -> ()) {
    FBDataservice.ds.REF_USERS.child(authorId).observeSingleEvent(of: .value) { (snapshot) in
        if let snapshot = snapshot.value as? Dictionary<String, Any> {
            if let userCredential = snapshot["credentials"] as? Dictionary<String, Any> {
                completion(User(userid: authorId, userData: userCredential))
            }
        }
    }
}

Так я назначаю данные в моем cellForItemAtIndexPath

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    self.posts.sort(by: { $0.timestamp < $1.timestamp})
    let post = posts[indexPath.row]
    if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as? SpotGroundCell {
        cell.configureCellData(post: post)
        getAllPosts(pid: post.postId) { (userid) in
            self.getpostAuthorData(authorId: userid, completion: { (userdata) in
                cell.configUserData(user: userdata)
            })
        }
    return cell
    } else {
        return SpotGroundCell()
    }

}

Код в моей ячейке:

//Consider this as likes. I allow users to like multiple times. Once the model is loaded, it fetches all the spots according to the timestamp and then siplayer the most recent ones. Even this is doesn't display according to the current image and flickers. I replicate previous cell values even though I am refreshing the view.
var currentUserSpots = [Spot]() {
    didSet {
        self.currentUserSpots.sort(by: { $0.timestamp < $1.timestamp})
        if !self.currentUserSpots.isEmpty {
            self.emotionImage.image = UIImage(named: (self.currentUserSpots.first?.spotted)!)
            self.emotionImage.alpha = 1
        } else {
            self.emotionImage.image = UIImage(named: "none")
            self.emotionImage.alpha = 0.5
        }
    }
}

func configUserData(user: User) {
    self.user = user
    self.name.text = self.user.name
}

func configureCellData(post: Posts) {
    print("Config is now called")
    self.posts = post
    self.caption.text = posts.caption

    FBDataservice.ds.REF_POSTS.child(post.postId).child("spot").queryOrdered(byChild: "senderID").queryEqual(toValue: uid!).observeSingleEvent(of: .childAdded) { (snapshot) in
        if let spotData = snapshot.value as? Dictionary<String, Any> {
            let spot = Spot(id: snapshot.key, spotData: spotData)
            if spot.spotted != nil {
                self.currentUserSpots.append(spot)
            }
        }
    }
}

Теперь, когда я делаю изменение или событие, которое обновляет базу данных (например, обновление представления). Я вижу мерцание в объектах объекта пользователя (таких как имя и т. Д.). Это событие также убивает другие процессы и наблюдателей уведомлений.

Я нашел в интернете решения, но пока просто смог найти один , который не решает мою проблему.

Любая помощь будет принята с благодарностью. Я действительно не уверен, где я иду не так.

1 Ответ

0 голосов
/ 07 ноября 2018

Всякий раз, когда есть изменение под REF_POSTS, вы прямо сейчас:

  1. удалить все данные из представления
  2. повторно добавить все данные (включая изменения) в представление

Учитывая, что большинство изменений затронут только один элемент в списке, вы делаете N-1 больше, чем нужно. Это вызывает мерцание.

Чтобы решить эту проблему, вы должны прослушать более детальную информацию из базы данных. Вместо наблюдения .value, добавьте слушателя для .childAdded. Блок завершения для этого слушателя будет срабатывать всякий раз, когда добавляется новый дочерний элемент, и в этот момент вы можете просто добавить нового дочернего элемента в свое представление.

FBDataservice.ds.REF_POSTS.queryOrdered(byChild: "timestamp").observe(.childAdded, with: { (snap) in
    if let postDict = snap.value as? Dictionary<String, Any> {
        let key = snap.key
        if let postAuthor = postDict["author"] as? String {
            for user in self.followerKeys {
                if postAuthor == user {
                    let post = Posts(postId: key, postData: postDict)
                    self.posts.append(post)
                }
            }
        }
    }
})

В качестве бонуса .childAdded также немедленно срабатывает для всех существующих дочерних узлов, поэтому вам больше не нужен наблюдатель для .value. Мне нравится держать это сам, хотя. Поскольку Firebase гарантирует, что он запускает .value после всех соответствующих событий child*, событие .value является отличным моментом, чтобы сказать, что все изменения произошли.

FBDataservice.ds.REF_POSTS.queryOrdered(byChild: "timestamp").observe(.value, with: { (snapshot) in
    self.collectionView.reloadData()
})

Вам понадобится еще несколько вещей для полной реализации:

  1. Вы также должны соблюдать .childChanged, .childMoved и childRemoved для обработки этих типов изменений в базе данных.
  2. Поскольку дочерний элемент может быть добавлен (или перемещен) в любом месте списка, вы должны использовать observe(_, andPreviousSiblingKey: ), чтобы иметь возможность поместить элемент в нужное место в списке.
...