如何以正确的方式从 firebase 数据库加载图像
Posted
技术标签:
【中文标题】如何以正确的方式从 firebase 数据库加载图像【英文标题】:How do I load image from firebase database the correct way 【发布时间】:2019-12-28 08:37:25 【问题描述】:我有一个 -post 和 -user 的数据库结构,在 post 下我有发布信息,即发布图像、文本数据和发布它的用户的 uid。在用户节点中,我有用户信息,其中包括用户的个人资料图像。
现在我正在尝试检索帖子以及用户的个人资料图片。我能够检索帖子但无法正确加载个人资料图片,我尝试了很多不同的方法,代码中的一个有 2 个索引,当计数相等时重新加载 tableview,这会产生冲突并加载图像以随机方式,即使计数相同。任何想法我都可以加载图像,以便它们对应于正确的帖子。我是火力基地的新手。
func postRetrieve()
SVProgressHUD.show()
let ref = Database.database().reference()
ref.child("posts").queryOrdered(byChild: "timesStamp").observeSingleEvent(of: .value) (snapshot) in
let post = snapshot.value as! [String : AnyObject]
self.userPost.removeAll()
for (_ , value ) in post
if let usersId = value["userID"] as? String
self.usersKey.append(usersId)
print("here toooo")
let finalPostToShow = Post()
if let topicData = value["topic"] as? String,
let userName = value["creator"] as? String,
let timeStamp = value["timesStamp"] as? Double
finalPostToShow.topicData = topicData
finalPostToShow.usrId = usersId
finalPostToShow.userName = userName
self.userPost.append(finalPostToShow)
print("there are \(self.userPost.count) posts")
guard self.userPost.count != self.userCred.count else
print("loading cells now 01")
return self.TableViewControllerHome.reloadData()
ref.removeAllObservers()
加载配置文件图像的代码单独调用:
func profileImage()
let ref = Database.database().reference()
ref.child("users").queryOrdered(byChild: "timesStamp").observeSingleEvent(of: .value) (dataSnapshots) in
let usersnap = dataSnapshots.value as? [String : AnyObject]
self.userCred.removeAll()
for (_,value) in usersnap!
if let userIdKey = value["uid"] as? String
let finalUserToShow = user()
for each in self.usersKey
if each == userIdKey
if let userPhotoImg = value["urlToImage"] as? String
finalUserToShow.ImagePath = userPhotoImg
self.userCred.append(finalUserToShow)
print(finalUserToShow)
print("there are \(self.userCred.count) profile photos")
我的火力基地结构:
"posts" :
"-LwxPtjQh6Sq9cMw-XSO" :
"attachedUsers" : [ "BtJEpoob3CPR1EN1nFRiUj6uKIX2", "OtipKVdgmyNNR9eGwVFBqyYrvL93", "PV3NEJhepRZQiNHJ7H7KO9aWGy62", "wt4PZb6UUtbeUI2v2SFRZ9hZBNt1" ],
"content" : "The 2019 Range Rover Autobiography delivers 16 mpg in ",
"creator" : "Mac",
"description" : "Rangerover ",
"likes" : 0,
"pathToImage" : "https://firebasestorage.googleapis.com/v0/b/neighbors-3a1df.appspot.com/o/posts%2Fssb9HIqEp4SxPuUZSJDUfYYLKPS2%2F-LwxPtjQh6Sq9cMw-XSO.jpeg?alt=media&token=8a87777f-d2c4-4fb9-8c7d-89b5fb0ef042",
"timesStamp" : 1577283495118,
"topic" : "Rangerover",
"userID" : "ssb9HIqEp4SxPuUZSJDUfYYLKPS2",
"views" : 0,
"webUrl" : "https://www.landrover.in/index.html"
"-Lx0z-1LlbIhpBFGNIfW" :
"attachedUsers" : [ "X0kiMno8EEclwlVvVymNK00wNw52", "OtipKVdgmyNNR9eGwVFBqyYrvL93", "38nPNJujqpeovv558FeFHqB90Qu2", "0NQbltVlx3d3yOwich9IXntyBqD3" ],
"content" : "The been",
"creator" : "Mac",
"description" : "Cybertruck ",
"likes" : 0,
"mainImages" :
"-Lx0z0_Nc6FIA_flu_Eq" : "https://firebasestorage.googleapis.com/v0/b/neighbors-3a1df.appspot.com/o/posts%2Fssb9HIqEp4SxPuUZSJDUfYYLKPS2%2F-Lx0z0_Nc6FIA_flu_Eq.jpeg?alt=media&token=becde9e1-ca9d-4e98-98ce-e1aa5edeede2",
"-Lx0z0_T1uxgmU8DCVda" : "https://firebasestorage.googleapis.com/v0/b/neighbors-3a1df.appspot.com/o/posts%2Fssb9HIqEp4SxPuUZSJDUfYYLKPS2%2F-Lx0z0_T1uxgmU8DCVda.jpeg?alt=media&token=3b71bca8-8988-481f-994c-41e07fde6972"
,
"pathToImage" : "https://firebasestorage.googleapis.com/v0/b/neighbors-3a1df.appspot.com/o/posts%2Fssb9HIqEp4SxPuUZSJDUfYYLKPS2%2F-Lx0z-1LlbIhpBFGNIfW.jpeg?alt=media&token=0108e73d-3e34-4137-8729-7e826166d871",
"timesStamp" : 1577360038766,
"topic" : "Madness",
"userID" : "ssb9HIqEp4SxPuUZSJDUfYYLKPS2",
"views" : 0,
"webUrl" : ""
,
"users" :
"0NQbltVlx3d3yOwich9IXntyBqD3" :
"dateJoined" : 1541925226084,
"followers" :
"-LR2KlE95CjaV6NLh36e" : "DjyEuBBc3YRGq8lZ1AmDxtUOUNq1",
"-LRP2W1XXhB1oerC2LJu" : "2EWMigIqM2hObmRQqFgR60T9L5r1",
"-LTHvpSl8tcVWJs8Gyef" : "9zqI9O8uU1VdeysJ8p0F51AVQG13",
"-LTTrN2UxHRMkaoZdOJj" : "jeCBp7JvXhhV1hyBCW6pGSdJWxo2",
"-Lf_gGCB_1nWpfQNIf6d" : "38nPNJujqpeovv558FeFHqB90Qu2",
"-LtPH3HCtgkT5j2Xq7Hk" : "ssb9HIqEp4SxPuUZSJDUfYYLKPS2"
,
"full Name" : "Warren Buffett",
"uid" : "0NQbltVlx3d3yOwich9IXntyBqD3",
"urlToImage" : "https://firebasestorage.googleapis.com/v0/b/neighbors-3a1df.appspot.com/o/users%2F0NQbltVlx3d3yOwich9IXntyBqD3.jpg?alt=media&token=673f8326-870e-496c-89ee-3ad9c06693ca"
,
加载图片的函数:
class CustomImageView : UIImageView
func loadImagesWithUrl(from imageUrl : String!)
self.image = nil
if let cachedImage = imageCache.object(forKey: imageUrl as AnyObject) as? UIImage
self.image = cachedImage
return
let url = URLRequest(url: URL(string: imageUrl)!)
URLSession.shared.dataTask(with: url) (data, response, eror) in
if eror != nil
print(eror!)
return
DispatchQueue.main.async
if let downloadedImage = UIImage(data : data!)
imageCache.setObject(downloadedImage, forKey: imageUrl as AnyObject)
self.image = downloadedImage
.resume()
我的表格视图 cellForRowAt :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
let cellData = TableViewControllerHome.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! viewControllerHomeCell
cellData.writingfrom.setTitle(self.userPost[indexPath.row].topicData, for: .normal)
cellData.profileImage.loadImagesWithUrl(from:self.userCred[indexPath.row].ImagePath)
cellData.profileName.text = userPost[indexPath.row].userName
cellData.dateCreated.text = userPost[indexPath.row].dateCreated
cellData.userID = userPost[indexPath.row].usrId
return cellData
【问题讨论】:
加载图像的问题没有代码,我们需要看到它,因为它是问题的一部分。第二个ref.removeAllObservers()
不需要(在这种情况下),因为您没有添加观察者; .observeSingleEvent 触发一次且仅触发一次。我们真的需要看看你的 Firebase 结构;请在您的问题中包含一个作为 TEXT 的 sn-p。最后,提示:如果您将用户存储在用户节点中,并将其 uid 作为键,则不需要此 if let userIdKey = value["uid"]
,因为 snapshot.key
将为您提供用户的 uid。更新问题,我们来看看!
@jay 更新了好友
我的评论中最重要的问题似乎被忽略了:加载图像的问题中没有代码。包含的代码只是将 URL 分配给用户类中的 var,仅此而已。没有它,我们将不知道实际加载图像的顺序是什么,这是解决您的问题所必需的:
@jay 哦!我忽略了这一点,现在更新问题
【参考方案1】:
这个答案有两个部分:一个是展示如何处理数据流的示例代码。第二个是展示如何大大缩短和收紧你的代码,因为问题中有很多无关的代码。
在您处理帖子的问题中 - 这个答案几乎是一样的;我们将遍历我们的用户节点并将用户名和个人资料图像加载到 tableView 中。名称存储在 Firebase 实时数据库中,图像存储在 Firebase 存储中(我强烈建议使用它,因为 API 很棒)。
让我们从一个存储用户名(例如您的帖子文本)的类开始,然后是个人资料图片。请注意,在某些情况下图像可能为零,因此我们需要在 tableView 刷新时处理这些情况。
class UserNameAndImageClass
var uid = ""
var name = ""
var image: NSImage?
init(withName: String, andUid: String, andImage: NSImage?)
self.name = withName
self.uid = andUid
self.image = andImage
然后是一个类级别的 var 数组,用作 tableView 的数据源
var userNameAndImageArray = [UserNameAndImageClass]()
然后是读取所有数据并填充dataSouce的代码,完成后重新加载tableView
func fetchUsersAndImages()
let storage = Storage.storage()
let storageRef = storage.reference()
let imagesRef = storageRef.child("images") //where the profile images are stored
let usersRef = self.ref.child("users")
usersRef.observeSingleEvent(of: .value, with: snapshot in
let lastIndex = snapshot.childrenCount - 1 //how many total child nodes
var currentIndex = 0 //used to trigger the reload when all data has been read
//Assign all of the child nodes to an array of DataSnapshots. This keeps them in
// order and also maintains each child node as a DataSnapshot making it easier to
// access other child data within each node
let allUsersSnap = snapshot.children.allObjects as! [DataSnapshot]
for userSnap in allUsersSnap
let uid = userSnap.key
let name = userSnap.childSnapshot(forPath: "name").value as? String ?? "No Name"
let profileImageName = "\(uid).png"
let picRef = imagesRef.child(profileImageName)
//Firebase Storage code here - insert your own if needed
picRef.getData(maxSize: 1 * 1024 * 1024) data, error in // Download in memory with a maximum allowed size of 1MB (1 * 1024 * 1024 bytes)
var aImage: NSImage?
if let imageData = data //data could be nil
aImage = NSImage(data: imageData)
let aUser = UserNameAndImageClass(withName: name, andUid: uid, andImage: aImage)
self.userNameAndImageArray.append(aUser)
if currentIndex == lastIndex //if we've loaded all of the users and their images, refresh the UI
self.myTableView.reloadData()
currentIndex += 1
)
有几点需要注意。
在您的用户节点中,您将用户 uid 存储为节点的键以及子节点。没有理由这样做 - 只需将其存储为密钥!如果您读取该节点,则始终可以像这样使用 snapshot.key 访问密钥
let uid = userSnap.key
在图片中读取的部分,请注意我使用的是一个简单的计数器,它会等到我们读取最后一张图片后才更新 UI。它们会减少闪烁。有些人会建议使用调度组来完成这项任务,这也可以。
你不需要在使用 .observeSingleEvent 后删除AllObservers,因为它只会触发一次并且不会留下观察者。
最后也是最重要的一点:不要在 tableView 函数中做任何繁重的工作!它们应该是快速、轻量级的,并且代码极简。在单元格或 tableView 函数中从 Internet 检索数据通常不是一个好主意,因为这会使应用程序滞后和糟糕的用户体验。预加载所有数据以保持 tableview 流畅。
【讨论】:
帮助很大!您展示了如何更有效、更准确地从 firebase 数据库加载数据,它几乎解决了这个问题。但是,假设用户像在 instagram 中一样上传帖子,创建了一个单独的“帖子”节点,我可以轻松地从帖子中获取数据,除了上传帖子的用户的个人资料图片不在“帖子”中node ,而是在单独的“用户”节点中。那么,我如何从两个不同的节点获取数据并将它们显示在一个 tableviewcell 中。 @mac 非常简单。您将从帖子中获取该用户的 uid。然后从用户节点中提取 url 并按照我的回答进行填充。换句话说,在let uid = userSnap.key
之后,使用该uid 并在该闭包内的用户节点上观察SingleEvent,获取图像的url,然后通过该url 检索图像。如果结构变得太深,您可能需要对其进行非规范化,但听起来并非如此。以上是关于如何以正确的方式从 firebase 数据库加载图像的主要内容,如果未能解决你的问题,请参考以下文章
从 Firebase 完成加载后如何使 UIRefreshControl 结束刷新
如何初始加载 UITableView 然后观察 Firebase 节点以更改在 Swift 中刷新 tableview?