Firebase 存储异步镜像下载随机顺序



我正在尝试从 firebase 存储下载图像以显示在我的 collectionview 单元格中,但图像在单元格中一直以随机顺序显示。每个单元格都有一个从 Firebase 存储(item1、item2 等(中检索的标签,该标签每次都能很好地显示在正确的单元格中。存储在 Firebase 仓库中的每张图片在 Firebase 数据库中的相应商品名称中都有其存储网址作为子项。

我完全能够检索每个图像网址,并下载所有图像并正确显示在单元格中,只是每次打开应用程序时它们都会以随机顺序出现,因此图像与项目名称标签不对应。

我意识到我需要异步下载图像,因此每个图像在继续下一个图像之前都会在正确的单元格中完成加载,但我在这样做时遇到了麻烦。这是我到目前为止的代码:

func downloadImg(completion: @escaping (UIImage?) -> ()) {
let ref = Database.database().reference().child("somePath")
ref.observeSingleEvent(of: .value) { (snapshot) in
for item in snapshot.children {
let snap = item as! DataSnapshot
let imageSnap = snap.childSnapshot(forPath: "img/storageUrl")
if let url = imageSnap.value as? String {
let someRef = self.storageRef.reference(forURL: url)
someRef.getData(maxSize: 10 * 10024 * 10024) { data, error in
if let error = error {
print(error)
} else {
let image = UIImage(data: data!)
DispatchQueue.main.async {
completion(image)
}
}
}
}
}
}
}

然后我在视图加载中调用我的函数:

downloadImg { (completion) in
self.itemPicArray.append(completion!)
self.collectionView?.reloadData()
}

最后,我将单元格图像视图设置为itemPicArray[indexPath.row]

就像我说的,除了图像不断随机显示之外,这非常有效。非常感谢帮助!

您的问题是每次图像进入时,您都会重新加载整个集合视图。根据图像的大小和网络状态,图像几乎每次都会以不同的顺序进入。

请考虑先下载所有映像,然后重新加载集合视图一次。如果有很多图像,请考虑对结果进行分页。您可以枚举循环并按此原始顺序对数据源数组进行排序。我添加了一个自定义数据对象来帮助解决这个问题。

class CustomObject {
var image: UIImage?
let n: Int
init(image: UIImage?, n: Int) {
self.image = image
self.n = n
}
}
let dispatch = DispatchGroup()
for (n, item) in snapshot.children.enumerated() {
let object = CustomObject(image: nil, n: n) // init custom object with n (image is still nil)
dispatch.enter() // enter dispatch
someRef.getData(maxSize: 10 * 10024 * 10024) { data, error in // download image
if let error = error {
print(error)
} else {
let image = UIImage(data: data!)
object.image = image // inject custom object with image
itemPicArray.append(object) // append to array
}
dispatch.leave() // leave dispatch
}
}
dispatch.notify(queue: .global()) { // dispatch completion
itemPicArray.sort { $0.n < $1.n } // sort by n (original download order)
collectionView.reloadData() // reload collection view
}

使用模型可能是个好主意。

struct Image {
var imageName: String
var image: UIImage
}

这样,无论顺序如何,项目名称(图像名称(和图像都将配对。

也许现在更好的解决方案是配置方法下载Img,以便它将imageName作为参数。然后可以调用正确的节点来获取对应的存储URL。

func downloadImg(imageName: String, completion: @escaping (Image?) -> ()) {
// Use the parameter to create your database reference    
let ref = Database.database().reference().child(imageName)
ref.observeSingleEvent(of: .value) { (snapshot) in
for item in snapshot.children {
let snap = item as! DataSnapshot
let imageSnap = snap.childSnapshot(forPath: "img/storageUrl")
if let url = imageSnap.value as? String {
let someRef = self.storageRef.reference(forURL: url)
someRef.getData(maxSize: 10 * 10024 * 10024) { data, error in
if let error = error {
print(error)
return
} 
if let image = UIImage(data: data) {
// Create a variable of type Image (your custom model)
let imageWithName = Image(imageName: imageName, image: image)
completion(imageWithName)
}
}
}
}
}
}

调用和处理可以像这样完成:

// Create a variable to hold your item name/image-pairs
var imagesWithNames = [Image]()
let dispatchGroup = DispatchGroup()
// Iterate over your array of item names
for item in itemArray {
dispatchGroup.enter()
downloadImg(item) { (imageWithName) in
self.imagesWithNames.append(imageWithName)
dispatchGroup.leave()
}
}
dispatchGroup.notify(queue: .main) { {
self.collectionView?.reloadData()
}

要填充集合视图,您可以执行以下操作:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! YourCustomCell
// Get the pair at the given index
let imageWithName = self.imagesWithNames[indexPath.row]
DispatchQueue.main.async {
// Set image and item label (example below)
self.yourImageView.image = imageWithName.image
self.yourItemLabel.text = imageWithName.imageName
}
return cell
}

如果将来有人遇到此问题,请使用 -bsod answer 并创建自定义对象。同时创建一个变量变量计数器:Int = 0。以下是我的代码看起来像 Swift 5.X 的样子,它运行良好。

class CustomObject {
var image: UIImage?
let n: Int
init(image: UIImage?, n: Int) {
self.image = image
self.n = n
}

}

func reloadStuff() {
dispatch.notify(queue: .main) {
self.imageArrayCells.sort { $0.n < $1.n }
self.contentViewProfile.collectionView.refreshControl?.endRefreshing()
self.contentViewProfile.collectionView.reloadData()
}
}
for i in 0 ..< self.imageArrayInfo.count {
let object = CustomObject(image: nil, n: i)
let urlString = self.imageArrayInfo[i]
print(object.n)
let url = URL(string: urlString)
self.dispatch.enter()
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
self.dispatch.enter()
guard let data = data, error == nil else {
return
}
guard let image = UIImage(data: data) else {
return
}
object.image = image
self.imageArrayCells.append(object)
self.dispatch.leave()
self.counter += 1
if self.counter == self.imageArrayInfo.count {
for i in 0 ..< self.imageArrayInfo.count {
self.dispatch.leave()
}
self.counter = 0
}
}
task.resume()

}

self.reloadStuff()

这是我在collectionView(cellForItemAt:_(中调用的内容

cell.imageInProfileCollection.image = imageArrayCells[indexPath.row].image

最新更新