UICollectionView滚动后不删除旧单元格

时间:2013-07-17 15:51:35

标签: ios uicollectionview

我有一个带有图像网格的UICollectionView。当您点击其中一个时,它会打开网格并显示包含一些详细信息的子视图。像这样:

Supposed to look like this

我通过调整UICollectionViewLayoutAttributes并在transform3D属性上为所选项目的当前行下面的所有单元格设置转换,在我的UICollectionViewLayout中打开网格。这非常好用,并且是比我第一次尝试将另一个单元格插入网格时更好的动画和更简单的方法。网格与其他单元格的大小不同。

无论如何......它大部分时间都有效,但在继续使用之后我会在集合视图中看到旧图像。它们就像幽灵细胞。我无法点击它们,就像它们没有被正确地从集合视图中移除一样,并且它们位于单元顶部以防止点击并且只是令人讨厌。像这样:

Problem looks like this

为什么这些细胞会这样做?

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

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

这也会对性能造成太大影响。

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

3 个答案:

答案 0 :(得分:14)

我对我的问题的第二次编辑详细说明了为什么会发生这种情况,这是我的解决方法。这不是防弹,但它适用于我的情况,如果你遇到类似的东西,你可以调整我的解决方案:

- (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];    
}

- (void) scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    if (!decelerate) {
        [self removeNaughtyLingeringCells];
    }
}

- (void) scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    [self removeNaughtyLingeringCells];
}

答案 1 :(得分:2)

对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'测试不能更直接,但它隐藏在我实际代码中的一个类别中。

答案 2 :(得分:0)

一个Swift UICollectionView扩展版本的bandejapaisa的回答:

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()
    }
}
相关问题