IOSが開発した非同期ネットワーク画像をロードし、ローカルで滝の流れをキャッシュする(二)


前回のブログでは、非同期で画像をロードする機能を実現しましたが、前回の時間の問題で、比較的簡単に話したので、この文章では、前のコードで重要なコードについてもう一度詳しく説明します.
まず関数を見てみましょう
/*
 * @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];
    }
}

この関数の役割は、ネットワークピクチャごとにダウンロードスレッドを開くことですが、このプログラムはピクチャキャッシュの技術を使っているので、スレッドを開いてピクチャをダウンロードするたびにローカルキャッシュディレクトリを検索します.
画像が既に存在するかどうかは、存在する場合はビューに直接ロードされます.一般的なOCのスレッド関数は3つあり、NSThread、Cocoa Operations、GCD、(3つの違いを知りたい場合はリンクを開く)
ここでは比較的軽量級のNSThread,d e tachNewThreadSelector関数に伝わる関数名を用いた:cacheImageはクラスImageCacheで得られる関数であり,ここではiOS開発で用いられる比較的多い単例モードにより,
ImageCacheのハンドルが得る、パラメータdicに主にピクチャが格納されたネットワークアドレスおよびimageViewがaddピクチャのビューに用いられ、ピクチャのサイズに応じて適切なサイズに圧縮される.
次はcacheImage関数です.
- (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];
}

この関数は、ダウンロードした画像をファイルの砂箱にキャッシュし(キャッシュファイルは自分で定義して指定することができる)、画像の大きさに応じて等比例圧縮し、固定幅は画面の3分の1の大きさである.
画像表示では不完全や歪みは発生しません.ImageCacheとMyScrrollViewは2つの独立したクラスであるため、ここではiosのdelegate(エージェント)を使用してscrollView上のピクチャのロードを行い、
(エージェントモードとは:クリックしてリンクを開く).
砂箱にキャッシュフォルダを作成する方法を見てみましょう.キャッシュフォルダは普通のフォルダと同じですが、キャッシュファイルを保存するために使用されているだけです.クラスコードは次のとおりです.
//
//  FileUtil.m
//  Test515
//
//  Created by silicon on 14-5-30.
//  Copyright (c) 2014  silicon. All rights reserved.
//

#import "FileUtil.h"

@implementation FileUtil

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

/*
 @breif  
 */
- (void)createPathInDocumentDirectory{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
    NSString *diskCachePath = [[[paths objectAtIndex:0] stringByAppendingPathComponent:@"ImageCache"] retain];
    NSLog(@"%@", diskCachePath);
    
    if(![[NSFileManager defaultManager] fileExistsAtPath:diskCachePath]){
        NSError *error = nil;
        [[NSFileManager defaultManager] createDirectoryAtPath:diskCachePath
                                  withIntermediateDirectories:YES
                                                   attributes:nil
                                                        error:&error];
    }
}

/*
 @breif      
 @param     fileName: 
 */
- (NSString *)pathInDocumentDirectory:(NSString *)fileName{
    NSArray *fileArray = NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
                                                             NSUserDomainMask, YES);
    NSString *cacheDirectory = [fileArray objectAtIndex:0];
    return [cacheDirectory stringByAppendingPathComponent:fileName];
}

/*
 @breif      
 @param     fileName: 
 */
- (NSString *)pathInCacheDirectory:(NSString *)fileName{
    NSArray *fileArray = NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
                                                             NSUserDomainMask, YES);
    NSString *cacheDirectory = [fileArray objectAtIndex:0];
    return [cacheDirectory stringByAppendingPathComponent:fileName];
}

/*
 @breif      
 @param     url: 
 */
- (BOOL)hasCachedImage:(NSURL *)url{
    NSFileManager *fileManager = [NSFileManager defaultManager];
    if([fileManager fileExistsAtPath:[self pathForUrl:url]]){
        return YES;
    }else{
        return NO;
    }
}

/*
 @breif      URL 
 @param     url: url
 */
- (NSString *)pathForUrl:(NSURL *)url{
    return [self pathInCacheDirectory:[NSString stringWithFormat:@"qiaoqiao-%u", [[url description] hash]]];
}

@end

今回のdemoでは、ユーザーが画像をクリックして拡大して左右にスライドできる機能を新たに追加しましたが、実は簡単に実現できました.最初はScrollViewのImageViewごとにtag値を設定し、追加しました.
ジェスチャー(UITAPGestureRecognizer)は、ユーザが画像をクリックすると、クリックビューのtag値に基づいて、対応する画像がどれであるかを取得し、ロードすることができる.左右のスライドをサポートする機能が新しいインタフェースに追加されました
ScrollViewで、ダウンロードした画像をscrollViewに追加します.コードは次のとおりです.
//
//  PhotoViewController.m
//  Test515
//
//  Created by silicon on 14-5-22.
//  Copyright (c) 2014  silicon. All rights reserved.
//

#import "PhotoViewController.h"
#import "ImageLoader.h"

@interface PhotoViewController ()

@end

@implementation PhotoViewController
@synthesize scrollView = _scrollView;
@synthesize imageArray = _imageArray;
@synthesize page = _page;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view.
    [self.view setFrame:CGRectMake(0, 0, MY_WIDTH, MY_HEIGHT)];
    [self.view setBackgroundColor:[UIColor blackColor]];
    
    self.scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 20, MY_WIDTH, MY_HEIGHT)];
    _scrollView.delegate = self;
    _scrollView.contentSize = CGSizeMake(MY_WIDTH * [_imageArray count], MY_HEIGHT);
    _scrollView.showsVerticalScrollIndicator = NO;
    _scrollView.showsHorizontalScrollIndicator = NO;
    _scrollView.backgroundColor = [UIColor blackColor];
    _scrollView.bounces = YES;
    _scrollView.pagingEnabled = YES;
    [self.view addSubview:_scrollView];
    
    // 
    UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(closePhotoView)];
    tapRecognizer.delegate = self;
    _scrollView.userInteractionEnabled = YES;
    [_scrollView addGestureRecognizer:tapRecognizer];
    [tapRecognizer release];
    
    [self loadingImages];
}

- (void)viewWillAppear:(BOOL)animated{
    [_scrollView setContentOffset:CGPointMake([_imageArray indexOfObject:_imageName] * MY_WIDTH, 0)];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

// 
- (void)closePhotoView{
    [self.view removeFromSuperview];
}

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

- (void)loadingImages{
    // 
    for(int i = 0; i < [_imageArray count]; i++){
        NSString *picName = [_imageArray objectAtIndex:i];
        UIImageView *imageV = [[ImageLoader shareInstance] compressImage:MY_WIDTH imageView:nil imageName:picName flag:NO];
        
        float width = imageV.image.size.width;
        float height = imageV.image.size.height;
        
        float new_width = MY_WIDTH;
        float new_height = (MY_WIDTH * height)/width;
        
        imageV.frame = CGRectMake(MY_WIDTH * i, 0, new_width, new_height);
        [_scrollView addSubview:imageV];
        [imageV release];
    }
}

- (void)scrollViewDidScroll:(UIScrollView *)_scrollView{

}

@end

基本的にこのプログラムの主な機能はこれだけですが、他の滝の流れに関する実装は私の前のブログを見てください.クリックしてリンクを開く
どこか書いたり話したりして問題があれば、大神さんの指摘を歓迎します.