2013-09-03 64 views
8

Tôi có một ViewController với một CollectionView bên trong. Khi khung nhìn tải, các ô hiển thị (9 ô) được hiển thị chính xác. Khi tôi cuộn xuống, tôi muốn tải các mục hiển thị trong collectionview bằng loadImagesForOnscreenRows bằng cách gọi indexPathsForVisibleItems cho partnerCollectionView. Nhưng khi loadImagesForOnscreenRows indexPathsForVisibleItems có allways 9 ô đầu tiên trong đó, ngay cả khi các ô 10 đến 18 sẽ hiển thị trên màn hình. Code tôi sử dụng là:UICollectionView indexPathsForVisibleItems không cập nhật các ô hiển thị mới

#import "PartnerListViewController.h" 
#import "AppDelegate.h" 
#import "Partner.h" 
#import "ImageLoader.h" 
#import "PartnerDetailViewController.h" 

@interface PartnerListViewController() 

@end 

@implementation PartnerListViewController 

@synthesize lblTitle; 
@synthesize partnerCollectionView; 

@synthesize imageDownloadsInProgress; 

@synthesize fetchedResultsController; 
@synthesize managedObjectContext; 

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 
    // Do any additional setup after loading the view. 

    AppDelegate * appDelegate = (AppDelegate *) [[UIApplication sharedApplication] delegate]; 
    managedObjectContext = [appDelegate managedObjectContext]; 
    imageDownloadsInProgress = [NSMutableDictionary dictionary]; 
    appDelegate = nil; 

    [self setupFetchedResultsController]; 
    [partnerCollectionView reloadData]; 
} 

- (void)didReceiveMemoryWarning 
{ 
    [super didReceiveMemoryWarning]; 
    // Dispose of any resources that can be recreated. 
} 

#pragma mark - Collection view data source 

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { 
    return [[fetchedResultsController sections] count]; 
} 

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { 
    id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section]; 
    return [sectionInfo numberOfObjects]; 
} 

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

    Partner *partner = [self.fetchedResultsController objectAtIndexPath:indexPath]; 

    UIImageView *imageView = (UIImageView *)[cell viewWithTag:100]; 
    if (!partner.image) 
    { 
     imageView.image = [UIImage imageNamed:@"annotationMap.png"]; 
     if (self.partnerCollectionView.dragging == NO && self.partnerCollectionView.decelerating == NO) 
     { 
      [self startDownload:partner.imageUrl forIndexPath:indexPath]; 
     } 
    } else { 
     imageView.image = [UIImage imageWithData:partner.image]; 
    } 

    UILabel *lblTitlePartner = (UILabel *)[cell viewWithTag:101]; 
    lblTitlePartner.text = partner.title; 
    lblTitlePartner.font = [UIFont fontWithName:fontName size:10]; 

    return cell; 
} 

#pragma mark - Table cell image support 
- (void)startDownload:(NSString *)urlString forIndexPath:(NSIndexPath *)indexPath 
{ 
    NSLog(@"startDownload:%ld", (long)indexPath.row); 

    ImageLoader *imageLoader = [imageDownloadsInProgress objectForKey:indexPath]; 
    imageLoader = [[ImageLoader alloc] init]; 
    imageLoader.urlString = urlString; 
    imageLoader.indexPathTableView = indexPath; 
    imageLoader.delegate = (id)self; 
    [imageDownloadsInProgress setObject:imageLoader forKey:indexPath]; 
    [imageLoader startDownload]; 
} 

// this method is used in case the user scrolled into a set of cells that don't have their app icons yet 
- (void)loadImagesForOnscreenRows 
{ 
    NSArray *visiblePaths = [self.partnerCollectionView indexPathsForVisibleItems]; 
    NSMutableArray *rowsArray = [NSMutableArray arrayWithCapacity:[visiblePaths count]]; 
    [visiblePaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) { 
     NSLog(@"loadImagesForOnscreenRows1%@", @(indexPath.item)); 
     [rowsArray addObject:@(indexPath.item)]; 
    }]; 
    for (NSIndexPath *indexPath in visiblePaths) 
    { 
     NSLog(@"loadImagesForOnscreenRows2:%ld", (long)indexPath.row); 

     Partner *item = [self.fetchedResultsController objectAtIndexPath:indexPath]; 

     if (!item.image) // avoid the app icon download if the app already has an icon 
     { 
      [self startDownload:item.imageUrl forIndexPath:indexPath]; 
     } 
    } 
} 

// called by our ImageDownloader when an icon is ready to be displayed 
- (void)imageLoaderDidFinishDownloading:(NSIndexPath *)indexPath 
{ 
    NSLog(@"imageLoaderDidFinishDownloading:%ld", (long)indexPath.row); 

    ImageLoader *imageLoader = [imageDownloadsInProgress objectForKey:indexPath]; 
    if (imageLoader != nil) 
    { 
     // Save the newly loaded image 
     Partner *item = [self.fetchedResultsController objectAtIndexPath:indexPath]; 
     item.image = UIImageJPEGRepresentation(imageLoader.image, 1.0); 

     [self performSelectorOnMainThread:@selector(saveItem) withObject:nil waitUntilDone:YES]; 
     [self performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:YES]; 
    } 
} 

- (void)saveItem 
{ 
    [self.managedObjectContext save:nil]; 
} 

- (void)reloadData 
{ 
    [self.partnerCollectionView reloadData]; 
} 

#pragma mark deferred image loading (UIScrollViewDelegate) 

// Load images for all onscreen rows when scrolling is finished 
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate 
{ 
    if (!decelerate) 
{ 
     [self loadImagesForOnscreenRows]; 
    } 
} 

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 
{ 
    [self loadImagesForOnscreenRows]; 
} 

- (void)setupFetchedResultsController 
{ 
    // 1 - Decide what Entity you want 
    NSString *entityName = @"Partner"; // Put your entity name here 
    NSLog(@"Setting up a Fetched Results Controller for the Entity named %@", entityName); 

    // 2 - Request that Entity 
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:entityName]; 

    // 4 - Sort it if you want 
    request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"title" ascending:NO selector:@selector(localizedCaseInsensitiveCompare:)]]; 
    // 5 - Fetch it 
    self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil]; 
    NSError *error = nil; 
    if (![[self fetchedResultsController] performFetch:&error]) { 
     NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 
    abort(); 
    } 
} 

@end 

Và kết quả này trong đầu ra:

chương trình ban đầu các mặt hàng có thể nhìn thấy

2013-09-02 06:45:21.940 [2564:c07] startDownload:0 
2013-09-02 06:45:21.943 [2564:c07] imageLoaderDidFinishDownloading:0 
2013-09-02 06:45:21.950 [2564:c07] startDownload:1 
2013-09-02 06:45:21.951 [2564:c07] imageLoaderDidFinishDownloading:1 
2013-09-02 06:45:21.958 [2564:c07] startDownload:2 
2013-09-02 06:45:21.959 [2564:c07] imageLoaderDidFinishDownloading:2 
2013-09-02 06:45:21.965 [2564:c07] startDownload:3 
2013-09-02 06:45:22.063 [2564:c07] imageLoaderDidFinishDownloading:3 
2013-09-02 06:45:22.072 [2564:c07] startDownload:4 
2013-09-02 06:45:22.073 [2564:c07] imageLoaderDidFinishDownloading:4 
2013-09-02 06:45:22.081 [2564:c07] startDownload:5 
2013-09-02 06:45:22.082 [2564:c07] imageLoaderDidFinishDownloading:5 
2013-09-02 06:45:22.089 [2564:c07] startDownload:6 
2013-09-02 06:45:22.090 [2564:c07] imageLoaderDidFinishDownloading:6 
2013-09-02 06:45:22.098 [2564:c07] startDownload:7 
2013-09-02 06:45:22.099 [2564:c07] imageLoaderDidFinishDownloading:7 
2013-09-02 06:45:22.104 [2564:c07] startDownload:8 
2013-09-02 06:45:22.163 [2564:c07] imageLoaderDidFinishDownloading:8 

Sau khi di chuyển đến mục 10-19:

2013-09-02 06:45:26.212 [2564:c07] loadImagesForOnscreenRows1:8 
2013-09-02 06:45:26.212 [2564:c07] loadImagesForOnscreenRows1:0 
2013-09-02 06:45:26.212 [2564:c07] loadImagesForOnscreenRows1:1 
2013-09-02 06:45:26.212 [2564:c07] loadImagesForOnscreenRows1:6 
2013-09-02 06:45:26.213 [2564:c07] loadImagesForOnscreenRows1:2 
2013-09-02 06:45:26.213 [2564:c07] loadImagesForOnscreenRows1:3 
2013-09-02 06:45:26.213 [2564:c07] loadImagesForOnscreenRows1:4 
2013-09-02 06:45:26.213 [2564:c07] loadImagesForOnscreenRows1:5 
2013-09-02 06:45:26.213 [2564:c07] loadImagesForOnscreenRows1:7 
2013-09-02 06:45:26.214 [2564:c07] loadImagesForOnscreenRows2:8 
2013-09-02 06:45:26.214 [2564:c07] loadImagesForOnscreenRows2:0 
2013-09-02 06:45:26.214 [2564:c07] loadImagesForOnscreenRows2:1 
2013-09-02 06:45:26.214 [2564:c07] loadImagesForOnscreenRows2:6 
2013-09-02 06:45:26.214 [2564:c07] loadImagesForOnscreenRows2:2 
2013-09-02 06:45:26.215 [2564:c07] loadImagesForOnscreenRows2:3 
2013-09-02 06:45:26.215 [2564:c07] loadImagesForOnscreenRows2:4 
2013-09-02 06:45:26.215 [2564:c07] loadImagesForOnscreenRows2:5 
2013-09-02 06:45:26.215 [2564:c07] loadImagesForOnscreenRows2:7 

Như bạn có thể thấy sau khi cuộn, các đường dẫn chỉ mục có thể nhìn thấy vẫn giữ nguyên. Có ai khác đã gặp phải điều này hoặc một ý tưởng cho một giải pháp? Hoặc tôi không hiểu một số nguyên tắc của collectionview sai?

Rất cám ơn trước! Trân trọng, Jan

+0

Tôi không có mục đích 'loadImagesForOnscreenRows'. Trong 'cellForRowAtIndexPath' bạn đặt hình ảnh nếu nó có sẵn. Nếu không, trong 'imageLoaderDidFinishDownloading' bạn có thể tải lại đường dẫn chỉ mục, nó sẽ gọi lại' cellForRowAtIndexPath' hoặc bạn có thể đặt trực tiếp hình ảnh trên ô nếu nó vẫn còn trên màn hình. Sẽ không bao gồm nó? –

+0

Xin chào Timothy, 'cellForRowAtIndexPath' tải các ô hiển thị trong khung nhìn, trong trường hợp của tôi là 9 ô đầu tiên. Khi tôi bắt đầu cuộn, các ô sắp tới sẽ không được điền vào, thay vào đó 'scrollViewDidEndDragging' được gọi, sẽ gọi' loadImagesForOnscreenRows', giống như trong bảng xem trước. Trong hàm này, tôi muốn lấy các đường dẫn chỉ mục, 'indexPathsForVisibleItems', được hiển thị và bắt đầu tải xuống các hình ảnh, một khi chúng được tải xuống, hãy tải lại các đường dẫn chỉ mục. Nhưng 'indexPathsForVisibleItems' luôn trả về chín đầu tiên. Hay tôi đang làm điều gì đó hoàn toàn sai. Với bảng nó hoạt động tốt theo cách này. –

+0

Các ô không có hình ảnh thường sẽ được điền riêng lẻ khi tải xuống cho ô đó hoàn tất, trong trường hợp của bạn sẽ xảy ra trong 'imageLoaderDidFinishDownloading'. Cho điều này, tôi không nhận được điểm của 'loadimageforOnscreenRows'. Nói cách khác, tại sao bạn không điền vào ô trong 'imageLoaderDidFinishDownloading'? –

Trả lời

5

Nếu bạn đang nhắm mục tiêu iOS 8.0 trở lên, bạn nên sử dụng collectionView:willDisplayCell:forItemAtIndexPath: để bắt đầu tải xuống. Nếu sử dụng iOS 7.0, bạn nên tiếp tục sử dụng collectionView:cellForItemAtIndexPath:.

Trong số gọi lại imageLoaderDidFinishDownloading:, bạn nên kiểm tra xem đường dẫn chỉ mục được đề cập có hiển thị hay không. Nếu có, hãy truy xuất ô tương ứng và cập nhật chế độ xem hình ảnh của ô đó. Nếu ô không hiển thị thì công việc của bạn đã hoàn thành. Gọi số -reloadData cho mỗi lần hoàn thành hình ảnh đang thực hiện rất nhiều công việc tốn kém và có thể có các vấn đề UX quan trọng nếu người dùng của bạn hiện đang cuộn giữa bảng và bạn đặt lại nội dung của nó. Bạn cũng có khả năng thực hiện công việc UIImageJPEGRepresentation() nhiều lần, nó sẽ giúp hiệu suất cuộn của bạn nếu bạn thực hiện công việc này một lần trong imageLoaderDidFinishDownloading: và sau đó lưu vào bộ nhớ cache.

Vì có vẻ như gọi lại xảy ra trên chuỗi nền đảm bảo bạn chỉ thao tác UICollectionView từ chuỗi chính.