当 Firebase 中的数据发生变化时,Swift CollectionView.reload 数据不起作用?

Posted

技术标签:

【中文标题】当 Firebase 中的数据发生变化时,Swift CollectionView.reload 数据不起作用?【英文标题】:Swift CollectionView.reload data not working when data changes in Firebase? 【发布时间】:2018-06-19 07:55:50 【问题描述】:

我目前有这个工作代码,它从 Firebase 中的 users 位置读取,其中包含每个用户的所有用户个人资料信息。我正在使用从 Firebase 返回的快照(数据)来构建我的数组并在 CollectionView 中显示用户。

我预期的行为(没有发生)是,当 Firebase 中 user 属性的值发生更改时(例如,online 状态更改为“离线”),这应该被推送到我的应用程序这样CollectionView 就会更新,因此不会显示刚刚“离线”的用户。

我在下面使用了正确的 Firebase 查询吗?

self.REF_USERS.observeSingleEvent(of: .value , with:  (snapshot) in

                    guard let usersSnapshot = snapshot.children.allObjects as? [DataSnapshot] else  return 

                    for user in usersSnapshot

                        let discoverable = user.childSnapshot(forPath: "discoverable").value as! Bool

                        if user.key == key && discoverable == true 

                            let uid = user.key
                            let name = user.childSnapshot(forPath: "name").value as! String
                            let email = user.childSnapshot(forPath: "email").value as! String
                            let profilePictureURL = user.childSnapshot(forPath: "profilePictureURL").value as! String

                            let birthday = user.childSnapshot(forPath: "birthday").value as! String
                            let firstName = user.childSnapshot(forPath: "firstName").value as! String
                            let lastName = user.childSnapshot(forPath: "lastName").value as! String
                            let gender = user.childSnapshot(forPath: "gender").value as! String
                            let discoverable = user.childSnapshot(forPath: "discoverable").value as! Bool
                            let online = user.childSnapshot(forPath: "online").value as! Bool

                            let discoveryPrefs = user.childSnapshot(forPath: "discoveryPrefs").value as! [String : Any]

                            let dictionary: [String : Any] = ["uid": uid, "name": name, "email": email, "profilePictureURL": profilePictureURL, "birthday": birthday, "firstName": firstName, "lastName": lastName, "gender": gender, "discoverable": discoverable, "online": online, "discoveryPrefs": discoveryPrefs]

                            let user = User(uid: uid, dictionary: dictionary)

                            users.append(user)

                        //end if

                    //end for

                    //filter out "remove" current user from array
                    let filteredUsers = users.filter( (user: User) -> Bool in return !user.uid.contains(uid) )

                    handler(filteredUsers.shuffled(), true)//return a shuffled version of the array via handler

                )//end FIR snapshot call

这就是我使用上面的代码加载/重新加载数据的方式:

DataService.run.getUsersAtVenue(forVenueLocation: location, forUid: userId)  (users, successBool) in

            if successBool 
                self.users = users
                self.collectionView.reloadData()
                Utilities.run.dismissSVHUD(delay: 0.5)

            
        

【问题讨论】:

当用户离线时你从online 得到什么? True/False? 我目前正在将discoverable 的值更改为truefalse,此字段指定是否应在collectionview 上显示用户。 online 也一样,truefalse 【参考方案1】:

你在主线程中,users 改变了吗?试着把它放进去:

         DispatchQueue.main.async 
                if users.count > 0 
                      self.users = users
                      self.collectionView.reloadData()
                      Utilities.run.dismissSVHUD(delay: 0.5)
                  
        

【讨论】:

这会加载 collectioview,但是当 Firebase 中的数据发生变化时它不会更新。 评论您的加载方法并分配users 值以测试您的收藏视图是否正常工作。如果没有问题,请取消注释 load 方法,然后检查可能多次加载的潜在原因,并且您的 users variable.count 不是 0,但对于下一轮调用,它被设置为 nil 或其他。你能检查一下你的加载用户方法被调用了多少次吗?【参考方案2】:

Roggie,您需要注册一个观察者,以便在特定“用户”节点(“用户”位置下的子节点)发生更改时接收通知。此代码应如下所示

_refHandleUserChanged = ref.child("users").observe(.childChanged, with:  (snapshot) in
    guard let changedUserIndex = self.userSnapshots.index $0.key == snapshot.key else  return 
    self.userSnapshots[changedUserIndex] = snapshot
    self.userCollectionView.reloadRows(at: [IndexPath(row: changedUserIndex, section: 0)], with: .automatic)
)

其中“ref”是对您的 Firebase 数据库的引用,“_refHandleUserChanged”是您当前类(可能是您的 View Controller 类)的实例 var 属性。然后,当您的实例被取消初始化时,您应该取消注册/删除此观察者,如下面的代码。

deinit 
    ref.child("users").removeObserver(withHandle: _refHandleUserChanged)

【讨论】:

您对注册“观察员”的反馈很有道理。我只是想了解我的代码所需的更改。我需要将“观察者”部分添加到我的cellForItemAt 吗?如果没有,那么在哪里?另外,var _refHandleUserChanged 会是我当前的 ViewController 的一个实例,它被称为DiscoverVC?。你能给我指点一些在线教程吗? 您的 DiscoverVC 是否符合 UICollectionViewDataSource?如果是这样,您应该在 DiscoverVC 类中创建一个单独的方法,例如称为 configureDatabase,其中包含“ref = Database.database().reference()”和上面的“观察者注册”代码。然后在 viewDidLoad 方法中调用此方法。当然,您需要将“ref”和“_refHandleUserChanged”声明为 DiscoverVC 类的 var 属性,如下所示:“private var ref: DatabaseReference!” & "私有变量 _refHandleUserChanged: DatabaseHandle!" DiscoverVC 确实符合UICollectionViewDataSource,我已经创建了建议的函数,但是我收到了一个错误Use of unresolved identifier '_refHandleUserChanged'。这个变量应该是什么?还有Anonymous closure arguments cannot be used inside a closure that has explicit arguments; did you mean 'snapshot'?Replace '$0' with 'snapshot' '_refHandleUserChanged' 应在 DiscoverVC 类的顶部声明,如“private var ref: DatabaseReference!”。请注意,我假设您的 var 属性“userSnapshots”加载了 Firebase 数据库中的用户记录。然后将此属性用作数据源来填充您的 CollectionView。您可以查看这个简短而有用的课程udacity.com/course/firebase-in-a-weekend-by-google-ios--ud0351【参考方案3】:

我对小数据集所做的是在你拥有所有新数据后完成completionBlock。

topViewController 是 CollectionView 如果将其转换为自定义的 collectionView 然后调用collectionview.reloadData。

所有这些都嵌入到主队列中。

您很可能在取回数据之前重新加载

【讨论】:

以上是关于当 Firebase 中的数据发生变化时,Swift CollectionView.reload 数据不起作用?的主要内容,如果未能解决你的问题,请参考以下文章

当实际数据发生变化时,结果缓存数据会发生啥变化?

当 Kotlin 中的数据发生变化时,如何在 RecyclerView 中重新绑定项目?

每当firebase发生任何变化时,自动向所有用户发送通知[关闭]

当 Firebase 数据库中的值更改时创建推送通知

Swiftui Firebase RTDB 不会在视图中更新

当SwiftUI中的相关实体发生变化时,如何更新@FetchRequest?