#import "ActivityListViewController.h"
#import "ActivityListViewCell.h"
#import "Activity.h"
@interface ActivityListViewController ()
// 存放activity的数组
@property (nonatomic,strong) NSMutableArray *modelArray;
@end
@implementation ActivityListViewController
// 请求网络数据
- (void)setNetWorkData
{
// 活动网址: http://project.lanou3g.com/teacher/yihuiyun/lanouproject/activitylist.php
// 1. URL
NSURL *url = [NSURL URLWithString:@"http://project.lanou3g.com/teacher/yihuiyun/lanouproject/activitylist.php"];
// 2. request
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
// 3. connection
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
// 4. 解析数据
NSDictionary *rootDic = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
// events
NSArray *array = [rootDic objectForKey:@"events"];
// 开辟空间
self.modelArray = [NSMutableArray array];
// kvc赋值
for (NSDictionary *dic in array) {
Activity *activity = [[Activity alloc] init];
[activity setValuesForKeysWithDictionary:dic];
[self.modelArray addObject:activity];
}
// 5. 刷新单元格数据
// 原因: 网络数据请求需要时间,因此 单元格创建会在数据请求完成之前执行。所以,我们在数据请求完成之后,再次给单元格赋值。
[self.tableView reloadData];
}];
}
- (void)viewDidLoad {
[super viewDidLoad];
// 调用请求网络数据
[self setNetWorkData];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
#warning Incomplete implementation, return the number of sections
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
#warning Incomplete implementation, return the number of rows
return [self.modelArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
ActivityListViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell1" forIndexPath:indexPath];
// Configure the cell...
Activity *activity = self.modelArray[indexPath.row];
// 查看activity中,是否已经下载图片,如果没有,则下载
if (activity.picture == nil && activity.isDownLoading == NO) {
[activity loadImage];
// 下载图片后,给activity添加观察者
// 1. 添加观察者
[activity addObserver:self forKeyPath:@"picture" options:(NSKeyValueObservingOptionNew) context:(__bridge void * _Nullable)(indexPath)];
// arc 需要 桥接,(__bridge void *)indexPath
// mrc 里需要进行retain [indexPath retain],防止野指针
}
// 此时存在问题,当单元格第一次加载的时候,没有图片,因为,第一次加载时可能图片没有下载下来,赋值时就没有值。 当再次加载单元格时才有数据。
// 原因: 数据请求下来后,没有被及时添加到单元格上。 数据有了,但是没有赋值。
// 解决方案:使用 KVO 观察者模式,观察 activity,观察activity 的pICTure属性, tableViewController 去观察
// 1. 被观察者 activity ,
// 2. 被观察属性 picture
// 3. 观察者 self (tableViewController)
// 4. 执行操作 当图片下载下来后,执行观察方法,我们把下载下来的图片赋值给单元格。
cell.activity = activity;
return cell;
}
// 2. 实现kvo方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
// 1. 获取改变的值--获取下载后的图片
UIImage *newImage = (UIImage *)[change objectForKey:@"new"];
// 2. 获取传递过来的数据 indexPath
// 注意: 也需要强制转换,如果是arc,需要桥接
NSIndexPath *indexPath = (__bridge NSIndexPath *)context;
// 如果下载好的单元格正在被显示,我们才把图片赋值给它,否则就没有意义。如果一个没有正在显示的单元格数据,(重用队列里面的单元格)就会出现,单元格加载出来时,有别的数据。类似于,拿了一个脏盘子给你。
// 3. 获取正在显示的单元格下标数组
NSArray *array = [self.tableView indexPathsForVisibleRows];
// 4. 判断indexPath 是否正在显示,即是否在数组中
if ([array containsObject:indexPath]) {
// 说明正在显示,把下载好的图片赋值给单元格
// 获取单元格
ActivityListViewCell *cell = (ActivityListViewCell *)[self.tableView cellForRowAtIndexPath:indexPath];
// 给单元格赋值
cell.pictureView.image = newImage;
}
// 三、 触发观察者方法
// ......
// 四、移除观察者
[object removeObserver:self forKeyPath:@"picture"];
// 补充: mrc 如果想要观察者一直观察,又不想出现内容泄露,那么,可以把移除写在 dealloc 里面。
}