【问题标题】:Clear Collection View Cells when new ones are generated (iOS7 - AFNetworking)生成新单元格时清除集合视图单元格(iOS7 - AFNetworking)
【发布时间】:2014-02-03 16:40:23
【问题描述】:

我有一个集合视图控制器,其中包含从 JSON 调用获取其内容的单元格。每个单元格都有一个滚动视图(图像幻灯片)。我注意到幻灯片正在加载在旧幻灯片之上(我可以看到旧单元格中的幻灯片出现在新幻灯片加载之前)。或者,如果我滑动到第一个单元格的第三个图像,然后滚动第四个单元格,它会显示我的第三个图像(而不是第一个),但页面控件会显示这是第一张幻灯片。

如何在生成新单元格时“清除”旧单元格(或至少清除滚动视图,或阻止其重复使用)?

Article.h (NSObject)

@interface Article : NSObject

@property (readonly) NSURL *imageURL;
@property (nonatomic, strong) NSArray *images;

- (instancetype)initWithAttributes:(NSDictionary *)attributes;

+ (void)latestNewsWithBlock:(void (^)(NSArray *news, NSError *error))block;

@end

#pragma mark -

@interface ArticleImage : NSObject

@property (readonly, nonatomic, strong) NSURL *URL;

- (instancetype)initWithAttributes:(NSDictionary *)attributes;

@end

Article.m (NSObject)

@interface Article ()
@property (readwrite, nonatomic, strong) NSURL *URL;
@end

@implementation Article

- (instancetype)initWithAttributes:(NSDictionary *)attributes {
    self = [super init];
    if (!self) {
        return nil;
    }

    self.URL = [NSURL URLWithString:attributes[@"url"]];

    NSMutableArray *mutableImages = [NSMutableArray array];

    for (NSDictionary *imageAttributes in attributes[@"photos"]) {
        NSDictionary *imageFileAttributes = [imageAttributes valueForKeyPath:@"photo_file.photo_file"];
        ArticleImage *image = [[ArticleImage alloc] initWithAttributes:imageFileAttributes];
        [mutableImages addObject:image];
    }
    self.images = mutableImages;

    return self;
}

- (NSURL *)imageURL {
    return [[self.images firstObject] URL];
}

+ (void)latestNewsWithBlock:(void (^)(NSArray *news, NSError *error))block {
    [[DeadstockAPIManager sharedManager] GET:@"articles" parameters:nil success:^(AFHTTPRequestOperation *operation, id JSON) {
        NSMutableArray *mutableNews = [NSMutableArray array];
        for (NSDictionary *attributes in JSON[@"articles"]) {
            Article *news = [[Article alloc] initWithAttributes:attributes];
            [mutableNews addObject:news];

            [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
        }

        [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;

        if (block) {
            block([NSArray arrayWithArray:mutableNews], nil);
        }
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        if (block) {
            block(nil, error);
        }
    }];
}

@end

#pragma mark -

@interface ArticleImage ()
@property (readwrite, nonatomic, strong) NSURL *URL;
@property (readwrite, nonatomic, strong) NSURL *thumbnailURL;
@end

@implementation ArticleImage

- (instancetype)initWithAttributes:(NSDictionary *)attributes {
    self = [super init];
    if (!self) {
        return nil;
    }

    self.URL = [NSURL URLWithString:[attributes valueForKeyPath:@"thumb.url"]];

    return self;
}

集合视图控制器

- (void)setLatestNews:(NSArray *)latestNews {
    _latestNews = latestNews;

    [self.collectionView reloadData];
}

- (void)loadData:(id)sender {
    [Article latestNewsWithBlock:^(NSArray *news, NSError *error) {
        self.latestNews = news;
    }];
}

#pragma mark - UIViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    [self.collectionView registerClass:[ArticleCell class] forCellWithReuseIdentifier:@"ArticleCell"];

    [self loadData:nil];
}

#pragma mark - UICollectionViewDataSource

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *identifier = @"articleCell";

    ArticleCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
    cell.article = [self.latestNews objectAtIndex:indexPath.row];

    return cell;
}

集合视图单元格

@interface ArticleCell ()
@property (readwrite, nonatomic, strong) NSArray *pageImages;
@end

@implementation ArticleCell

- (void)setArticle:(Article *)article {
    _article = article;

    self.pageImages = [self.article.images valueForKeyPath:@"URL"];

    NSUInteger pageCount = [self.pageImages count];
    self.pageControl.currentPage = 0;
    self.pageControl.numberOfPages = pageCount;
    self.pageControl.hidden = (pageCount == 1);

    for (NSInteger page = 0; page < pageCount; page++) {
        CGRect frame = self.scrollView.bounds;
        frame.origin.x = frame.size.width * page;
        frame.origin.y = 0.0f;

        UIImageView *imageView = [[UIImageView alloc] init];
        imageView.contentMode = UIViewContentModeScaleAspectFit;
        imageView.frame = frame;
        [imageView setImageWithURL:self.pageImages[page]];
        [self.scrollView addSubview:imageView];
    }

    CGSize pagesScrollViewSize  = self.scrollView.frame.size;

    self.scrollView.contentSize = CGSizeMake(pagesScrollViewSize.width * self.pageImages.count, pagesScrollViewSize.height);
}

谢谢。

【问题讨论】:

  • 顺便说一句,您通常不希望创建像 article 属性那样具有副作用的属性。相反,删除setArticle 并创建一个您调用的-(void)configureForArticle:(Article *)article 方法,然后该方法可以执行所有必要的代码并设置article 属性。

标签: ios json uiscrollview afnetworking uicollectionviewcell


【解决方案1】:

示例项目可以在这里找到:http://www.filedropper.com/collectionviewtest

在您的 ArticleCell 中将此作为公共方法实现:

- (void)cleanForReuse
{
   [[self.scrollView subviews]
               makeObjectsPerformSelector:@selector(removeFromSuperview)];
   self.scrollView.contentSize = CGSizeZero;
}

- (void)prepareForReuse
{
   [super prepareForReuse];
   self.pageImages = nil;
}

然后在重用单元格之前更新您的代码以调用 cleanForReuse:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView 
                  cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *identifier = @"articleCell";

    ArticleCell *cell = 
           [collectionView dequeueReusableCellWithReuseIdentifier:identifier 
                                                     forIndexPath:indexPath];

    [cell cleanForReuse];
    cell.article = [self.latestNews objectAtIndex:indexPath.row];

    return cell;
}

【讨论】:

  • 如果我在生成新的滚动视图后向上滚动,此代码将删除我的滚动视图。
  • @Chris 你还有其他事情要做。此代码在我的测试项目中运行。
  • 我用我拥有的 NSObject(“Article”)更新了我的代码,它解析我的 JSON 字符串以防万一。
  • 删除和重新创建子视图破坏了单元重用的大部分要点。
  • @AaronBrager 你错了。重用的重点是最大限度地减少内存流失,如果您必须不断地一遍又一遍地重新初始化一个单元格对象,并提高滚动性能,否则会因为为每一行创建一个新的单元格对象的延迟而发生。重用与单元格的内容无关。事实上,如果你从不移除 scrollView 的子视图,那么每一行的子视图数量就会增加一倍。
【解决方案2】:

您看到这一点是因为单元正在被重复使用(出于性能原因,这是一件好事)。

如果您有旧内容需要在重用时从单元格中删除,您需要在 collectionView:cellForItemAtIndexPath: 中将其删除。

在您的情况下,您需要在添加新的 imageView 之前对添加到 scrollView 的所有 imageView 调用 removeFromSuperview。

你也可以在 UICollectionViewCell 方法 prepareForReuse 中做一些复用准备。但是,出于 UITableViewCells 中的性能原因,Apple 建议“您应该只重置与内容无关的单元格属性,例如 alpha、编辑和选择状态。”。据推测,同样的建议也适用于 UICollectionViewCells。

【讨论】:

  • 对添加到 scrollView 的每个 imageView 调用 removeFromSuperview。您可能希望有一个数组属性来存储它们以使它们更易于访问。
  • 不要以这种方式使用prepareForReuse。出于性能原因,您应该只重置与内容无关的单元格属性。例如,alpha、编辑和选择状态。
  • 有趣,我以前没听说过这个。我真的不明白在 prepareForReuse 中做某事比在 collectionView:cellForItemAtIndexPath: 中做某事要耗费更多的资源。无论哪种方式,您都需要在重新使用之前重置单元格的状态。你碰巧知道关于这个主题的任何好的文章吗?我在搜索中找不到任何相关内容。
  • Apple 在 UITableViewCell: developer.apple.com/library/ios/documentation/UIKit/Reference/… 的文档中指出了这一点。应该注意的是,他们没有对UICollectionReusableView 发表相同的评论,但他们确实指出“子类可以覆盖此方法以将任何新的 data 分配给视图”,这似乎表明类似的方法(没有 UI 更改)。
  • 删除和重新创建子视图破坏了单元重用的大部分要点。例如,我将 UIImageView 的 image 设置为 nil,但在下次单元格出现时重用 UIImageView。
猜你喜欢
  • 2022-01-07
  • 1970-01-01
  • 2016-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-06
  • 2018-12-09
相关资源
最近更新 更多