1. 程式人生 > >iOS 開發中如何顯示網路圖片

iOS 開發中如何顯示網路圖片

              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的資料, 你也可以自己實現快取來快取圖片,相信自己實現這個快取也不復雜的。

這個介面對應的資源下載地址: