簡體   English   中英

新的 Firebase 數據導致 TableView 單元格閃爍 (Firebase/iOS/Swift)

[英]New Firebase Data Causes Flicker of TableView Cells (Firebase/iOS/Swift)

我的主要問題是如何擺脫閃爍,但我也只想知道我是否正確且最有效地處理非規范化 Firebase 數據。 我的方法是否接近正確?

因此,我正在努力嘗試使用非規范化的數據正確顯示來自 Firebase 數據庫的數據。 我有帖子,然后是與每個帖子相關的評論。 每次有人打開帖子的評論部分時,通過從視圖控制器轉移到新的視圖控制器,它會獲取帖子的唯一鍵 (postKey),然后掃描與包含在 postCommentGroup 中的 postKey 關聯的評論組。 評論組,即 postCommentGroup 中每個 postKey 的子項,只是將 commentKey 作為鍵,“true”作為值,表示哪些評論與哪些帖子相關聯。 評論在一個完全不同的分支中,因為我認為 Firebase 文檔建議人們應該這樣做。

我基本上有 3 層嵌套觀察者。

為了清楚起見,我在 tableview 中使用 dequeuereusablecells 回收單元格,並且我還有一個基本的延遲加載/圖像緩存機制,它也可能會干擾事情,但我在其他不太復雜的 tableviews 上有相同的機制,所以我不要認為這是問題所在。

由於我缺乏知識,除了經歷這個循環之外,我不知道如何顯示數據。 我認為這個循環,可能會導致閃爍,但我不知道如何讓它加載數據。 我嘗試了其他各種方法,例如使用查詢,但我一直無法讓它工作。

作為旁注,我試圖加快了解如何查詢數據(我認為這可能對我有幫助),但是 Swift 的語法已經更新,Firebase 也有更新,使前面的例子有點難以遵循。

另外,我在 Firebase 站點或 Github 上的任何 Firebase 文檔中都找不到以某種復雜的方式正確使用非規范化數據的好的最近示例。 有沒有人知道關於使用 Swift 3.0 和 Firebase(最新版本 - 不是舊版本)處理非規范化數據的良好參考資料,無論是 GitHub 上的項目,還是博客,或者只是最stackoverflow 上有用的帖子?

這是 Firebase 數據結構:

 "comments" : { "-KaEl8IRyIxRbYlGqyXC" : { "description" : "1", "likes" : 1, "postID" : "-KaEfosaXYQzvPX5WggB", "profileImageUrl" : "https://firebasestorage.googleapis.com", "timePosted" : 1484175742269, "userID" : "9yhij9cBhJTmRTexsRfKRrnmDRQ2", "username" : "HouseOfPaine" } }, "postCommentGroup" : { "-KaEfosaXYQzvPX5WggB" : { "-KaEl8IRyIxRbYlGqyXC" : true, "-KaEl9HiPCmInE0aJH_f" : true, "-KaF817rRpAd2zSCeQ-M" : true }, "-KaF9ZxAekTEBtFgdB_5" : { "-KaFEcXsSJyJwvlW1w2u" : true }, "-KaJyENJFkYxCffctymL" : { "-KaQYa0d08D7ZBirz5B4" : true } }, "posts" : { "-KaEfosaXYQzvPX5WggB" : { "caption" : "Test", "comments" : 11, "imageUrl" : "https://firebasestorage.googleapis.com/", "likes" : 0, "profileImageUrl" : "https://firebasestorage.googleapis.com/", "timePosted" : 1484174347995, "title" : "test", "user" : "17lIDKNx6LgzQmaeQ2ING582zi43", "username" : "Freedom" } },

這是我的代碼:

func commentGroupObserver() {

    DataService.ds.REF_POST_COMMENT_GROUP.observeSingleEvent(of: .value, with: { (snapshot) in

        if snapshot.value != nil {

            if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] , snapshots.count > 0 {

                self.comments = []

                for snap in snapshots {

                    if let tempVarPostKeyForCommentGroup = snap.key as String? {

                        if tempVarPostKeyForCommentGroup == self.post.postKey {

                            self.postKeyForCommentGroup = tempVarPostKeyForCommentGroup

                            self.commentObservers()
                        } else {

                        }
                    } else {

                    }
                }

            }

        } else {
            print("error")
        }

    })


}


func commentObservers() {

    if postKeyForCommentGroup != nil {

        constantHandle = DataService.ds.REF_POST_COMMENT_GROUP.child(postKeyForCommentGroup).observe(.value, with: { (snapshot) in

            if snapshot.value != nil {

                if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot], snapshots.count > 0

                {

                    self.comments = []

                    for snap in snapshots {

                        if let theCommentIDForEachComment = snap.key as String? {
                             DataService.ds.REF_COMMENTS.child(theCommentIDForEachComment).queryOrdered(byChild: "timePosted").observeSingleEvent(of: .value, with: { (snapshots) in

                                if let commentDict = snapshots.value as? Dictionary<String, AnyObject> {

                                    let key = snapshots.key
                                    let comment = Comment(commentKey: key, dictionary: commentDict)
                                    self.comments.insert(comment, at: 0)     
                                }
                                self.tableView.reloadData()
                            })
                        }
                    } 
                }

            } else {

            }
        })

    } else {

    }

}

更新:

我想出了如何使用之前的 stackoverflow 帖子中概述的查詢和委托模式:

從從 firebase 檢索數據的閉包中獲取數據

但我不知道我是否正確使用了委托模式。

代碼已經通過使用查詢進行了簡化,但它仍然閃爍。 也許我沒有正確使用委托模式?

    func commentGroupObserver() {
    DataService.ds.REF_POST_COMMENT_GROUP.queryOrderedByKey().queryStarting(atValue: post.postKey).queryEnding(atValue: post.postKey).observeSingleEvent(of: .value, with: { (snapshot) in
        self.postKeyForCommentGroup = self.post.postKey
        self.commentObservers()
    })

}

func commentObservers() {
    if postKeyForCommentGroup != nil {
        constantHandle = DataService.ds.REF_POST_COMMENT_GROUP.child(postKeyForCommentGroup).observe(.value, with: { (snapshot) in
            if snapshot.value != nil {
                if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot]
                {
                    self.comments = []
                    for snap in snapshots {
                        if let theCommentIDForEachComment = snap.key as String? {
                            DataService.ds.REF_COMMENTS.child(theCommentIDForEachComment).queryOrdered(byChild: "timePosted").observe(.value, with: { (snapshots) in

                                if let commentDict = snapshots.value as? Dictionary<String, AnyObject> {

                                    let key = snapshots.key
                                    let comment = Comment(commentKey: key, dictionary: commentDict)
                                    self.comments.insert(comment, at: 0)

                                }

                                self.didFetchData(comments: self.comments)

                            })



                        }

                    }

                }

            } else {

            }
        })

    } else {

    }

}

func didFetchData(comments data:[Comment]){
    self.tableView.reloadData()
}

}

和協議

 protocol MyDelegate{
func didFetchData(comments:[Comment]) }

我這邊的代碼解決了這個問題:

根據 Jay 的建議,我刪除了不必要的 postCommentGroup,並在評論下查詢了該評論所屬的帖子的 UID:

    func commentObservers() {

    let queryRef = DataService.ds.REF_COMMENTS.queryOrdered(byChild: "postID").queryEqual(toValue: self.post.postKey)

    queryRef.observe(.value, with: { snapshot in

        if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {

            for snap in snapshots {

                if let commentDict = snap.value as? Dictionary<String, AnyObject> {
                    let key = snap.key
                    let comment = Comment(commentKey: key, dictionary: commentDict)
                    self.comments.insert(comment, at: 0)
                }
            }
        }

        self.tableView.reloadData()
    })
}

您的方法可能需要通過簡化來調整。 我想提供所有的螺母和螺栓,所以它有點冗長,而且本身可以簡化。

雖然非規范化是正常的,但它不是必需的,並且在某些情況下會增加額外的復雜性。 您的結構 postCommentGroup 中的“層”似乎是不需要的。

看起來您有一個包含帖子的視圖控制器,以及當用戶點擊第一個控制器上的帖子時顯示評論的第二個視圖控制器。

你真的只需要一個帖子節點和一個評論節點

posts
   post_id_0
     title: "my post title"
     caption: "some caption"
     uid: "uid_0"
   post_id_1
     title: "another post title
     caption: "another caption
     uid: "uid_0"

和引用帖子的評論節點

comments
   comment_0
     post_id: "post_id_0"
     uid: "uid_1"
     likes: "10"
   comment_1
     post_id: "post_id_0"
     uid: "uid_1"
     likes: "7"
   comment_2
     post_id: "post_id_1"
     uid: "uid_1"
     likes: "2"

設置:

class CommentClass {
    var commentKey = ""
    var comment = ""
    var likes = ""
}

var postsArray = [ [String: [String:AnyObject] ] ]()
var commentsArray = [CommentClass]()

加載所有帖子的代碼:

    let postsRef = ref.child("posts")

    postsRef.observeSingleEvent(of: .value, with: { snapshot in

        for snap in snapshot.children {
            let postSnap = snap as! FIRDataSnapshot
            let postKey = postSnap.key //the key of each post
            let postDict = postSnap.value as! [String:AnyObject] //post child data

            let d = [postKey: postDict]
            self.postsArray.append(d)
        }
        //postsTableView.reloadData
        print(self.postsArray) //just to show they are loaded
    })

然后,當用戶點擊帖子時,加載並顯示評論。

    self.commentsArray = [] //start with a fresh array since we tapped a post
    //placeholder, this will be the post id of the tapped post
    let postKey = "post_id_0" 
    let commentsRef = ref.child("comments")
    let queryRef = commentsRef.queryOrdered(byChild: "post_id")
                              .queryEqual(toValue: postKey)

    //get all of the comments tied to this post
    queryRef.observeSingleEvent(of: .value, with: { snapshot in

        for snap in snapshot.children {
            let commentSnap = snap as! FIRDataSnapshot
            let commentKey = commentSnap.key //the key of each comment
            //the child data in each comment
            let commentDict = commentSnap.value as! [String:AnyObject] 
            let comment = commentDict["comment"] as! String
            let likes = commentDict["likes"] as! String
            let c = CommentClass()
            c.commentKey = commentKey
            c.comment = comment
            c.likes = likes

            self.commentsArray.append(c)
        }

        //commentsTableView.reload data

        //just some code to show the posts are loaded
        print("post:  \(postKey)")
        for aComment in self.commentsArray {
            let comment = aComment.comment
            print("  comment: \(comment)")
        }
    })

和結果輸出

post:  post_id_0
  comment: I like post_id_0
  comment: post_id_0 is the best evah

上面的代碼經過測試,沒有閃爍。 顯然,它需要針對您的用例進行調整,因為您有一些圖像要加載等,但上述內容應該可以解決問題並且更易於維護。

我只在圖片中經歷過這種閃爍。 每次在集合視圖中發生某些事情時,該應用程序都會下載圖片。

我在網上找到的解決方案。 NSCache。 您可以緩存圖片,如果圖片鏈接沒有改變,圖片將從緩存中加載。 當您滾動 Instagram 或 Facebook 等收藏視圖時也很有幫助。 我在 Swift 5 中沒有找到太多解決方案。所以讓我與您分享。

TODO:創建一個新的 swift 文件。 復制此代碼,它會創建一個自定義 ImageView 類。 在情節提要上為這個自定義類設置圖像視圖,ctrl+drag。 或者在你的 swift 文件中以編程方式執行相同的操作。

let imageCache = NSCache<NSString, UIImage>()
class CustomImageView: UIImageView {
var imageUrlString: String?

func loadImageUsingUrlString(urlString: String) {
    
    imageUrlString = urlString
    
    guard let url = URL(string: urlString) else { return }
    
    image = nil
    
    if let imageFromCache = imageCache.object(forKey: urlString as NSString) {
        self.image = imageFromCache
        print("local")
        return
    }
    
    URLSession.shared.dataTask(with: url, completionHandler: { (data, respones, error) in
        
        if error != nil {
            print(error ?? "")
            return
        }
        
        DispatchQueue.main.async {
            guard let imageToCache = UIImage(data: data!) else { return }
            
            if self.imageUrlString == urlString {
                self.image = imageToCache
                print("most mentem a kepet")
            }
            
            imageCache.setObject(imageToCache, forKey: urlString as NSString)
        }
        
    }).resume()
}
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM