生成新单元格时清除集合视图单元格(iOS7 - AFNetworking)
Posted
技术标签:
【中文标题】生成新单元格时清除集合视图单元格(iOS7 - AFNetworking)【英文标题】:Clear Collection View Cells when new ones are generated (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
属性。
【参考方案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。以上是关于生成新单元格时清除集合视图单元格(iOS7 - AFNetworking)的主要内容,如果未能解决你的问题,请参考以下文章