随意细解:UI -- KVO、图片异步下载

KVO

键值观察者
观察model中的某一个属性的值的变化。如果属性的值发生了变化,会触发一个方法。

明确:

  • 观察者 (控制器中触发一个方法 – 改变视图的颜色)

  • 被观察者 (被观察的对象)

  • 观察的属性 (对象中的属性)

控制器作为观察者去观察model(被观察者)中的一个属性值的变化 ,会触发一个方法(改变视图的)。

步骤

  1. 添加一个观察者

    Observer 填观察者 self
    NSKeyValueObservingOptionNew 新值
    NSKeyValueObservingOptionOld 旧值

    [被观察者 addObserver:self forKeyPath:@"属性名" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:@"携带的参数"];
  2. 键值观察 值发生变化 触发的方法

    object :被观察的对象
    change :值的变化

    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;
  3. 在触发方法中移除观察者

    [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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值