[英]Swift - Firebase - Downloading images and data asynchronously leads to wrong display within the collection view cell
I have a collection view and want to load images and other data asynchronously from firebase and display them within the cell.我有一个集合视图,想从 firebase 异步加载图像和其他数据,并将它们显示在单元格中。 However, my current approach displays wrong images to the text data (they simply don't fit) and also, the image in the one specific cell changes few times until it settles down (sometime wrong, sometimes correct).
但是,我当前的方法向文本数据显示了错误的图像(它们根本不适合),而且,一个特定单元格中的图像会更改几次,直到稳定下来(有时是错误的,有时是正确的)。
My code我的代码
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let photoCell = collectionView.dequeueReusableCell(withReuseIdentifier: "mainViewCollectionCell", for: indexPath) as! MainViewCollectionViewCell
// issue when refreshing collection view after a new challenge has been created
if (hitsSource?.hit(atIndex: indexPath.row) == nil) {
return photoCell
}
let challengeObject = Challenge(json: (hitsSource?.hit(atIndex: indexPath.row))!)
let group = DispatchGroup()
group.enter()
// async call
self.checkIfChallengeIsBlocked(completionHandler: { (IsUserBlocked) in
if (IsUserBlocked) {
return
}
else {
group.leave()
}
}, challengeObject: challengeObject)
group.notify(queue: .main) {
photoCell.setChallengeLabel(title: challengeObject.title)
// async call
photoCell.fetchChallengeImageById(challengeObject: challengeObject)
photoCell.checkIfToAddOrRemovePlayIcon(challengeObject: challengeObject)
// async call
self.dataAccessService.fetchUserById(completionHandler: { (userObject) in
photoCell.setFullName(userObject: userObject)
photoCell.setStarNumber(challengeObject: challengeObject)
}, uid: challengeObject.organizerId)
// async all
self.dataAccessService.fetchAllParticipantsByChallengeId(completionHandler: { (participationArray) in
photoCell.setParticipationNumber(challengeObject: challengeObject, participationArray: participationArray)
}, challengeId: challengeObject.id)
// resize image to collection view cell
self.activityView.removeFromSuperview()
}
return photoCell
}
... Just to show you my MainViewCollectionViewCell ... 只是为了向您展示我的MainViewCollectionViewCell
class MainViewCollectionViewCell: UICollectionViewCell {
...
public func fetchChallengeImageById(challengeObject:Challenge) {
self.dataAccessService.fetchChallengeImageById(completion: { (challengeImage) in
self.challengeImageView.image = challengeImage
self.layoutSubviews()
}, challengeId: challengeObject.id)
}
and DataAccessService.swift和DataAccessService.swift
class DataAccessService {
...
// fetch main challenge image by using challenge id
public func fetchChallengeImageById(completion:@escaping(UIImage)->(), challengeId:String) {
//throws {
BASE_STORAGE_URL.child(challengeId).child(IMAGE_NAME).getData(maxSize: 1 * 2048 * 2048,
completion:({ data, error in
if error != nil {
print(error?.localizedDescription as Any)
let notFoundImage = UIImage()
completion(notFoundImage)
} else {
let image = UIImage(data: data!)!
completion(image)
}
}))
}
... ...
public func fetchUserById(completionHandler:@escaping(_ user: User)->(), uid:String?) { //
throws{
var userObject = User()
let _userId = UserUtil.validateUserId(userId: uid)
USER_COLLECTION?.whereField("uid", isEqualTo: _userId).getDocuments(completion: {
(querySnapshot, error) in
if error != nil {
self.error = error
print(error?.localizedDescription as Any)
} else {
for document in querySnapshot!.documents {
userObject = User(snapShot: document)
completionHandler(userObject)
}
}
})
}
Could anyone tell me what I need to change for being able to fit the text data to the correct image in the cell?谁能告诉我需要更改什么才能使文本数据适合单元格中的正确图像?
With asynchronous calls to fetch user data, the fact that cells are re-used introduces two issues:通过异步调用来获取用户数据,单元格被重用的事实引入了两个问题:
When a cell is re-used, make sure that you do not show the values for the prior cell while your asynchronous request is in progress.重新使用单元格时,请确保在异步请求进行期间不显示前一个单元格的值。 Either have
collectionView(_:cellForItemAt:)
reset the values or, better, have the cell's prepareForReuse
make sure the controls are reset.要么让
collectionView(_:cellForItemAt:)
重置这些值,要么更好地让单元格的prepareForReuse
确保控件被重置。
In the asynchronous request completion handler, check to see if the cell is still visible before updating it.在异步请求完成处理程序中,在更新之前检查单元格是否仍然可见。 You do this by calling
collectionView.cellForItem(at:)
.您可以通过调用
collectionView.cellForItem(at:)
完成此操作。 If the resulting cell
is nil
, then the cell is not visible and there's nothing to update.如果生成的
cell
为nil
,则该单元格不可见,并且没有任何可更新的内容。
Thus:因此:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let photoCell = collectionView.dequeueReusableCell(withReuseIdentifier: "mainViewCollectionCell", for: indexPath) as! MainViewCollectionViewCell
// make sure to initialize these so if the cell has been reused, you don't see the old values
photoCell.label.text = nil
photoCell.imageView.image = nil
// now in your asynchronous process completion handler, check to make sure the cell is still visible
someAsynchronousProcess(for: indexPath.row) {
guard let cell = collectionView.cellForItem(at: indexPath) else { return }
// update `cell`, not `photoCell` in here
}
return photoCell
}
Obviously, if one asynchronous completion handler initiates another asynchronous request, then you have to repeat this pattern.显然,如果一个异步完成处理程序发起另一个异步请求,那么您必须重复此模式。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.