繁体   English   中英

UICollectionView在UICollectionViewCells上调用setNeedsDisplay

[英]UICollectionView calling setNeedsDisplay on UICollectionViewCells

我有一个带有自定义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;
}

您应该将初始化语句放在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方法时,它不会绘制mini,因为当您调用时

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

在您的视图控制器中,迷你视图已被初始化并作为子视图添加到单元格中,但是当分配了MiniView的迷你iVar时,单元格的迷你iVar最有可能是nil。 换句话说,当您在initWithFrame:(CGRect)frame方法中调用此方法时:

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

此时“ _mini”为零。 然后在cellForItemAtIndexPath:方法中,分配单元格的mini,而不是MiniView的mini,因此MiniView的mini继续为nil。 您可以在cellForItemAtIndexPath中分配MiniView的mini:而不是cell的mini,这样就可以解决它。

您的单元格应该代表模型中存在的某些数据。

现在,我看到您为每个单元格创建了一个随机的背景,但是可以根据需要创建和销毁单元格,因此您确实应该将此信息存储在某个位置。

一旦确定背景存储在例如视图控制器的@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]

UICollectionView仅缓存和重用可见的单元格(例如6),即使您有100,也是如此。因此,在滚动时,它会将出现在屏幕外的单元格取为新队列。

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

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

并将它们存储在内部数组中(每一行),当委托人请求已经创建的单元格时,您将其从数组中分配给他。此解决方案将消耗更多的内存,因为所有单元格都在内存中。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM