在 UITableView Swift 中滚动时图片变得混合

Posted

技术标签:

【中文标题】在 UITableView Swift 中滚动时图片变得混合【英文标题】:Pictures getting Mixed when scrolling in UITable View Swift 【发布时间】:2018-07-16 18:57:45 【问题描述】:

我的程序中有一个带有 dequeueReusableCells 的 UITable 视图 我应该从服务器加载几张图片并在幻灯片中显示它们

我有一个自定义单元格,在配置每个单元格时,我将图像下载到 DispatchQueue.global(qos: .userInitiated).async 和 DispatchQueue.main.async 中,我将下载的图片添加到幻灯片图像中

但是当我开始滚动一些不应该有任何图片的单元格时,有另一个单元格的重复图片

你知道是什么原因造成的吗?!

我正在使用 swift 和 ImageSlideShow pod 来播放每个单元格中的幻灯片

这是我的代码的一些部分:

在我的新闻单元类中,我有以下部分用于获取图像:

class NewsCell: UITableViewCell

    @IBOutlet weak var Images: ImageSlideshow!
    @IBOutlet weak var SubjectLbl: UILabel!
    @IBOutlet weak var NewsBodyLbl: UILabel!

    func configureCell(news: OneNews) 


        self.SubjectLbl.text = news.Subject
        self.NewsBodyLbl.text =  news.Content



        if news.ImagesId.count==0
            self.Images.setImageInputs([ImageSource(image: UIImage(named: "ImagePlaceholderIcon")!)])
         
        else
            for imgId in news.ImagesId 

                let Url = URL(string: "\(BASE_URL)\(NEWS_PATH)/\(imgId)/pictures")
                DispatchQueue.global(qos: .userInitiated).async 

                    let data = try? Data(contentsOf: Url!)

                    DispatchQueue.main.async 

                        if let d = data 
                            let img = UIImage(data: data!)!
                            imageSrc.append(ImageSource(image: img))
                            self.Images.setImageInputs(imageSrc);
                        
                    
                
            

        
    self.Images.slideshowInterval = 3


这是 cellForRow 方法:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 

    let cell = generalNewsTableView.dequeueReusableCell(withIdentifier: "NewsCell" , for: indexPath) as!  NewsCell

    if let news = NewsInfo.sharedInstance.getGeneralNews()
        cell.configureCell(news: news[indexPath.row])

    
    return cell


getGeneralNews() 是一个 getter,它返回一个新闻数组 所以我在 cellForRowAt 中所做的是我在给定的索引路径中获取新闻并用它配置我的单元格。

class NewsInfo 

static var sharedInstance = NewsInfo()

private init()

private (set) var generalNews:[OneNews]!
    didSet
        NotificationCenter.default.post(name:
            NSNotification.Name(rawValue: "GeneralNewsIsSet"), object: nil)
    

func setGeneralNews(allGeneralNews:[OneNews])
    self.generalNews = allGeneralNews

func getGeneralNews() -> [OneNews]!
    return self.generalNews


每条新闻都包含一个图片ID数组

这些是我的 OneNews 类中的字段 变量主题:字符串! 变量内容:字符串! var ImagesId:[Int]!

谢谢!

【问题讨论】:

你为什么不显示你的getGeneralNews()configureCell(news:)?您可能对这些方法做错了什么。例如,如果getGeneralNews() 是一个异步方法,那么您描述的事情很容易发生。无论如何显示足够的代码来解决您的问题。 @OOPer 我刚刚编辑了这个问题。请让我知道代码的任何其他部分是否可以提供帮助,并感谢您的时间和努力! :) OOPer 要求您发布这两种方法的代码。也许您应该编辑您的问题以显示这些方法? 问题是您假设您对Data(contentsOf: Url!) 的调用将按照您发出它们的顺序完成。他们可能不会。您应该创建一个以 URL 为键的字典并使用它来查找您的图像。实际上,您可能不想将所有图像加载到内存中的数组中。如果您的表格视图变大,您将耗尽内存并崩溃。最好将文件保存到磁盘并保留一组可选文件 URL。 @DuncanC 所以不会一遍又一遍地将它们加载为 URL 并让应用程序停止工作几秒钟?!你的意思是每次我想加载图片时,我都应该从它的 URL 中获取它? 【参考方案1】:

UITableViewCell 在您滚动时被重用。当一个单元格离开屏幕顶部时,它将被重新用于出现在屏幕底部的另一行。

UITableViewCell 有一个方法prepareForReuse 你可以覆盖。您可以使用该方法清除 iamgeViews 或任何其他应重置或取消图像下载的状态。

在您的情况下,您可能不应该使用Data(contentsOf:),因为它无法让您取消它。 URLSessionDataTask 会是更好的选择,因为它可以让您在请求完成之前取消它。

【讨论】:

确实如此。我无法使用 prepareForUse 因为它似乎不再可用但正如你所说的问题是我需要取消下载因为已经开始下载的图片出现在新的重用单元格中。感谢您的帮助 prepareForReuse 仍然可用。 developer.apple.com/documentation/uikit/uitableviewcell/…【参考方案2】:

你可以试试这样的。这段代码的主要思想是给出一个唯一的编号来检查单元格是否被重复使用。

我已在您的代码中重命名了许多属性,因为非类型的大写标识符使代码难以阅读。您不能只替换原始 NewsCell 的整个定义。 原始定义中没有imageSrc 的声明。我认为它是一个局部变量。如果它是一个全局变量,它可能会导致其他问题,您应该避免。

(重要行标有###。)

class NewsCell: UITableViewCell 

    @IBOutlet weak var images: ImageSlideshow!
    @IBOutlet weak var subjectLbl: UILabel!
    @IBOutlet weak var newsBodyLbl: UILabel!

    //### An instance property, which holds a unique value for each cellForRowAt call
    var uniqueNum: UInt32 = 0

    func configureCell(news: OneNews) 
        self.subjectLbl.text = news.subject
        self.newsBodyLbl.text =  news.content


        let refNum = arc4random() //### The output from `arc4random()` is very probably unique.
        self.uniqueNum = refNum //### Assign a unique number to check if this cell is reused
        if news.imagesId.count==0 
            self.images.setImageInputs([ImageSource(image: UIImage(named: "ImagePlaceholderIcon")!)])
         else 
            var imageSrc: [ImageSource] = [] //###
            for imgId in news.imagesId 

                let Url = URL(string: "\(BASE_URL)\(NEWS_PATH)/\(imgId)/pictures")
                DispatchQueue.global(qos: .userInitiated).async 

                    let data = try? Data(contentsOf: Url!)

                    DispatchQueue.main.async 
                        //### At this point `self` may be reused, so check its `uniqueNum` is the same as `refNum`
                        if self.uniqueNum == refNum, let d = data 
                            let img = UIImage(data: d)!
                            imageSrc.append(ImageSource(image: img))
                            self.images.setImageInputs(imageSrc)
                        
                    
                
            
        
        self.images.slideshowInterval = 3
    

请记住,图片的顺序可能与您的OneNews 中的imagesId 的顺序不同(如Duncan C 的评论中所述)。

请尝试。

【讨论】:

我测试了它并且它有效!非常感谢您的时间和帮助以及编写代码! :-)))【参考方案3】:

如果您想尝试使用这个小代码修复,而不覆盖单元格的 prepareForReuse,只需更改配置单元格:

if news.ImagesId.count==0
    self.Images.setImageInputs([ImageSource(image: UIImage(named: "ImagePlaceholderIcon")!)])
 
else
// STUFF

self.Images.setImageInputs([ImageSource(image: UIImage(named: "ImagePlaceholderIcon")!)]) 

if news.ImagesId.count > 0
// STUFF

所以即使重复使用,每个单元格都会以 placeholderIcon 开头

【讨论】:

感谢您抽出宝贵时间,虽然我测试了它但它没有用。我认为问题在于,如上所述,当一个单元重新使用前一个单元的图片时仍在下载,即使我们为新单元设置了一个占位符,下载的图片也会替换它 嗯嗯明白了。因此,也许通过调度任务它也可以工作,取消前一个任务并创建一个新任务。无论如何,你已经得到了解决方案,所以欢呼:)

以上是关于在 UITableView Swift 中滚动时图片变得混合的主要内容,如果未能解决你的问题,请参考以下文章

在 UITableView Swift 中滚动时图片变得混合

UITableView Swift 不滚动 Swift

UIScrollView 中的 Swift UITableView(不滚动)

添加/删除 UITableView 选择到数组,复选标记在滚动时消失 - Swift

UITableView 不滚动到底部(Swift IOS)

Swift4滚动UITableview后,动画UITableview框架是错误的