简体   繁体   English

Swift - Firebase - 异步下载图像和数据会导致集合视图单元格中的显示错误

[英]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.swiftDataAccessService.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:通过异步调用来获取用户数据,单元格被重用的事实引入了两个问题:

  1. 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确保控件被重置。

  2. 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.如果生成的cellnil ,则该单元格不可见,并且没有任何可更新的内容。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM