2

我想制作一个将图像显示到 UICollectionView 中的应用程序。

  • 图片将从服务器下载,然后显示到 collectionView 中。
  • 我在 xib 文件中使用自定义 collectionView 布局。
  • 一次,从服务器接收 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 应用程序,这可能是一个非常愚蠢的问题。

4

3 回答 3

2

使用异步fetch从服务器获取数据并在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

#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

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

在.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,将图像保存到模型以保留它

于 2013-10-24T05:52:24.907 回答
1

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

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

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

一种常见的设计模式是将提供给 collectionView 的信息存储在类变量中(它不一定是属性,但通常是)。你总是在那个变量中存储一些东西,即使它是旧的或陈旧的数据。

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

其他方法可以获取、检索和发送更新的数据,一旦它们完成,您reloadData:就调用 collectionView 来更新接口。

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

关于如何开始的建议是将获取数据的代码移动到单独的方法中,然后将数据暂存到一个或两个类变量中,collectionView 可以可靠地从中提取数据。

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

于 2013-10-24T05:17:35.467 回答
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;
}
于 2013-10-24T05:12:26.213 回答