KVO
键值观察者
观察model中的某一个属性的值的变化。如果属性的值发生了变化,会触发一个方法。
明确:
观察者 (控制器中触发一个方法 – 改变视图的颜色)
被观察者 (被观察的对象)
观察的属性 (对象中的属性)
控制器作为观察者去观察model(被观察者)中的一个属性值的变化 ,会触发一个方法(改变视图的)。
步骤
添加一个观察者
Observer 填观察者 self
NSKeyValueObservingOptionNew 新值
NSKeyValueObservingOptionOld 旧值[被观察者 addObserver:self forKeyPath:@"属性名" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:@"携带的参数"];
键值观察 值发生变化 触发的方法
object :被观察的对象
change :值的变化- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;
在触发方法中移除观察者
[object removeObserver:self forKeyPath:keyPath];
异步加载图片
封装可以异步加载图片的类ImageDownLoader
ImageDownLoader.h
// 创建一个协议
@protocol ImageDownLoaderDelegate <NSObject>
// 请求成功
- (void)imageDownLoaderSucceedWithData:(NSMutableData *)data;
// 请求失败
- (void)imageDownLoaderFailedWithError:(NSError *)error;
@end
/*
本类可以异步加载图片
*/
@interface ImageDownLoader : NSObject <NSURLConnectionDataDelegate, NSURLConnectionDelegate>
// 连接
@property (nonatomic, retain) NSURLConnection *conn;
// data
@property (nonatomic, retain) NSMutableData *data;
// 初始化方法(需要写 请求中 可能用到的参数 -- 网址)
- (instancetype)initWithURL:(NSString *)url Delegate:(id<ImageDownLoaderDelegate>)delegate;
// 开始方法
- (void)ImageDownLoaderStart;
// 终止方法
- (void)ImageDownLoadercancel;
// 声明代理对象
@property (nonatomic, assign) id<ImageDownLoaderDelegate> delegate;
ImageDownLoader.m
- (void)dealloc
{
[_conn release];
[_data release];
[super dealloc];
}
- (instancetype)initWithURL:(NSString *)url Delegate:(id<ImageDownLoaderDelegate>)delegate
{
self = [super init];
if (self) {
NSURL *MyUrl = [NSURL URLWithString:url];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:MyUrl cachePolicy:(NSURLRequestUseProtocolCachePolicy) timeoutInterval:10];
[request setHTTPMethod:@"Get"];
self.conn = [NSURLConnection connectionWithRequest:request delegate:self];
// 设置代理
self.delegate = delegate;
}
return self;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
self.data = [NSMutableData data];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.data appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if (_delegate != nil && [_delegate respondsToSelector:@selector(imageDownLoaderSucceedWithData:)]) {
[_delegate imageDownLoaderSucceedWithData:self.data];
}
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
if (_delegate != nil && [_delegate respondsToSelector:@selector(imageDownLoaderFailedWithError:)]) {
[_delegate imageDownLoaderFailedWithError:error];
}
}
// 开始方法
- (void)ImageDownLoaderStart
{
[self.conn start];
}
// 终止方法
- (void)ImageDownLoadercancel
{
[self.conn cancel];
}
TableView使用异步加载图片(类似第三方框架SDWebImage)
ActivityModel.h
#import <Foundation/Foundation.h>
// 如果使用UI开头的控件,必须要引入UIKit框架
#import <UIKit/UIKit.h>
#import "ImageDownLoader.h"
@interface ActivityModel : NSObject
/*
title : 活动标题
begin_time : 开始时间
end_time :结束时间
address :地址
category_name:活动类型
participant_count:参加人数
wisher_count:感兴趣人数
image:活动图像(先显示占位图像)
name:活动举办方
content: 活动介绍
*/
@property (nonatomic, retain) NSString *title;
@property (nonatomic, retain) NSString *begin_time;
@property (nonatomic, retain) NSString *end_time;
@property (nonatomic, retain) NSString *address;
@property (nonatomic, retain) NSString *category_name;
@property (nonatomic, retain) NSNumber *participant_count;
@property (nonatomic, retain) NSNumber *wisher_count;
@property (nonatomic, retain) NSString *image;
@property (nonatomic, retain) NSString *name;
@property (nonatomic, retain) NSString *content;
// 请求图片
@property (nonatomic, retain) ImageDownLoader *imageDownLoader;
// 声明一个请求图片的方法
- (void)downLoaderImage;
// 显示的图片
@property (nonatomic, retain) UIImage *coverimage;
// 判断是否正在加载当中
@property (nonatomic, assign) BOOL isDownLoading;
@end
ActivityModel.m
@interface ActivityModel () <ImageDownLoaderDelegate>
@end
@implementation ActivityModel
- (void)setValue:(id)value forUndefinedKey:(NSString *)key
{
if ([key isEqualToString:@"owner"]) {
self.name = value[@"name"];
}
}
// 声明一个请求图片的方法
- (void)downLoaderImage
{
// 初始化请求对象
self.imageDownLoader = [[ImageDownLoader alloc]initWithURL:self.image Delegate:self];
// 请求开始
[self.imageDownLoader ImageDownLoaderStart];
// 更改图片的加载状态
self.isDownLoading = YES;
}
- (void)imageDownLoaderSucceedWithData:(NSMutableData *)data
{
// 加载完数据,说明异步请求完成了
self.coverimage = [UIImage imageWithData:data];
// 赋值成功,值从无到有
//更改图片的加载状态,加载完成 改为NO
self.isDownLoading = NO;
}
- (void)imageDownLoaderFailedWithError:(NSError *)error
{
NSLog(@"%@", error);
//更改图片的加载状态,加载失败 改为NO
self.isDownLoading = NO;
}
- (void)dealloc
{
[_title release];
[_begin_time release];
[_end_time release];
[_address release];
[_category_name release];
[_participant_count release];
[_wisher_count release];
[_image release];
[_name release];
[_content release];
[super dealloc];
}
@end
ActivityListCell.h
#import "ActivityModel.h"
@interface ActivityListCell : UITableViewCell
@property (nonatomic,retain) UIImageView * activityImageView;
@property (nonatomic,retain) UILabel *titleLabel;
@property (nonatomic,retain) UILabel *timeLabel;
@property (nonatomic,retain) UILabel *addressLabel;
@property (nonatomic,retain) UILabel *categoryLabel;
@property (nonatomic,retain) UILabel *wishLabel;
@property (nonatomic,retain) UILabel *participantLabel;
@property (nonatomic,retain) ActivityModel * activityModel;
ActivityListCell.m
#import "UIView+WLFrame.h"
#define kLeftMangin 10
#define kCellBackgroundViewHeight 140
#define kIconHeightAndWitdh 16
@interface ActivityListCell ()
@end
@implementation ActivityListCell
- (void)dealloc
{
[_activityModel release];
[_titleLabel release];
[_timeLabel release];
[_addressLabel release];
[_categoryLabel release];
[_wishLabel release];
[_participantLabel release];
[_activityImageView release];
[super dealloc];
}
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
[self addSubviews];
}
return self;
}
//布局子视图
- (void)addSubviews
{
//蓝色背景图
UIImageView * cellBackgroundView = [[UIImageView alloc] initWithFrame:CGRectMake(kLeftMangin, kLeftMangin, kScreenWidth - 16, kCellBackgroundViewHeight)];
cellBackgroundView.image = [UIImage imageNamed:@"bg_eventlistcell"];
[self.contentView addSubview:cellBackgroundView];
[cellBackgroundView release];
//活动标题
self.titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(kLeftMangin, 5, 280, kIconHeightAndWitdh)];
self.titleLabel.font = [UIFont systemFontOfSize:14.0];
[cellBackgroundView addSubview:self.titleLabel];
self.titleLabel.text = @"罗马与巴洛克艺术";
[_titleLabel release];
//信息背景图
UIImageView * infoBackgroundView = [[UIImageView alloc] initWithFrame:CGRectMake(5, _titleLabel.bottom + 5, cellBackgroundView.width - 5*2, kCellBackgroundViewHeight - _titleLabel.bottom - 10)];
//infoBackgroundView.backgroundColor = [UIColor redColor];
infoBackgroundView.image = [UIImage imageNamed:@"bg_share_large"];
[cellBackgroundView addSubview:infoBackgroundView];
[infoBackgroundView release];
//时间
UIImageView * timeImageView = [[UIImageView alloc] initWithFrame:CGRectMake(infoBackgroundView.left, 5, kIconHeightAndWitdh, kIconHeightAndWitdh)];
timeImageView.image = [UIImage imageNamed:@"icon_date"];
[infoBackgroundView addSubview:timeImageView];
[timeImageView release];
self.timeLabel = [[UILabel alloc] initWithFrame:CGRectMake(timeImageView.right + 5, timeImageView.top, 192, kIconHeightAndWitdh)];
self.timeLabel.font = [UIFont systemFontOfSize:12.0];
[infoBackgroundView addSubview:self.timeLabel];
self.timeLabel.text = @"07-22 09:00 -- 10-16 17:00";
[_timeLabel release];
//地址
UIImageView * addressImageView = [[UIImageView alloc] initWithFrame:CGRectMake(infoBackgroundView.left, timeImageView.bottom, kIconHeightAndWitdh, kIconHeightAndWitdh)];
addressImageView.image = [UIImage imageNamed:@"icon_spot"];
[infoBackgroundView addSubview:addressImageView];
[addressImageView release];
self.addressLabel = [[UILabel alloc] initWithFrame:CGRectMake(addressImageView.right + 5, timeImageView.bottom, _timeLabel.width, kIconHeightAndWitdh)];
self.addressLabel.font = [UIFont systemFontOfSize:12.0];
[infoBackgroundView addSubview:self.addressLabel];
self.addressLabel.text = @"北京 东城区 东长安街";
[_addressLabel release];
//类型
UIImageView * categoryImageView = [[UIImageView alloc] initWithFrame:CGRectMake(infoBackgroundView.left, addressImageView.bottom, kIconHeightAndWitdh, kIconHeightAndWitdh)];
categoryImageView.image = [UIImage imageNamed:@"icon_catalog"];
[infoBackgroundView addSubview:categoryImageView];
[categoryImageView release];
self.categoryLabel = [[UILabel alloc] initWithFrame:CGRectMake(categoryImageView.right + 5, addressImageView.bottom, _timeLabel.width, kIconHeightAndWitdh)];
self.categoryLabel.font = [UIFont systemFontOfSize:12.0];
[infoBackgroundView addSubview:self.categoryLabel];
self.categoryLabel.text = @"类型:展览";
[_categoryLabel release];
//感兴趣
UILabel * wish = [[UILabel alloc] initWithFrame:CGRectMake(12, 80, 40, kIconHeightAndWitdh)];
wish.font = [UIFont systemFontOfSize:10.0];
[infoBackgroundView addSubview:wish];
wish.text = @"感兴趣:";
[wish release];
self.wishLabel = [[UILabel alloc] initWithFrame:CGRectMake(52, 80, 35, kIconHeightAndWitdh)];
self.wishLabel.font = [UIFont systemFontOfSize:12.0];
self.wishLabel.textColor = [UIColor redColor];
[infoBackgroundView addSubview:self.wishLabel];
self.wishLabel.text = @"1968";
[_wishLabel release];
//参加
UILabel * participant = [[UILabel alloc] initWithFrame:CGRectMake(94, 80, 40, kIconHeightAndWitdh)];
participant.font = [UIFont systemFontOfSize:10.0];
[infoBackgroundView addSubview:participant];
participant.text = @"参加:";
[participant release];
self.participantLabel = [[UILabel alloc] initWithFrame:CGRectMake(124, 80, 35, kIconHeightAndWitdh)];
self.participantLabel.font = [UIFont systemFontOfSize:12.0];
self.participantLabel.textColor = [UIColor redColor];
[infoBackgroundView addSubview:self.participantLabel];
self.participantLabel.text = @"1638";
[_participantLabel release];
//活动图片
self.activityImageView = [[UIImageView alloc] initWithFrame:CGRectMake(kScreenWidth - 100, _timeLabel.top, 66, infoBackgroundView.height - 5*2)];
[infoBackgroundView addSubview:self.activityImageView];
[_activityImageView release];
}
- (void)setActivityModel:(ActivityModel *)activityModel
{
if (_activityModel != activityModel) {
[_activityModel release];
_activityModel = [activityModel retain];
}
//标题
_titleLabel.text = _activityModel.title;
//时间
NSString * startTime = [_activityModel.begin_time substringWithRange:NSMakeRange(5, 11)];
NSString * endTime = [_activityModel.end_time substringWithRange:NSMakeRange(5, 11)];
_timeLabel.text = [NSString stringWithFormat:@"%@ -- %@",startTime,endTime];
//地址
_addressLabel.text = _activityModel.address;
//类型
_categoryLabel.text = [NSString stringWithFormat:@"类型:%@",_activityModel.category_name];
//感兴趣人数
_wishLabel.text = [NSString stringWithFormat:@"%@",_activityModel.wisher_count];
//参与人数
_participantLabel.text = [NSString stringWithFormat:@"%@",_activityModel.participant_count];
}
RootViewController.m
#import "ActivityModel.h"
#import "ActivityListCell.h"
#define ActivityListAPI @"http://project.lanou3g.com/teacher/yihuiyun/lanouproject/activitylist.php"
@interface RootViewController () <UITableViewDataSource, UITableViewDelegate>
@property (nonatomic, retain) NSMutableArray *dataArray;
@property (nonatomic, retain) UITableView *tableView;
@end
@implementation RootViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self setUpdata];
[self addSubView];
}
- (void)setUpdata
{
NSURL *url = [NSURL URLWithString:ActivityListAPI];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:(NSURLRequestUseProtocolCachePolicy) timeoutInterval:10];
[request setHTTPMethod:@"Get"];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
// data数据解析
NSMutableDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:(NSJSONReadingMutableContainers) error:nil];
// NSLog(@"%@", dic);
// 取出展示的数据(数组)
NSArray *dataArr = dic[@"events"];
self.dataArray = [NSMutableArray array];
for (NSDictionary *OneDic in dataArr) {
ActivityModel *model = [[ActivityModel alloc]init];
[model setValuesForKeysWithDictionary:OneDic];
[self.dataArray addObject:model];
[model release];
}
// NSLog(@"%@", self.dataArray);
[self.tableView reloadData];
}];
}
- (void)addSubView
{
self.tableView = [[UITableView alloc]initWithFrame:[UIScreen mainScreen].bounds style:(UITableViewStylePlain)];
self.tableView.delegate = self;
self.tableView.dataSource = self;
[self.view addSubview:self.tableView];
[self.tableView release];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.dataArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = @"MyCell";
ActivityListCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[[ActivityListCell alloc]initWithStyle:(UITableViewCellStyleSubtitle) reuseIdentifier:identifier] autorelease];
}
// 显示数据
ActivityModel *model = self.dataArray[indexPath.row];
// cell内部赋值
cell.activityModel = model;
// 加载占位图
cell.activityImageView.image = [UIImage imageNamed:@"picholder"];
// 两种情况
// 不是正在加载时:
// 1.图片还没有请求
// 2.图片已经加载完成
if (model.coverimage == nil && model.isDownLoading == NO) {
// 请求图片
// 异步加载图片的方法
[model downLoaderImage];
// 添加观察者
// 为了把图片显示到对应的cell上,需要把indexPath索引传递过去
[model addObserver:self forKeyPath:@"coverimage" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:indexPath];
}else{
// 只要走到这里就说明model的coverimage已经有值了 直接进行重新赋值就可以了
cell.activityImageView.image = model.coverimage;
}
return cell;
}
// 键值观察的方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
// 一旦图片加载出来,就会触发这个方法
// 把加载出来的图片显示到对应的cell上
// 有了indexPath就可以把对应的cell取出来,取出cell,就可以让cell显示图片
// 取出cell
// 转化context类型
NSIndexPath *indexPath = (NSIndexPath *)context;
ActivityListCell *cell = (ActivityListCell *)[self.tableView cellForRowAtIndexPath:indexPath];
UIImage *newImage = change[@"new"];
// 显示到cell上
cell.activityImageView.image = newImage;
// 移除观察者
[object removeObserver:self forKeyPath:keyPath];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 150;
}