关闭

iOS 开发中如何显示网络图片

标签: objciOS
2176人阅读 评论(0) 收藏 举报
分类:
              by Fanxiushu  2015-07-10 转载或引用请注明原作者

 iOS开发中,使用UIImageView控件来显示图片,非常简单几句话就能显示一个完整的图片:
UIImageView* img =[[UIImageView alloc] initWithFrame:frame];
[superView addSubView:img];
img.image=[UIImage imageNamed:@"picture.png"];
这是图片在本地的情况,可是如果图片文件在网络上该如何实现呢?
你可以使用 NSData* data = [NSData dataWithContentsOfURL:URL];
然后NSData数据转化到 UIImage,
但是 dataWithContentsOfURL是同步获取数据,如果图片文件太大,会阻塞主线程,造成界面假死。
当然也可以使用别人开发的库,比如 SDWebImage就能很好的下载和缓存网络图片。
可是如果花不了多少精力能自己实现这个功能,我就会尽力自己实现来满足某些项目的要求,
这样看起来简洁好维护,更重要的是通过自己实现更能掌握核心部分。
自己实现关键是要解决网络同步传输问题。
然后你也许又想到一堆别人开发的网络库,比如AFNetworking,ASIHTTPRequest等等,
其实这些都用不着,直接用苹果原生SDK API就可以了,使用他的 NSURLConnection,
至少iOS的自带的网络API,比起windows平台下的WININET要简洁不少。

设计一个 UIImageView的Category类别类,在这个类别类中增加一个方法,比如setImageWithUrl,
这个方法就是我们需要实现的从网络下载并且显示到 UIImageView控件的方法。

 要在这个方法中异步下载网络图片,并且要实时显示下载进度,因此得做个NSURLConnection代理类,管理下载进度。

 定义的接口如下:
 @interface ImageDownDelegate : NSObject
///
/////
@end
 
@interface UIImageView (URLImage)
 
@property(nonatomic,strong)ImageDownDelegate* callback;
-(void) setImageWithUrl:(NSString*)url imgName:(NSString*)imgName progress:(void(^)(int progress, NSError* error)) progress;
 
@end

 ImageDownDelegate 是负责数据下载的接口。
setImageWithUrl的progress,表示使用BLOCK函数块的方式显示下载进度。
参数imgName的意思占位图片,当网络图片正在下载的时候,
用一个本地图片来暂时代替展现。
 
ImageDownDelegate私有属性如下:
 
typedef void (^FUNC)(int progress,  NSError* error);         这个就是setImageWithUrl提供的下载进度回调函数
typedef void (^FUNC2)(NSData* data, NSError* error);     这个是完成回调函数
@interface ImageDownDelegate()
/////
@property(copy) FUNC func;
@property(copy) FUNC2 completion;
@property(nonatomic)UIImageView* imageView;   当下载完成后,设置这个图片控件的Image,让网络图片展示出来
@property(nonatomic)UIButton* imageButton;  这个是UIButton的情况下,下载完网络图片之后展示UIButton的背景。
@property(nonatomic,strong)NSMutableData* data; 这个是下载的图片数据内容,在didReceiveData 组合所有下载的数据。
@property(nonatomic) long long total_length; 图片数据的总长度,在 didReceiveResponse计算得知
@property(nonatomic) long long curr_length;当前下载的数据长度,在 didReceiveData 计算得知
 
@end
 
ImageDownDelegate 接口实现如下主要几个方法:
 
////这个方法是从网络获取 HTTP回答头信息,从这里可以知道下载的图片长度,因此记录下来,作为下载进度的依据。
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
/////真正的数据接收回调函数,在这里应该把接收到的数据组合起来,计算并且调用进度函数,通知下载进度。
 -(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
 
/////图片数据已经完全下载完成,在这里应该设置UIImageView的Image,这个时候,图片就可以展现出来。
 -(void)connectionDidFinishLoading:(NSURLConnection *)connection;
 
///////下载过程中出现错误,这里应该通知失败。
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
 
 
可以看到 UIImageView (URLImage) 里有个 callback属性, 但是类别类不允许有属性存在,因此采用
objc_setAssociatedObject, objc_getAssociatedObject函数来解决这个问题。
为何需要这个代理类的 callback呢?
因为假设某个UIImageView调用 setImageWithUrl下载某个网络图片,当这个网络图片正在下载过程中,
接着它又调用setImageWithUrl下载另一个网络图片,应该取消第一次的下载,这个callback就起到这个作用。
 
最后看看 setImageWithUrl的大致实现过程:
 
-(void) setImageWithUrl:(NSString*)url imgName:(NSString*)imgName progress:(void(^)(int progress, NSError* error)) progress
{
    ////
    if( self.callback){ ///如果之前有正在下载的数据,则需要移除,否则遇到重新请求网络图片的UIImageView可能出现混乱
        ////这里应该真正取消前一次的图片下载,但是我使用 NSURLProtocol协议缓存数据,所以这里让他继续后台下载并且缓存起来。
        self.callback.imageButton = nil;
        self.callback.imageView = nil;
        ////
     //   NSLog(@"######***** CCOOOOOOOO ");
    }
   
    ///////这里我使用 NSURLProtocol 协议来缓存所有图片数据,这里的意思先判断缓存是否存在这个URL定位的图片,
          如果存在则从缓存获取图片直接显示。
         
    NSString* path = [SimpleURLProtocolCache getCacheFile:url];
    if(path){
        self.image = [UIImage imageWithContentsOfFile:path];
        ////
        return ; //////
    }
    ///////是否显示占位图
    if( imgName ){
        ////
        self.image = [UIImage imageNamed:imgName]; ////
       
        ///////
    }
   
    ////
    NSMutableURLRequest* newRequest = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url]
                                              cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
                                          timeoutInterval:10.0];
   
    ///////
    ImageDownDelegate* cbk = [[ImageDownDelegate alloc] init];
   
    cbk.func = progress;
    cbk.completion = nil;
    cbk.imageView = self;
    cbk.imageButton = nil;
   
   
    self.callback = cbk;  /////////
   
    ////
    if(progress){
        ////
        progress(0, nil); ///
    }
    设置代理,开始图片的真正下载,下载后会同时被 NSURLProtocol 协议缓存。
    NSURLConnection* conn = [NSURLConnection connectionWithRequest:newRequest delegate:cbk];
   
    [conn start];
    ///////
   
}
 图片缓存,我使用扩展 NSURLProtocol协议来缓存所有 NSURL的数据,
你也可以自己实现缓存来缓存图片,相信自己实现这个缓存也不复杂的。


这个接口对应的资源下载地址:

http://download.csdn.net/detail/fanxiushu/8886897



0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:121338次
    • 积分:1854
    • 等级:
    • 排名:千里之外
    • 原创:54篇
    • 转载:0篇
    • 译文:0篇
    • 评论:283条
    文章分类
    最新评论