ios:将新数据加载到UICollectionView中

时间:2013-10-24 04:59:44

标签: ios iphone objective-c uicollectionview

我想创建一个将图像显示到UICollectionView的应用程序。

  • 图像将从服务器下载,然后显示到collectionView中。
  • 我正在使用自定义collectionView布局到xib文件中。
  • 每次从服务器接收20张图片。

问题:我无法将新下载的图像显示到collectionView。

这是我的代码:

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

    BOOL reloaded = NO;

    static NSString *cellIdentifier = @"cvCell";

    CVCell *cell = (CVCell *)[collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];

    NSMutableArray *data = [self.dataArray objectAtIndex:indexPath.section];

    NSString *cellData = [data objectAtIndex:indexPath.row];

    dispatch_queue_t queue = dispatch_queue_create("com.justTest.anotherSingleApplication", NULL);
    dispatch_async(queue, ^{
        //code to be executed in the background
        NSString *imageName1 = [[NSString alloc]initWithFormat:@"http://www.abc.com/images/thumb/%@", cellData];
        NSString *url_Img1 = imageName1;
        UIImage *aImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:url_Img1]]];

        dispatch_async(dispatch_get_main_queue(), ^{
            //code to be executed on the main thread when background task is finished
            [cell.cellImage setImage:aImage];

        });
    });

    if (indexPath.row == self.imageArray.count - 1 && !reloaded) {

        getOnScrollImages *getImage = [[getOnScrollImages alloc] init]; // class to get image name from server
        NSMutableArray *astring = (NSMutableArray *)[getImage getImageNameFromServer:@"list" board:@"111" pin:@"122345"]; // method to get image name from server

        [self setNewTestArray:astring]; //adding newly downloaded image name into array

        reloaded = YES;

        dispatch_async(dispatch_get_main_queue(), ^{
            [self.collectionView reloadData];
        });
    }

    return cell;
}

有什么建议吗?

注意:我刚刚开始开发iOS应用程序,这可能是一个非常愚蠢的问题。

3 个答案:

答案 0 :(得分:2)

使用异步提取从服务器获取数据并将其显示在collectionView

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    YourDataModel *model = self.dataArray[indexPath.row];    
    YourCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];

    if ([self checkWhetherImageAlreadyExist]) {

        [cell.imageView setImage:model.image];

    } else {
        //show placeholder to avoid nothing in your UI, or your user gets confused
        [cell.imageView setImage:placeholderImage];
        [self startDownloadImageForIndexPath:indexPath];
    }
}

- (void)startDownloadImageForIndexPath:(NSIndexPath *)indexPath
{
    //YourImageDownloader is a class to fetch data from server
    //imageDownloadsInProgress is a NSMutableDictionary to record the download process, which can avoid repeat download
    YourImageDownloader *downloader = [self.imageDownloadsInProgress objectForKey:indexPath];

    if (downloader == nil) {
        YourDataModel *model = self.dataArray[indexPath.row];

        //configure downloader
        downloader = [[YourImageDownloader alloc] init];
        [downloader setURL:model.url];
        [downloader setCompletionHandler:^{
            //download the image to local, or you can pass the image to the block
            model.image = [UIImage imageWithContentsOfFile:model.localPath];
            YourCell *cell = [self.mCollectionView cellForItemAtIndexPath:indexPath];
            [cell.imageView setImage:model.image];

            //remove downloader from dictionary
            [self.imageDownloadsInProgress removeObjectForKey:indexPath];
        }];

        //add downloader to dictionary
        [self.imageDownloadsInProgress setObject:downloader forKey:indexPath];

        //start download
        [downloader startDownload];
    }
}

使用课程下载图像。如果您在一个集合视图中有许多图像,则可以考虑在发生内存警告时将这些图像保存到本地。如果现在很多,只需将图像保留在内存中并将其显示在集合视图中。

后面的代码是将图像保存到本地,并在显示时从本地读取图像数据。

在.h:

#import <Foundation/Foundation.h>

@interface PortraitDownloader : NSObject

@property (nonatomic, copy) NSString *portraitName;
@property (nonatomic, copy) void (^completionHandler)(void);

- (void)startDownload;
- (void)cancelDownload;

@end

in .m

#import "PortraitDownloader.h"
#import <CFNetwork/CFNetwork.h>
#import "NSString+ImagePath.h" // it's a category to get the image local path

@interface PortraitDownloader ()

@property (nonatomic, strong) NSMutableData *activeDownload;
@property (nonatomic, strong) NSURLConnection *portraitConnection;

@end

@implementation PortraitDownloader

- (void)startDownload
{
    self.activeDownload = [NSMutableData data];

    NSString *urlstr = [NSString serverPortraitPathWithPortrait:self.portraitName];
    NSURL *url = [NSURL URLWithString:urlstr];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];

    self.portraitConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}

- (void)cancelDownload
{
    [self.portraitConnection cancel];
    self.portraitConnection = nil;
    self.activeDownload = nil;
}

#pragma mark - NSURLConnectionDelegate

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    [self.activeDownload appendData:data];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    // Clear the activeDownload property to allow later attempts
    self.activeDownload = nil;

    // Release the connection now that it's finished
    self.portraitConnection = nil;
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    // save to local path
    NSString *localSavePath = [NSString localPortraitPathWithPortrait:self.portraitName];
    [self.activeDownload writeToFile:localSavePath atomically:YES];

    self.activeDownload = nil;

    // Release the connection now that it's finished
    self.portraitConnection = nil;

    // call our delegate and tell it that our icon is ready for display
    if (self.completionHandler) {
        self.completionHandler();
    }
}

@end

如果您想将图像保留在内存中,只需将完成块修改为:

in .h

typedef void (^Completion_handle) (UIImage *image);

@interface PortraitDownloader : NSObject

@property (nonatomic, copy) Completion_handle myCompletionBlock;

和.m

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    // get image from data
    UIImage *image = [UIImage imageWithData:self.activeDownload];

    self.activeDownload = nil;

    // Release the connection now that it's finished
    self.portraitConnection = nil;

    // call our delegate and tell it that our icon is ready for display
    if (self.myCompletionBlock) {
        self.myCompletionBlock(image);
    }
}

并修改方法startDownloadImageForIndexPath,将图像保存到模型中以保留它

答案 1 :(得分:1)

此方法希望立即得到答案:

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath

当您的代码响应速度不够快时,该应用通常不会显示任何内容,或者有时会崩溃(取决于您设置的内容)

一种常见的设计模式是将要提供给collectionView的信息存储在类变量中(它不必是属性,但通常是时候)。您始终将SOMETHING存储在该变量中,即使它是旧的或过时的数据。

然后你有了UICollectionViewDataSource协议中定义的方法,直接从类变量中提取他们需要的东西,没有延迟。

其他方法可以获取并检索和吊索更新的数据,一旦完成,您可以在collectionView上调用reloadData:来更新界面。

假设您正在使用的异步调用最终成功检索数据,它们可能对于UICollectionViewDataSource协议方法所期望的速度来说太慢了。

关于如何开始使用的建议是将代码提取到单独的方法,然后将数据分级到一个或多个类变量中,collectionView可以从中可靠地绘制数据。

如果需要,您可以先使用加载到包中的静态数据进行尝试,然后再从Web进行异步提取。

答案 2 :(得分:0)

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
 UICollectionViewCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:@"cellIdentifier" forIndexPath:indexPath];
 UIImageView *imgView=[[UIImageView alloc]initWithImage:[UIImage imageNamed:@"profile_pic.png"]];
 NSMutableDictionary *contactData=[NSMutableDictionary new];
 contactData = [self.collectionData objectAtIndex:indexPath.row];
 imgView.image=[contactData objectForKey:@"image"];
 [cell addSubview:imgView];
 return cell;
}