14

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

应该是这个样子

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

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

问题看起来像这样

任何想法为什么这些细胞会这样做?

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

编辑 2: 3d 变换或布局与此无关。一定是 UICollectionView 的 bug。我可以通过快速滚动、停止然后查询屏幕上的视图来利用。单元格的数量通常是原来的两倍,但它们是隐藏的,因为它们彼此堆叠在一起。由于我所做的翻译,我上面的实现揭示了它们。

这也确实会损害性能。

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

4

3 回答 3

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];
}
于 2013-07-18T12:20:24.507 回答
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”测试不能更直接一点,但它隐藏在我实际代码中的一个类别中。

于 2014-03-06T13:32:49.490 回答
1

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()
    }
}
于 2016-07-26T07:11:49.537 回答