标签:
在真实的开发中,网络接口返回的JSON或XML数据中,通常不直接包含图片,而是给出图片的URL,我们
需要根据图片URL获取图片数据。为了便于后续使用,可将图片下载封装到一个类(ImageDownloader)
里面,ImageDownloader允许外界指定URL,提供开始下载和取消下载功能,并提供delegate或block
将图片传递给外界
UITableView显示图片,中的Model类注意事项
1.除了包含必要的数据外,还要包含一个ImageDownloader对象。
2.包含一个image.
3.包含一个图片是否正在下载的BOOL值。(用于判断是否需要开始下载)
Cell是根据Model信息,做页面显示。
KVO:(Key-Value-Observer)键值观察者,是观察者设计模式的一种具体实现。
KVO触发机制:一个对象(观察者),检测另一对象(被观察者)的某属性是否发生变化,若被检测的属性
发生更改,会触发观察者的一个方法(方法名固定,类似代理方法)
KVO使用步骤:
1.注册观察者(为被观察者指定观察者以及被观察属性)
2.实现回调方法
3.触发回调方法(被观察属性发生更改)
4.移除观察者
KVO使用场景:MVC中M与C通信,M发生变化通知C。其中M是被观察者,C是观察者
KVO注意事项:观察者销毁之前,移除观察者,否则会出现程序异常(给已经销毁的对象发送消息)
1.创建TableViewHeader,创建网络连接
1 #define LanOu @"http://project.lanou3g.com/teacher/yihuiyun/lanouproject/activitylist.php"
2.在ViewController中创建tableView
1 //reason: ‘-[UIViewController tableView:numberOfRowsInSection:]: unrecognized selector sent to instance 0x10944ad90‘ 2 //这个错误可能原因1.没有添加根视图控制器-没有关联 2.没有添加tableView 3 @property (retain, nonatomic) IBOutlet UITableView *tableView;
3.引入ImageDownloader类
4.label中数据正常请求,主要看图片的传输过程
在Activity类中下载图片并将图片储存起来
1 //传递image只有两个步骤,用ImageDownloader中的便利构造器下载数据,然后用代理方法将值传给self.image 2 @interface Activity : NSObject<ImageDownloaderDelegate> 3 4 @property (nonatomic,copy) NSString *title; 5 @property (nonatomic,copy) NSString *address; 6 @property (nonatomic,copy) NSString *begin_time; 7 @property (nonatomic,copy) NSString *end_time; 8 @property (nonatomic,copy) NSString *imageUrl; 9 10 //创建实例变量image,实现image传递的功能 11 @property (nonatomic,retain) UIImage *image; 12 //创建图片是否正在下载属性, 13 @property (nonatomic,assign) BOOL isDownloading; 14 //创建图片下载方法 15 - (void)downloadImage; 16 @end 17 18 #import "Activity.h" 19 20 @implementation Activity 21 22 //1.图片下载过程 23 - (void)downloadImage { 24 // 在执行这个方法是对self.isDownloading赋值YES,表示图片正在下载 25 self.isDownloading = YES; 26 [ImageDownloader imageDownloaderWithURL:_imageUrl andDelegate:self]; 27 } 28 //2.图片下载完成,self.isDownloading的值有显示告诉外部图片是否下载完成的作用 29 //该方法为ImageDownloader的方法,代理方法,得到下载好的image 30 - (void)imageDownloader:(ImageDownloader *)downloader didFinishLoading:(UIImage *)image { 31 // 这个方法执行时,对self.isDownloading方法赋值NO,表示下载已经结束 32 // 将下载好的image赋给self.image,传向外部 33 self.image = image; 34 self.isDownloading = NO; 35 } 36 37 - (void)dealloc { 38 [_image release]; 39 [_title release]; 40 [_address release]; 41 [_imageUrl release]; 42 [_begin_time release]; 43 [_end_time release]; 44 [super dealloc]; 45 } 46 47 @end
在ViewController中实现图片的加载
1 //reason: ‘UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath 测试阶段都row和section都写0 2 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 3 4 ActivityCell *cell = [tableView dequeueReusableCellWithIdentifier:@"myCell" forIndexPath:indexPath]; 5 6 Activity *act = [self.actList objectAtIndex:indexPath.row]; 7 // act.image == nil,表示图片没有下载,act.isDownloading == NO,表示图片下载方法没有执行,如果图片为空并且图片下载方法没有执行,执行图片下载方法 8 if (act.image == nil && act.isDownloading == NO) { 9 // 先加载一个图片占位符 10 cell.newImageView.image = [UIImage imageNamed:@"fashi1.png"]; 11 // 执行图片下载方法,方法执行后act.isDownloading == YES 12 [act downloadImage]; 13 /*KVO-键值观察者 给act添加观察者,observer:观察者是 14 ViewController KeyPath:表示观察的属性是image 15 options:表示观察image的得到的值.New表示方法中 16 change得到的值为新值 .old表示change得到的值为旧值 17 context传值功能,把cell的indextPath传给观察者方法中 18 */ 19 // 图片下载是由act完成的,所以观察act 20 [act addObserver:self forKeyPath:@"image" options:NSKeyValueObservingOptionNew context:[indexPath retain]]; 21 22 }else { 23 // 如果图片不为空或正在下载,就直接传值 24 cell.newImageView.image = act.image; 25 } 26 27 cell.activity = act; 28 return cell; 29 } 30 31 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 32 // 1.获取下载图片 33 // 根据options:中的枚举值来决定取change中的值 34 // UIImage *image = [change objectForKey:@"new"]; 35 // 两个是等同的,都可以 36 // change字典{kind = 1,new = ?}; 37 UIImage *image = change[NSKeyValueChangeNewKey]; 38 39 // 2.判断图片是否为空,如果为空直接返回,不再进行图片加载 40 if (image == nil) { 41 return; 42 } 43 // 3.获取对应cell的indexPath indexPath是context直接传值过来的 44 NSIndexPath *indexPath = (NSIndexPath *)context; 45 // 4.获取tableView上正在显示的cell的indexPath 46 NSArray *arr = self.tableView.indexPathsForVisibleRows; 47 // 5.判断可见indexPaths是否包含indexPath 48 BOOL contain = [arr containsObject:indexPath]; 49 // 如果包含,添加图片 50 if (contain) { 51 ActivityCell *cell = (ActivityCell *)[self.tableView cellForRowAtIndexPath:indexPath]; 52 cell.newImageView.image = image; 53 } 54 // 6.刷新对应的cell上的数据,对指定cell刷新 55 // 不刷新也可以显示,为了让图片适应cell,对图片进行调试,写上 56 [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; 57 // 7.移除观察者 58 // 从self.actList中获取被观察者 59 Activity *act = [self.actList objectAtIndex:indexPath.row]; 60 // 将观察者从被观察者中移除 61 [act removeObserver:self forKeyPath:@"image"]; 62 // 8.释放indexPath 63 [indexPath release]; 64 }
通过KVO添加图片
1>对被观察者添加观察者:如果图片为空且没有加载则开始加载,先给每个图片加一个占位符,对act添加观察者。
2>实现观察者方法:change为字典{kind = 1,new = "image"}取出里边的new值来取出下载下来的image
如果image为空,则直接返回,如果不为空则继续。
context传递的作用,将indexPath从创建观察者哪里直接传过来,获取这个cell的indexPath,
获取显示的所有的cells,判断在这个indexPath中的cell是否在显示的cells中,如果在则从
self.tabelView中获取此cell,然后对里边图片赋值。
添加以后刷新显示的cells,在self.actList中通过indexPath获取被观察的act,从act中将观察者
移除,在context传值indexPath时,对indexPath进行了retain,防止中途崩溃,最后release。
标签:
原文地址:http://www.cnblogs.com/AnchoriteFiliGod/p/4510099.html