1. 程式人生 > >IOS開發之非同步載入網路圖片並快取本地實現瀑布流(一)

IOS開發之非同步載入網路圖片並快取本地實現瀑布流(一)

</pre><pre name="code" class="objc">
</pre><pre name="code" class="objc">
在前面的一篇部落格中,我寫了一個瀑布流照片牆的程式,由於之前的程式載入的圖片是本地的,所以在這篇文章中我來補上有關非同步載入網路圖片的程式碼,來實現之前程式的效果,希望大家批評指正呀!

這個程式中大部分的程式碼和之前的部落格中貼出來的相同,不同的只是添加了圖片快取機制,圖片非同步下載執行緒函式,以及圖片點選瀏覽的功能。

接下來看一下程式碼實現部分:

#import "MyScrollView.h"

#define COORDINATE_X_LEFT 5
#define COORDINATE_X_MIDDLE MY_WIDTH/3 + 5
#define COORDINATE_X_RIGHT MY_WIDTH/3 * 2 + 5
#define PAGESIZE 21

@interface MyScrollView ()

@end

@implementation MyScrollView

@synthesize isOnce = _isOnce;
@synthesize imagesName = _imagesName;
@synthesize loadedImageDic = _loadedImageDic;
@synthesize leftColumHeight = _leftColumHeight;
@synthesize midColumHeight = _midColumHeight;
@synthesize rightColumHeight = _rightColumHeight;
@synthesize loadedImageArray = _loadedImageArray;
@synthesize imgTag = _imgTag;
@synthesize imgTagDic = _imgTagDic;
@synthesize imageLoad = _imageLoad;
@synthesize page = _page;
@synthesize fileUtil = _fileUtil;
@synthesize imageCache = _imageCache;
@synthesize photoArray = _photoArray;
//@synthesize aDelegaet;

+ (MyScrollView *)shareInstance{
    static MyScrollView *instance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] initWithFrame:CGRectMake(0, 0, MY_WIDTH, MY_HEIGHT)];
        });
    
    return instance;
}

/*
 初始化scrollView的委託以及背景顏色,不顯示它的水平,垂直顯示條
 */
- (id)initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if(self){
        self.delegate = self;
        self.backgroundColor = [UIColor blackColor];
        self.pagingEnabled = NO;
        self.showsHorizontalScrollIndicator = NO;
        self.showsVerticalScrollIndicator = NO;
        
        self.isOnce = YES;
        self.loadedImageDic = [[NSMutableDictionary alloc] init];
        self.loadedImageArray = [[NSMutableArray alloc] init];
        self.imgTagDic = [[NSMutableDictionary alloc] init];
        self.photoArray = [[NSMutableArray alloc] init];

        //初始化列的高度
        self.leftColumHeight = 3.0f;
        self.midColumHeight = 3.0f;
        self.rightColumHeight = 3.0f;
        self.imgTag = 10086;
        self.page = 1;
        
        self.fileUtil = [FileUtil shareInstance];
        self.imageCache = [ImageCacher shareInstance];
        
        _imageCache.myDelegate = self;
        
        [self initWithPhotoBox];
    }
    
    return self;
}

/*
 將scrollView介面分為大小相等的3個部分,每個部分為一個UIView, 並設定每一個UIView的tag
 */
- (void)initWithPhotoBox{
    UIView *leftView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, MY_WIDTH/3, self.frame.size.height)];
    UIView *middleView = [[UIView alloc] initWithFrame:CGRectMake(leftView.frame.origin.x + MY_WIDTH/3, 0, MY_WIDTH/3,
                                                                  self.frame.size.height)];
    UIView *rightView = [[UIView alloc] initWithFrame:CGRectMake(middleView.frame.origin.x + MY_WIDTH/3, 0, MY_WIDTH/3,
                                                                 self.frame.size.height)];
    //設定三個部分的tag
    leftView.tag = 100;
    middleView.tag = 101;
    rightView.tag = 102;
    
    //設定背景顏色
    [leftView setBackgroundColor:[UIColor clearColor]];
    [middleView setBackgroundColor:[UIColor clearColor]];
    [rightView setBackgroundColor:[UIColor clearColor]];
    
    [self addSubview:leftView];
    [self addSubview:middleView];
    [self addSubview:rightView];
    
    self.imageLoad = [ImageLoader shareInstance];
    [_imageLoad loadImage:nil];
    
    //第一次載入圖片
    for(int i = 0; i < PAGESIZE; i++){
        NSString *imageName = [_imageLoad.imagesArray objectAtIndex:i];
        [self imageStartLoading:imageName];
    }
    
    //當前為第一頁
    self.page = 1;
}

/*
 * @brief 圖片載入通用函式
 * @parma imageName 圖片名
 */
- (void)imageStartLoading:(NSString *)imageName{
    NSURL *url = [NSURL URLWithString:imageName];
    if([_fileUtil hasCachedImage:url]){
        UIImageView *imageView = [[UIImageView alloc] init];
        NSString *path = [_fileUtil pathForUrl:url];
        imageView = [_imageLoad compressImage:MY_WIDTH/3 imageView:nil imageName:path flag:NO];
        [self addImage:imageView name:path];
        [self adjustContentSize:NO];
    }else{
        UIImageView *imageView = [[UIImageView alloc] init];
        NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:url, @"URL",
                             imageView, @"imageView", nil];
        [NSThread detachNewThreadSelector:@selector(cacheImage:) toTarget:[ImageCacher shareInstance] withObject:dic];
    }
}


/*
 *調整scrollview
 */
- (void)adjustContentSize:(BOOL)isEnd{
    UIView *leftView = [self viewWithTag:100];
    UIView *middleView = [self viewWithTag:101];
    UIView *rightView = [self viewWithTag:102];
    
    if(_leftColumHeight >= _midColumHeight && _leftColumHeight >= _rightColumHeight){
        self.contentSize = leftView.frame.size;
    }else{
        if(_midColumHeight >= _rightColumHeight){
            self.contentSize = middleView.frame.size;
        }else{
            self.contentSize = rightView.frame.size;
        }
    }
}

/*
 *得到最短列的高度
 */
- (float)getTheShortColum{
    if(_leftColumHeight <= _midColumHeight && _leftColumHeight <= _rightColumHeight){
        return _leftColumHeight;
    }else{
        if(_midColumHeight <= _rightColumHeight){
            return _midColumHeight;
        }else{
            return _rightColumHeight;
        }
    }
}

/*
 *新增一張圖片
 *規則:根據每一列的高度來決定,優先載入列高度最短的那列
 *重新設定圖片的x,y座標
 *imageView:圖片檢視
 *imageName:圖片名
 */
- (void)addImage:(UIImageView *)imageView name:(NSString *)imageName{
    //圖片是否載入
    if([self.loadedImageDic objectForKey:imageName]){
        return;
    }
    
    //若圖片還未載入則儲存
    [self.loadedImageDic setObject:imageView forKey:imageName];
    [self.loadedImageArray addObject:imageView];
    [_photoArray addObject:imageName];
    
    [self imageTagWithAction:imageView name:imageName];
    
    float width = imageView.frame.size.width;
    float height = imageView.frame.size.height;
    
    //判斷哪一列的高度最低
    if(_leftColumHeight <= _midColumHeight && _leftColumHeight <= _rightColumHeight){
        UIView *leftView = [self viewWithTag:100];
        [leftView addSubview:imageView];
        //重新設定座標
        [imageView setFrame:CGRectMake(2, _leftColumHeight, width, height)];
        _leftColumHeight = _leftColumHeight + height + 3;
        [leftView setFrame:CGRectMake(0, 0, MY_WIDTH/3, _leftColumHeight)];
    }else{
        if(_midColumHeight <= _rightColumHeight){
            UIView *middleView = [self viewWithTag:101];
            [middleView addSubview:imageView];

            [imageView setFrame:CGRectMake(2, _midColumHeight, width, height)];
            _midColumHeight = _midColumHeight + height + 3;
            [middleView setFrame:CGRectMake(MY_WIDTH/3, 0, MY_WIDTH/3, _midColumHeight)];
        }else{
            UIView *rightView = [self viewWithTag:102];
            [rightView addSubview:imageView];

            [imageView setFrame:CGRectMake(2, _rightColumHeight, width, height)];
            _rightColumHeight = _rightColumHeight + height + 3;
            [rightView setFrame:CGRectMake(2 * MY_WIDTH/3, 0, MY_WIDTH/3, _rightColumHeight)];
        }
    }
}


/*
 將圖片tag儲存,以及為UIImageView新增事件響應
 */
- (void)imageTagWithAction:(UIImageView *)imageView name:(NSString *)imageName{
    //將要顯示圖片的tag儲存
    imageView.tag = self.imgTag;
    [self.imgTagDic setObject:imageName forKey:[NSString stringWithFormat:@"%ld", (long)imageView.tag]];
    self.imgTag++;
    
    //圖片新增事件響應
    UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(imageClickWithTag:)];
    tapRecognizer.delegate = self;
    imageView.userInteractionEnabled = YES;
    [imageView addGestureRecognizer:tapRecognizer];
    [tapRecognizer release];
}


/*
     //若三列中最短列距離底部高度超過30畫素,則請求載入新的圖片
 */
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    //可視檢查
    //[self checkImageIsVisible];
    if((self.contentOffset.y + self.frame.size.height) - [self getTheShortColum] > 30){
        [self pullRefreshImages];
    }
}


/*
 上拉時載入新的圖片
 */
- (void)pullRefreshImages{
    int index = self.page *PAGESIZE;
    NSUInteger imgNum = [self.imageLoad.imagesArray count];
    
    if(index >= imgNum){
        //圖片載入完畢
        [self adjustContentSize:YES];
    }else{
        if((imgNum - self.page*PAGESIZE) > PAGESIZE){
            for (int i = index; i < PAGESIZE; i++) {
                NSString *imageName = [_imageLoad.imagesArray objectAtIndex:i];
                [self imageStartLoading:imageName];
            }
        }else{
            for (int i = index; i < imgNum; i++) {
                NSString *imageName = [_imageLoad.imagesArray objectAtIndex:i];
                [self imageStartLoading:imageName];
            }
        }
        self.page++;
    }
}

/*
 檢查圖片是否可見,如果不在可見視線內,則把圖片替換為nil
 */
- (void)checkImageIsVisible{
    for (int i = 0; i < [_loadedImageArray count]; i++) {
        UIImageView *imgView = [_loadedImageArray objectAtIndex:i];
        
        if((self.contentOffset.y - imgView.frame.origin.y) > imgView.frame.size.height ||
           imgView.frame.origin.y > (self.frame.size.height + self.contentOffset.y)){
            //不顯示圖片
            imgView.image = nil;
        }else{
            //重新根據tag值顯示圖片
            NSString *imageName = [self.imgTagDic objectForKey:[NSString stringWithFormat:@"%ld", (long)imgView.tag]];
            if((NSNull *)imageName == [NSNull null]){
                return;
            }
            
            UIImageView *view = [_imageLoad compressImage:MY_WIDTH/3 imageView:nil imageName:imageName flag:NO];
            imgView.image = view.image;
        }
    }
}

//點選圖片事件響應
- (void)imageClickWithTag:(UITapGestureRecognizer *)sender{
    UIImageView *view = (UIImageView *)sender.view;
    NSString *imageName = [self.imgTagDic objectForKey:[NSString stringWithFormat:@"%ld", (long)view.tag]];
    
    PhotoViewController *photoView = [[PhotoViewController alloc] init];
    photoView.imageArray = _photoArray;
    photoView.imageName = imageName;
    UIWindow *window = [[UIApplication sharedApplication].delegate window];
    [window addSubview:photoView.view];
}


- (void)dealloc{
    [_imagesName release];
    [_imgTagDic release];
    [_loadedImageArray release];
    [_imageCache release];
    [_fileUtil release];
    [_imageLoad release];
    [_photoArray release];
    [super dealloc];
}

@end
當程式第一次載入或者下拉重新整理時,就開始下載圖片,函式:imageStartLoading用於下載圖片,該函式為每一次下載圖片都開啟一個執行緒,在ImageCacher類中有cacheImage函式,用於判斷該圖片是否已經存在本地,並且將圖片放入到檢視中去。

以下是ImageCacher類的程式碼

#import "ImageCacher.h"

@implementation ImageCacher
@synthesize fileUtil = _fileUtil;
@synthesize imageLoader = _imageLoader;
@synthesize myDelegate = _myDelegate;

+ (ImageCacher *)shareInstance{
    static ImageCacher *instance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });
    
    return instance;
}

- (id)init{
    self = [super init];
    if(self){
        self.fileUtil = [FileUtil shareInstance];
        self.imageLoader = [ImageLoader shareInstance];
    }
    return self;
}

- (void)cacheImage:(NSDictionary*)dic{
    NSURL *url = [dic objectForKey:@"URL"];
    NSFileManager *fileManage = [NSFileManager defaultManager];
    NSData *data = [NSData dataWithContentsOfURL:url];
    
    NSString *fileName = [_fileUtil pathForUrl:url];
    if(data){
        [fileManage createFileAtPath:fileName contents:data attributes:nil];
    }
    
    UIImageView *imageView = [dic objectForKey:@"imageView"];
    imageView.image = [UIImage imageWithData:data];
    imageView = [_imageLoader compressImage:MY_WIDTH/3 imageView:imageView imageName:nil flag:YES];
    [self.myDelegate addImage:imageView name:fileName];
    [self.myDelegate adjustContentSize:NO];
}

- (void)dealloc{
    [super dealloc];
}

@end

由於時間的關係,詳細的講解就留到下期來說吧!

相關推薦

IOS開發非同步載入網路圖片快取本地實現瀑布

</pre><pre name="code" class="objc"></pre><pre name="code" class="objc">在前面的一篇部落格中,我寫了一個瀑布流照片牆的程式,由於之前的程式載入的圖片是本

iOS開發swift版非同步載入網路圖片(帶快取和預設圖片)

iOS開發之swift版非同步載入網路圖片     與SDWebImage非同步載入網路圖片的功能相似,只是程式碼比較簡單,功能沒有SD的完善與強大,支援預設新增圖片,支援本地快取。      非同步載入圖片的核心程式碼如下:  func setZYHWebImage(ur

iOS開發窺探UICollectionViewController(三) :使用UICollectionView自定義瀑布

上篇部落格的例項是自帶的UICollectionViewDelegateFlowLayout佈局基礎上來做的Demo, 詳情請看《iOS開發之窺探UICollectionViewController(二) –詳解CollectionView各種回撥》。UICollectionV

iOS 非同步載入網路圖片,獲取圖片真實尺寸

專案需求: 在網路上載入一組圖片的資料,並且這組圖片要按照比例展示出來 做法分析: (1)首頁介面上要是使用的圖片控制元件的建立,這裡的高度是用設定的高度,這部分需要在主執行緒完成; (2)第二部部分是獲取圖片的實際尺寸,這部分線上程中完成; (3)然後需要在主執行緒重新整

iOS非同步載入網路圖片

在iOS中載入網路圖片有多種方式: 法1:在主執行緒中同步載入網路圖片 在主執行緒中載入圖片,先將圖片的URL存放進NSURL,然後再用這個NSURL初始化NSData,再把UIImage用NSData初始化,就行了。程式碼如下: - (UITableViewCell *

Unity載入網路圖片顯示在UGUI上,解決載入網路圖片出現問號的問題及其案例分析,例項Demo親測可用

Unity載入網路圖片並顯示在UGUI上,解決載入網路圖片出現問號的問題及其案例分析,例項Demo親測可用 最近自己在載入網路圖片的時候也遇到了載入的圖片無法顯示或者是問號的問題。下面就分析下為什麼會出現這樣的情況。   首先我們直接上程式碼(比較簡單) using U

Android批量圖片載入經典系列——使用xutil框架快取非同步載入網路圖片

一、問題描述   為提高圖片載入的效率,需要對圖片的採用快取和非同步載入策略,編碼相對比較複雜,實際上有一些優秀的框架提供瞭解決方案,比如近期在git上比較活躍的xutil框架   Xutil框架提供了四大模組: 1、  DbUtil模組:採用ORM機制簡化Sqlite操作,

Android批量圖片載入經典系列——採用二級快取非同步載入網路圖片

public class AsyncImageLoader{ private MemoryCache mMemoryCache;//記憶體快取 private FileCache mFileCache;//檔案快取 private ExecutorService mExecu

使用UniversalImageLoader 載入網路圖片附帶快取效果

 Android開發中我們會經常遇到圖片過多或操作不當造成OOM異常,有時雖然是解決了這個問題但卻會影響程式的執行效率,例如:當用戶在快速滑動滾動條的過程中,我們程式在仍在艱難的載入伺服器端的圖片,這

廣告banner:手動滑動切換,自動切換,點選跳轉,非同步載入網路圖片

效果圖: 該banner功能有自動切換圖片,點選圖片可以自定義事件,手動滑動切換,非同步載入圖片 程式碼說話: 佈局檔案: <!-- 廣告位 --> <FrameLayout android:

UIImageView非同步載入網路圖片

方法1:在UI執行緒中同步載入網路圖片 UIImageView *headview = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 40, 40)]; NSURL *photourl

Android非同步載入網路圖片

Android圖片的非同步載入,主要原理: 載入圖片時先檢視快取中時候存在該圖片,如果存在則返回該圖片,否則先載入載一個預設的佔位圖片,同時建立一個通過網路獲取圖片的任務並新增,任務完成後放鬆訊息給主執行緒更新介面。 使用方法: Asyn

TextView使用自定義HtmlHttpImageGetter實現非同步載入網路圖片,可限制載入圖片數量

import android.graphics.Canvas; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import androi

Android 非同步載入網路圖片到ImageView Hnadler

首先建立一個Handler private Handler handler=new Handler(){ @Override public void handleMessage(Mes

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

              by Fanxiushu  2015-07-10 轉載或引用請註明原作者  iOS開發中,使用UIImageView控制元件來顯示圖片,非常簡單幾句話就能顯示一個完整的圖片:UIImageView* img =[[UIImageView allo

android textView 載入HTML 非同步載入網路圖片

/** * 重寫圖片載入介面 * * @author Ruffian * @date 2016年1月15日 * */ class HtmlImageGetter implements Html.ImageGetter { /** * 獲取圖片 */ @Ov

iOS開發UITableView中計時器的幾種實現方式(NSTimer、DispatchSource、CADisplayLink)

最近工作比較忙,但是還是出來更新部落格了。今天部落格中所涉及的內容並不複雜,都是一些平時常見的一些問題,通過這篇部落格算是對UITableView中使用定時器的幾種方式進行總結。本篇部落格會給出在TableView中使用NSTimer或者DispatchSourcer中常見的五種方式。當然下方第一種方式是常規

iOS開發旅--Swift3中的註釋、警告、提示寫法#pragma mark

iOS開發之旅–Swift3中的註釋、警告、提示寫法(#pragma mark) 1、在之前的iOS開發中,我們經常使用#pragma mark來註釋,使用#warning打警告,但是在swift3中

iOS開發音視訊邊下邊播快取方案

我還真沒看到目前有哪個公開的實現方案有做的更好的,可能是我孤陋寡聞,如果你知道更好的方案,一定要留言告訴我,鞠躬.. 進入正題,這次的主要內容  理解 AVAssetResourceLoaderDelegate 的使用  快取下載的實現  VIMediaCach

發秒殺系統方案

但是 clas image 數據庫 cdn blog 方案 對象 以及 memcatch相比redis而言,無法做持久化。 JSR303:服務端的驗證框架。 首先我們可以將靜態頁面緩存在用戶的瀏覽器端或者是手機端,然後用戶的請求會到達CDN(網站的緩存和鏡