界面效果图

Snip20170518_3.png
plist文件
文字及图片的地址从plist文件中进行读取,plist文件如下图所示:

Snip20170518_2.png
实现思路流程图

Snip20170518_8.png
具体实现代码
1、根据结构,我们自定义一个数据模型文件:
//.h文件
#import <Foundation/Foundation.h>
@interface App : NSObject
//名字
@property(nonatomic,copy)NSString *name;
//图标
@property(nonatomic,copy)NSString *icon;
//下载量
@property(nonatomic,copy)NSString *download;
+(instancetype)appWithDict:(NSDictionary *)dict;
@end
//.m文件
#import "App.h"
@implementation App
+(instancetype)appWithDict:(NSDictionary *)dict
{
App *app = [[self alloc]init];
[app setValuesForKeysWithDictionary:dict];
return app;
}
@end
2、在视图控制器中的代码实现:
#import "ViewController.h"
#import "App.h"
@interface ViewController ()
/** 所有数据 */
@property(nonatomic,strong)NSArray *apps;
/** 内存缓存的图片 */
@property(nonatomic,strong)NSMutableDictionary *images;
/** 所有操作(用来存储下载图片的线程操作的字典,主要作用是防止重复下载) */
@property(nonatomic,strong)NSMutableDictionary *operations;
/** 队列对象 */
@property(nonatomic,strong)NSOperationQueue *queue;
@end
@implementation ViewController
#pragma mark 初始化方法
-(NSArray *)apps
{
if (!_apps) {
NSArray *dictArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"apps.plist" ofType:nil]];
NSMutableArray *apps = [NSMutableArray array];
for (NSDictionary *dict in dictArray) {
[apps addObject:[App appWithDict:dict]];
}
_apps = apps;
}
return _apps;
}
-(NSMutableDictionary *)images
{
if (!_images) {
_images = [NSMutableDictionary dictionary];
}
return _images;
}
-(NSMutableDictionary *)operations
{
if (!_operations) {
_operations = [NSMutableDictionary dictionary];
}
return _operations;
}
-(NSOperationQueue *)queue
{
if (!_queue) {
_queue = [[NSOperationQueue alloc]init];
//控制最大并发数为3
_queue.maxConcurrentOperationCount = 3;
}
return _queue;
}
#pragma mark-<UITableViewDataSource>
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.apps.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *ID = @"app";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
App *app = self.apps[indexPath.row];
cell.textLabel.text = app.name;
cell.detailTextLabel.text = app.download;
if (self.images[app.icon])// 如果内存中有图片,则使用内存中的图片
{
cell.imageView.image = self.images[app.icon];
}else{// 如果内存中没有图片,则看看沙河中是否有图片
// 获取文件的全路径
NSString *filePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask , YES)lastObject] stringByAppendingPathComponent:[app.icon lastPathComponent]];
// 加载沙盒的文件数据
NSData *data = [NSData dataWithContentsOfFile:filePath];
if (data) {// 如果数据存在则直接加载沙盒中图片
UIImage *image = [UIImage imageWithData:data];
cell.imageView.image = image;
// 把图片保存到内存缓存
self.images[app.icon] = image;
}else{ // 如果内存和沙河中都没有图片,则下载图片
NSOperation *operation = self.operations[app.icon];
if (!operation) {//检查该图片时候正在下载,如果不是则再添加下载任务
//subTitle类型的cell,默认imageView如果在加载cell的时候没有设置图片那就默认不存在,不会显示,所以得设置一个占位图片,在image加载完毕后可以有地方显示
cell.imageView.image = [UIImage imageNamed:@"placeholder"];//占位图片
//开一条线程进行图片下载操作
operation = [NSBlockOperation blockOperationWithBlock:^{
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:app.icon]];
//如果数据加载失败
if (data==nil) {
//移除操作
[self.operations removeObjectForKey:app.icon];
return ;
}
UIImage *image = [UIImage imageWithData:data];
//把图片保存到内存缓存
self.images[app.icon] = image;
//回到主线程显示图片
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
//会出现重复占位的问题
//cell.imageView.image = image;
//刷新一行数据,解决重复占位问题(因为图片已经保存到内存缓存中,则会去内存中加载图片)
[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}];
// 将图片文件数据写入沙盒中
[data writeToFile:filePath atomically:YES];
//移除图片的下载操作
[self.operations removeObjectForKey:app.icon];
}];
//添加到队列中
[self.queue addOperation:operation];
//添加到字典中
self.operations[app.icon] = operation;
}
}
}
return cell;
}
#pragma mark-内存警告处理
-(void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
//内存警告的处理
[self.queue cancelAllOperations];
self.operations = nil;
self.apps = nil;
}
@end
细节总结
- 下载操作在子线程进行,解决UI不流畅问题
- 将每一个下载任务与一个图片的url对应,通过判断图片是否正在下载,解决重复下载问题,
- 处理数据加载失败问题。
- 通过设置占位图片,解决因网络数据加载慢导致的图片不显示问题。
- 通过刷新一行数据来显示UI界面,解决重复占位(图片数据错乱)问题。