Ios UICollectionView在滚动后不删除旧单元格

Ios UICollectionView在滚动后不删除旧单元格,ios,uicollectionview,Ios,Uicollectionview,我有一个UICollectionView和一个网格图像。当你点击其中一个时,它会打开网格并显示一个子视图和一些细节。像这样: 我在UICollectionViewLayout中打开网格,方法是调整UICollectionViewLayoutAttribute并在transform3D属性上为选定项当前行下方的所有单元格设置转换。这真的很好,是一个更好的动画和一个更简单的方法,比我第一次尝试插入另一个单元格到网格,这是一个不同于其他单元格的大小 无论如何。。。它大部分时间都能工作,但在继续使用后

我有一个UICollectionView和一个网格图像。当你点击其中一个时,它会打开网格并显示一个子视图和一些细节。像这样:

我在UICollectionViewLayout中打开网格,方法是调整UICollectionViewLayoutAttribute并在transform3D属性上为选定项当前行下方的所有单元格设置转换。这真的很好,是一个更好的动画和一个更简单的方法,比我第一次尝试插入另一个单元格到网格,这是一个不同于其他单元格的大小

无论如何。。。它大部分时间都能工作,但在继续使用后,我会在“收藏”视图中看到旧图像。它们就像幽灵细胞。我不能点击它们,就像它们没有被正确地从集合视图中移除一样,它们坐在单元格的顶部,防止点击,只是一个麻烦。像这样:

知道为什么这些细胞会这样做吗

编辑: 我想补充一点,我认为只有当我快速滚动收藏视图时才会发生这种情况。我已经编写了自己的UICollectionViewFlowLayout替换来测试它是否仍然发生。是的

编辑2: 三维变换或布局与此无关。它一定是UICollectionView中的错误。我可以通过快速滚动来利用这个漏洞,让它停止,然后查询屏幕上的视图。通常有两倍数量的细胞,但它们是隐藏的,因为它们堆叠在一起。由于我所做的翻译,我上面的实现揭示了它们

这也会影响性能


请参阅我的答案以获得解决方案。

我第二次编辑的问题详细说明了为什么会发生这种情况,下面是我的解决方法。它不是防弹的,但在我的情况下是有效的,如果您遇到类似的情况,您可以调整我的解决方案:

- (void) removeNaughtyLingeringCells {

    // 1. Find the visible cells
    NSArray *visibleCells = self.collectionView.visibleCells;
    //NSLog(@"We have %i visible cells", visibleCells.count);

    // 2. Find the visible rect of the collection view on screen now
    CGRect visibleRect;
    visibleRect.origin = self.collectionView.contentOffset;
    visibleRect.size = self.collectionView.bounds.size;
    //NSLog(@"Rect %@", NSStringFromCGRect(visibleRect));


    // 3. Find the subviews that shouldn't be there and remove them
    //NSLog(@"We have %i subviews", self.collectionView.subviews.count);
    for (UIView *aView in [self.collectionView subviews]) {
        if ([aView isKindOfClass:UICollectionViewCell.class]) {
            CGPoint origin = aView.frame.origin;
            if(CGRectContainsPoint(visibleRect, origin)) {
                if (![visibleCells containsObject:aView]) {
                    [aView removeFromSuperview];
                }
            }
        }
    }
    //NSLog(@"%i views shouldn't be there", viewsShouldntBeThere.count);

    // 4. Refresh the collection view display
    [self.collectionView setNeedsDisplay];    
}


对bandejapaisa的进一步评论是:仅在iOS 6下,我发现
UICollectionView
也有搞糟动画过渡的习惯。原始单元格将保留在原来的位置,复制副本,然后对副本进行动画处理。通常在原件上,但不总是这样。所以一个简单的边界测试是不够的

因此,我编写了一个自定义的
UICollectionView
子类,它执行以下操作:

- (void)didAddSubview:(UIView *)subview
{
    [super didAddSubview:subview];

    //
    // iOS 6 contains a bug whereby it fails to remove subviews, ever as far as I can make out.
    // This is a workaround for that. So, if this is iOS 6...
    //
    if(![UIViewController instancesRespondToSelector:@selector(automaticallyAdjustsScrollViewInsets)])
    {
        // ... then we'll want to wait until visibleCells has definitely been updated ...
        dispatch_async(dispatch_get_main_queue(),
        ^{
            // ... then we'll manually remove anything that's a sub of UICollectionViewCell
            // and isn't currently listed as a visible cell
            NSArray *visibleCells = self.visibleCells;
            for(UIView *view in self.subviews)
            {
                if([view isKindOfClass:[UICollectionViewCell class]] && ![visibleCells containsObject:view])
                    [view removeFromSuperview];
            }
        });
    }
}

很明显,“这是iOS 6吗”测试不能再直接一点了,但它隐藏在我实际代码的一个类别中,这是一个遗憾。

bandejapaisa答案的Swift UICollectionView扩展版本:

extension UICollectionView {

    func removeNaughtyLingeringCells() {

        // 1. Find the visible cells
        let visibleCells = self.visibleCells()
        //NSLog("We have %i visible cells", visibleCells.count)


        // 2. Find the visible rect of the collection view on screen now
        let visibleRect = CGRectOffset(bounds, contentOffset.x, contentOffset.y)
        //NSLog("Rect %@", NSStringFromCGRect(visibleRect))


        // 3. Find the subviews that shouldn't be there and remove them
        //NSLog("We have %i subviews", subviews.count)
        for aView in subviews {
            if let aCollectionViewCell = aView as? UICollectionViewCell {

                let origin = aView.frame.origin
                if (CGRectContainsPoint(visibleRect, origin)) {
                    if (!visibleCells.contains(aCollectionViewCell)) {
                        aView.removeFromSuperview()
                    }
                }

            }
        }

        // 4. Refresh the collection view display
        setNeedsDisplay()
    }
}

你能发布一个截图吗?奇怪的随机时刻,几个小时前看到的。查看您的个人资料页面。有一个应用程序的想法。搜索Flickr API。在Github上看到FlickrKit。他认出了这个名字。英雄联盟网络概念!是的,我四处走动。。。。。你在利兹,我来自利兹。。。实际上,我正准备开车去那里。从未来的角度来看:我似乎在iOS 6下遇到了这个问题,但在iOS 7下却没有。当细胞在6岁以下通过动画移动时,它们通常会留下拷贝;滚动时,如果我在集合视图单元格的
removeFromSuperview
或集合视图的
willRemoveSubview的创造性子类中添加代码以检查是否删除:
我看到视图从未被删除。在iOS 7下,我看不到这样的问题。所以我认为苹果已经修复了这个错误。根据到目前为止的评论,我觉得我还应该补充一句:我在约克住了五年,那时。你也应该看看这篇文章。我认为这是同样的事情,但更优雅的解决方案。我还没有完全测试过。非常感谢!在我的例子中,在相同的情况下删除
隐藏的
单元格效果很好。另外,我重现这个问题的方法是:1。滚动非常快。2.等待减速停止。3.删除单元格。4.等待删除动画完成。我在快速滚动调用performBatchUpdate:animations时看到了这种奇怪的“幽灵”效果,但我不知道它是什么。我永远不会想到collection视图正在为当前屏幕上的单元格创建一个新的单元格,这似乎适得其反。接得好,谢谢
extension UICollectionView {

    func removeNaughtyLingeringCells() {

        // 1. Find the visible cells
        let visibleCells = self.visibleCells()
        //NSLog("We have %i visible cells", visibleCells.count)


        // 2. Find the visible rect of the collection view on screen now
        let visibleRect = CGRectOffset(bounds, contentOffset.x, contentOffset.y)
        //NSLog("Rect %@", NSStringFromCGRect(visibleRect))


        // 3. Find the subviews that shouldn't be there and remove them
        //NSLog("We have %i subviews", subviews.count)
        for aView in subviews {
            if let aCollectionViewCell = aView as? UICollectionViewCell {

                let origin = aView.frame.origin
                if (CGRectContainsPoint(visibleRect, origin)) {
                    if (!visibleCells.contains(aCollectionViewCell)) {
                        aView.removeFromSuperview()
                    }
                }

            }
        }

        // 4. Refresh the collection view display
        setNeedsDisplay()
    }
}