UICollectionView项目未正确重用(Swift 2.0+)

时间:2016-08-22 13:30:27

标签: swift image asynchronous uicollectionview

我在TableView Cell中有一个UICollectionView。 CollectionView单元格本身包含UIImageView和Label。 CollectionView单元格应显示其下方名字的用户图片。此数据模型在ViewDidLoad中构建,并输出正确的数据。但是,我的UICollectionViews在不稳定的位置产生用户图片和用户名(即,用户图片和名称显示在该不同TableView行的相应UICollectionViews内的不同TableView行上)。如果需要,我可以尝试更详细地说明这一点。

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {

    let cell = collectionView.dequeueReusableCellWithReuseIdentifier("userCell", forIndexPath: indexPath) as! wigoCollectionViewCell

    var sumSections = 0
    for  i in 0 ..< indexPath.section {

        let rowsInSection = eventFeed.numberOfRowsInSection(i)
        sumSections = sumSections + rowsInSection

    }
    let currentRow = sumSections + indexPath.row

    // Flip the collectionView
    var scalingTransform : CGAffineTransform!
    scalingTransform = CGAffineTransformMakeScale(-1, 1);
    cell.transform = scalingTransform

    // Make user profile picture circular and dispose of remaining image resources not visible
    cell.userPicture.layer.cornerRadius = cell.userPicture.frame.size.width/2
    cell.userPicture.clipsToBounds = true

    for (key, value) in (sortedArray[currentRow]["usersJoining"]!! as? NSDictionary)! {

        if key as? String != FIRAuth.auth()?.currentUser?.uid {

            var name = String()
            var photoUrl = NSURL()

            name = value["userName"] as! String

            let nameSplit = name.characters.split(" ").map(String.init)
            // Edit label
            cell.userNameLabel.text = nameSplit[0]

            // Make UIImage = user picture
            photoUrl = NSURL(string: value["userPhotoUrl"] as! String)!
            let data = NSData(contentsOfURL: photoUrl)


            cell.userPicture.image = UIImage(data: data!)

        }

    }

    return cell

}

在打印任何特定变量时,输出正是我想要的。所以这完全是在单元格中显示正确信息的问题。

据我所知,我正确使用prepareForReuse

override func prepareForReuse() {

    userPicture.image = nil
    userNameLabel.text = ""

    super.prepareForReuse()

}

我能想到的唯一解决方案围绕使用dispatch_async方法在后台线程中添加图像。但是,我似乎无法解决我的问题。

我希望有人可以帮助指导我解决问题的正确方法,因为我还没有通过StackOverflow或其他互联网找到解决我特定问题的解决方案(或解释)它非常适合Swift 2.0 +)。

提前致谢。

3 个答案:

答案 0 :(得分:0)

也许以下一些评论会对您有所帮助:

  1. 您使用UIImage初始化NSData,而不是名称,因此您必须使用适当的构造函数:

     cell.userPicture.image = UIImage(data: data!)
    
  2. 一个好的做法是下载用户的图片异步(以防止在网络连接不良时用户界面冻结):

    // Somewhere in your code
    func getImageFromUrl(url:NSURL, completion: ((data: NSData?, response: NSURLResponse?, error: NSError? ) -> Void)) { 
        NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) in
            completion(data: data, response: response, error: error)
        }.resume()
    }
    

    tableView...cellForItemAtIndexPath功能中:

    // Make UIImage = user picture
    var photoUrl = value["userPhotoUrl"] as! String
    
    // Start working in background
    let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
    dispatch_async(dispatch_get_global_queue(priority, 0)) { () -> Void in
    
    // Call the function with callback
        getImageFromUrl(NSURL(string: photoUrl)!) { (data, response, error) in
            guard let data = data where error == nil else { return }
    
            // Set the image in place in the main thread
            dispatch_async(dispatch_get_main_queue()) {
                cell.userPicture.image = UIImage(data: data!)
            }
        }
    }
    
  3. 如果单元格在滚动时开始显示不正确的数据: 我假设您在UITableViewController设置内部UICollectionView。因此,在设置每个内部集合的数据之后,尝试在主队列中调用reloadData(),如下所示:

    dispatch_async(dispatch_get_main_queue()) {
        myCollectionView.reloadData()
    }
    

答案 1 :(得分:0)

我对这个问题没有答案,但我注意到我在混合的Swift / ObjC代码库中看到的一些我要评论的内容。

Swift和Objective-C之间的主要区别之一是使用nil的类型系统和行为。在Objective-C中,使用nil或多或少是安全的(它可能导致意外行为但不会崩溃。当nil被强制转换为非可选类型时,Swift代码会崩溃。当消息发生时也会发生崩溃被发送到nil对象。

换句话说,nil在Swift中非常危险,因此应该避免强制解包运算符!和强制转换as!

在您的代码中,可以在许多地方看到这些危险操作,因此当输入意外或例如,代码可能会崩溃。无法找到图像的数据。

如果您的代码是沿着以下几行,它会更安全,也更清晰,因为它包括更少的强制演员阵容和展开。代码未经过测试,我可能对您的数据类型做了一些错误的假设。

guard let a = sortedArray as? [NSDictionary],
      let d = a[currentRow]["usersJoining"] as? NSDictionary,
      let currentUID = FIRAuth.auth()?.currentUser?.uid 
    else { return nil }

for (key, value) in d {
    guard let k = key as? String where k != currentUID,
          let name = value["userName"] as? String
        else { continue }

    guard let urlString = value["userPhotoUrl"] as? String,
          let photoUrl = NSURL(string: urlString),
          let data = NSData(contentsOfURL: photoUrl)
        else { continue }

    let nameSplit = name.characters.split(" ").map(String.init)
    let firstName = nameSplit[0]

    cell.userNameLabel.text = firstName
    cell.userPicture.image = UIImage(named: data)
}

理想情况下,解析数据以键入安全的Swift数据类型应该在除collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath)之外的其他地方完成

答案 2 :(得分:0)

我最终找到了解决问题的方法。所以对于任何好奇或有类似问题的人来说:

问题在于遍历 cellForItemAtIndexPath方法中的NSDictionary 。当以编程方式创建时,NSDictionaries本质上是无序的,并且通过遍历字典来获取我需要的数据导致出现,好像CollectionView中的单元格中的图像存在负载问题。

解决方案只是将NSDictionary变为Array并使用indexPath.item作为索引从数组中检索我需要的值。

这是一个非常简单的解决方案,根本不会发生在我身上。因此,如果您像我一样正在寻找类似问题的解决方案,请确保您的逻辑是可靠的!