为啥屏幕外的 UICollectionViewCells 不会更新?

Posted

技术标签:

【中文标题】为啥屏幕外的 UICollectionViewCells 不会更新?【英文标题】:Why won't UICollectionViewCells that are offscreen update?为什么屏幕外的 UICollectionViewCells 不会更新? 【发布时间】:2015-06-08 10:25:35 【问题描述】:

我有一个UICollectionView 在水平流布局中显示自定义单元格;换句话说,一些内容被放置在屏幕边界之外。 此外,我有一个手势触发 NSNotification 导致我的单元格的某些元素(即主题)发生颜色变化。除了出现在屏幕边界之外的单元格不会全部更新为新的颜色变化之外,一切都运行良好。有没有办法强制他们重绘?

NSNotification 被触发时调用的函数中,我尝试使用self.collectionView.reloadData()self.collectionView.setNeedsDisplay()self.collectionView.setNeedsLayout 重绘集合视图,但无济于事。我在自定义单元格类的awakeFromNib() 中尝试了列表的最后两个,但没有。

这是我的cellForItemAtIndexPath的代码:

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell 
            let cell = popularCollectionView!.dequeueReusableCellWithReuseIdentifier("popular", forIndexPath: indexPath) as! PopularPainting
            cell.thumbnail.image = paintings[indexPath.row].paintingImage
            cell.name!.text = paintings[indexPath.row].paintingName
            cell.price!.setTitle(paintings[indexPath.row].paintingPrice, forState: UIControlState.Normal)
            if cell.isDark 
                cell.name!.textColor = UIColor(red: 205/255, green: 205/255, blue: 205/255, alpha: 1)
                cell.price!.setTitleColor(self.kPaleBlue, forState: .Normal)
                self.popularCollectionView.reloadData()
            

            return cell

有什么建议吗?

注意:滚动到屏幕外内容并重复手势以更改主题效果很好,所以我不知道发生了什么。

【问题讨论】:

在其他线程中调用它。尝试使用performSelectorOnMainThread: 方法进行更新。 @KarthikSivam 尝试了它的 Swift 替代方案,但也没有用 请发布您的cellForItem: 方法代码或您更新集合视图单元格颜色的任何位置。 @KarthikSivam 更新了我的问题以添加代码 【参考方案1】:

您认为集合视图中的每个项目都存在屏幕外的单元格的假设是不正确的。事实上,表格视图和集合视图重用了滚动到屏幕外的单元格,以便在屏幕上出现新的单元格,因此存在的单元格仅超过一屏。

您可以在通知触发后致电reloadData。但是您需要确保collectionView:itemForRowAtIndexPath: 的实现将正确配置随后在屏幕上滚动的单元格。这可能意味着在通知触发后将状态更改保存在属性中,并在您配置 collectionView:itemForRowAtIndexPath: 中的单元格时检查该属性。

【讨论】:

我在我的自定义 UICollectionViewCell 中添加了一个 BOOL isDark 并将其设置为 true 每当触发暗模式通知时。我测试了它,它工作正常。然后,在检查cell.isDark 之后,我尝试将元素的颜色设置为collectionView:itemForRowAtIndex 中的深色,但是当我添加self.collectioView.reloadData() 并且日志中没有错误时应用程序崩溃。 :// 1.不要在collectionView:itemForRowAtIndex: 中调用self.collectionView.reloadData()。它已经在重新加载其数据。调用reloadData 的正确位置是在您设置isDark 属性后收到您的通知。 2. 你的isDark 属性不应该在单元格上,它应该在你的视图控制器上,因为它是影响所有 单元格的单一设置。 3. 如果isDarkNO,您的collectionView:itemForRowAtIndex: 方法还需要格式化颜色。所以添加一个else 块。 如果可以,您介意回答这个问题吗? ***.com/questions/30695969/… ios10 开始,至少存在一个极端情况,即单元格存在于屏幕外,cellForItem 无法到达它们,但有时它们会在屏幕上返回而不会被回收。例如,这使得取消选择变得棘手。【参考方案2】:

要解决“使用cellForItemAtIndexPath: 无法访问视图,但在屏幕上显示之前不会被回收”问题,您可以将视图初始化逻辑从collectionView:cellForItemAtIndexPath: 移动到collectionView:willDisplayCell:forItemAtIndexPath:

例如,您原来的位置:

override func collectionView(
    _ collectionView: UICollectionView,
    cellForItemAt indexPath: IndexPath
) -> UICollectionViewCell 
    let cell = collectionView.dequeueReusableCell(
        withReuseIdentifier: self.reuseIdentifier,
        for: indexPath
    )
    // Initialize cell
    return cell

您可以将其替换为:

override func collectionView(
    _ collectionView: UICollectionView,
    cellForItemAt indexPath: IndexPath
) -> UICollectionViewCell 
    return collectionView.dequeueReusableCell(
        withReuseIdentifier: self.reuseIdentifier,
        for: indexPath
    )


override func collectionView(
    _ collectionView: UICollectionView,
    willDisplay cell: UICollectionViewCell,
    forItemAt indexPath: IndexPath
) 
    // Initialize cell

这样可以确保如果cellForItemAtIndexPath: 返回nil,则单元格将在下次显示在屏幕上之前由collectionView:willDisplayCell:forItemAtIndexPath: 正确初始化。

【讨论】:

以上是关于为啥屏幕外的 UICollectionViewCells 不会更新?的主要内容,如果未能解决你的问题,请参考以下文章

如何限制 ipad 屏幕外的图像翻译

选择 TableView 中的每一行,包括屏幕外的行

为啥 Swift 不更新闭包外的变量?

屏幕外的Android动画视图不起作用

Swift - 创建屏幕上和屏幕外的 UITextView 内容的图像

延伸到屏幕外的 HTML 表格