繁体   English   中英

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

[英]Swift - Firebase - Downloading images and data asynchronously leads to wrong display within the collection view cell

我有一个集合视图,想从 firebase 异步加载图像和其他数据,并将它们显示在单元格中。 但是,我当前的方法向文本数据显示了错误的图像(它们根本不适合),而且,一个特定单元格中的图像会更改几次,直到稳定下来(有时是错误的,有时是正确的)。

我的代码

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
}

... 只是为了向您展示我的MainViewCollectionViewCell

class MainViewCollectionViewCell: UICollectionViewCell  {
...
public func fetchChallengeImageById(challengeObject:Challenge) {
    self.dataAccessService.fetchChallengeImageById(completion: { (challengeImage) in
        self.challengeImageView.image = challengeImage
        self.layoutSubviews()
    }, challengeId: challengeObject.id)
} 

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)
            }
        }
    })
}

谁能告诉我需要更改什么才能使文本数据适合单元格中的正确图像?

通过异步调用来获取用户数据,单元格被重用的事实引入了两个问题:

  1. 重新使用单元格时,请确保在异步请求进行期间不显示前一个单元格的值。 要么让collectionView(_:cellForItemAt:)重置这些值,要么更好地让单元格的prepareForReuse确保控件被重置。

  2. 在异步请求完成处理程序中,在更新之前检查单元格是否仍然可见。 您可以通过调用collectionView.cellForItem(at:)完成此操作。 如果生成的cellnil ,则该单元格不可见,并且没有任何可更新的内容。

因此:

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
}

显然,如果一个异步完成处理程序发起另一个异步请求,那么您必须重复此模式。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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