UICollectionView在UICollectionViewCells上调用setNeedsDisplay

时间:2013-12-03 10:58:28

标签: ios iphone objective-c uicollectionview uicollectionviewcell

我有一个带有自定义UICollectionViewCell类的UICollectionView,我在其中绘制了必要的内容。然而,我发现当我向上和向下滚动集合视图时,它会重新绘制每个单元格,因为它会滚动并再次返回到屏幕上。这导致相当不稳定的滚动。我认为UICollectionViewCells是自动缓存和重用的,所以我不明白它为什么不断重绘它们。

我在自定义UICollectionViewCell的子视图的setNeedsDisplay中有一个NSLog语句,这就是我知道它重绘它们的原因。这是我用于绘制信息的UICollectionView代码。 (PFObject只是使用Parse从数据库中检索的对象)

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    MiniCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"MiniCell" forIndexPath:indexPath];

    if (!cell.mini) {
        PFObject *pfMini = [_searchResults objectAtIndex:indexPath.row];
        Mini *mini = [Mini instanceWithPFObject:pfMini];
        cell.mini = mini;
        cell.backgroundColor = [UIColor grayColor];
    }
    return cell;
}

我的自定义单元格。这里的MiniView是我为显示图像而创建的自定义视图,我在其他各个地方使用它:

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
    }
    return self;
}

- (void)drawRect:(CGRect)rect
{
    [super drawRect:rect];

    MiniView *miniView = [[MiniView alloc] initWithFrame:rect];
    miniView.mini = _mini;
    [self addSubview:miniView];

    UIImageView *backgroundImage = [[UIImageView alloc] initWithImage:[self randomBackground]];
    self.backgroundView = backgroundImage;
}

- (UIImage *)randomBackground
{
    int backgroundInt = arc4random()%26 + 8;
    UIImage *background = [UIImage imageNamed:[NSString stringWithFormat:@"MainBackground%i", backgroundInt]];
    return background;
}

3 个答案:

答案 0 :(得分:4)

您应该将初始化语句放在initWithFrame:方法中。

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        MiniView *miniView = [[MiniView alloc] initWithFrame:rect];
        miniView.mini = _mini;
        [self addSubview:miniView];

        UIImageView *backgroundImage = [[UIImageView alloc] initWithImage:[self randomBackground]];
        self.backgroundView = backgroundImage;
    }
    return self;
}

我不记得在没有实际绘制内容的情况下初始化在drawRect:方法中完成。如果您想实际绘制单元格的内容,您应该使用图形上下文:

UIGraphicsBeginImageContext(self.bounds.size);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *imageOfContextSnapshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

这将获取当前上下文的“快照”。您可以将其视为将对象粘贴到画布上。在drawRect:方法中调用它应绘制您的内容。请注意,您应该只更新drawRect:方法中的上下文,否则您将收到并发生错误。此外,如果您实际上将调用绘图方法,则仅使用drawRect:方法。在创建新的UIView类时,drawRect:方法甚至声明只有在绘制内容时才应该使用该方法。

有关详细信息,请查看Apple的参考资料。他们对图形上下文以及如何使用它有非常详尽的解释。

https://developer.apple.com/library/mac/documentation/graphicsimaging/conceptual/drawingwithquartz2d/dq_context/dq_context.html

编辑1:

根据单元格的数量,您可能需要考虑不绘制任何内容。 dequeueReusableCellWithIdentifier已经回收了单元格内容,并且做得非常好。你应该在必要的同时增加复杂性。虽然这对我来说很容易说,因为我不知道MiniView正在/正在做什么。

此外,如果您想要更好的性能,您可能需要考虑特定于对象的绘图方法,而不是使用renderInContext:。例如,NSSString具有:

-drawAtPoint:forWidth:withFont:
-drawAtPoint:forWidth:withFont:

对于绘制文本而言,这些表现更好,而不是渲染整个上下文。要绘制图像,您应该致电:

-drawInRect:

必须在图形上下文中调用它们,例如在drawRect:方法中。

编辑2:

在初始化init方法时不会绘制迷你,因为当你调用

PFObject *pfMini = [_searchResults objectAtIndex:indexPath.row];
Mini *mini = [Mini instanceWithPFObject:pfMini];
cell.mini = mini;
cell.backgroundColor = [UIColor grayColor];

在视图控制器中,迷你视图已初始化并作为子视图添加到单元格中,但当分配MiniView的mini iVar时,单元格的mini iVar很可能是零。换句话说,当你在initWithFrame:(CGRect)框架方法中调用它时:

MiniView *miniView = [[MiniView alloc] initWithFrame:rect];
miniView.mini = _mini;
[self addSubview:miniView];

此时“_mini”为零。在你的cellForItemAtIndexPath:方法中,你正在分配单元格的mini,而不是MiniView的mini,因此MiniView的mini继续为零。您可以在cellForItemAtIndexPath中分配MiniView的mini:而不是单元格的mini,这应该可以解决它。

答案 1 :(得分:2)

您的单元格应代表模型中的一些数据。

现在我看到你为每个单元格创建了一个随机背景,但是可以根据需要创建和销毁单元格,所以你真的应该将这些信息存储在某个地方。

一旦您确定背景存储在视图控制器的@property NSArray *backgrounds中,就可以使用标准模式:

@interface MyCell : UITableViewCell

@property NSString *backgroundName; 
@property (weak) MiniView *miniView;
...

@implementation MyCell

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier] ) {
        MiniView *miniView = [[MiniView alloc] initWithFrame:rect];
        [self addSubview:miniView];
        self.miniView = miniView;
      // continue creating all necessary subviews ...
}

- (void)setBackgroundName:(NSString *)name {
    // assign background 
}

然后,当您将单元格出列时,可以使用cell.backgroundName = backgrounds[index]

答案 2 :(得分:0)

UICollectionView仅缓存并重用例如6中的可见单元格,即使你有100个。所以当你滚动它时,需要离屏的单元格然后作为新单元格去除。

如果要将所有单元格保存在内存中,可以创建单元格

MyCell *cell = [[MyCell alloc] initWithFrame:CGRectMake(...)];

并将它们存储在内部数组中(对于每一行),当委托请求已经创建的单元格时,你给了他一个来自array.This解决方案将消耗更多内存,因为所有单元格都将在内存中。