Pull to refresh

Асинхронная загрузка в iOS

Reading time3 min
Views8.3K
Наверное на эту тему есть достаточно материалов, в том числе и у Apple, но я опишу свой опыт и приведу свой код.

Задача следующая: для некоторого View, имеющего определенное количество subview в которые можно грузить изображения (UIImageView к примеру), нужно асинхронно загрузить некоторое количество изображений без блокировки основного UI.


Наш класс будет называться ImageLoader и будет иметь протокол ImageLoaderDelegate, что бы иметь возможность сообщить делегату, когда картинка уже «приехала»
ImageLoader.h

#import 

@protocol ImageLoaderDelegate; // Предварительное объявление протокола, что бы не иметь проблем
                                                     // с объявлением свойства delegate

@interface ImageLoader : NSObject {
    id  delegate; // Свойство для задания делегата
    int index; // Свойство для упрощения обработки полученной картинки в делегате
    NSMutableData *activeDownloadData; // Буфер для данных
}

@property (nonatomic, assign) id  delegate;
@property (nonatomic) int index;

- (void)loadImage:(NSString *)imageURLString; // Метод, который грузит катринку. 
                                                                          // Принимает на вход URL на картинку

@end

@protocol ImageLoaderDelegate

- (void) appImageDidLoad:(UIImage *)image index:(int)index; // Метод делегата, вызываемый по получению файла

@end

ImageLoader.m

#import "ImageLoader.h"


@implementation ImageLoader

@synthesize delegate, index;

- (void)loadImage:(NSString *)imageURLString {
    NSURL *imgURL = [NSURL URLWithString:imageURLString];
    NSMutableURLRequest *request = [NSURLRequest requestWithURL:imgURL];
    NSURLConnection *newConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
    if (newConnection) {
        activeDownloadData = [[NSMutableData data] retain]; // Важный момент - сделать retain
    }
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    [activeDownloadData setLength:0]; //Если есть отклик от сервера, ставим буфер в 0
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [activeDownloadData appendData:data]; // Собираем данные файла
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
    [connection release];
    [activeDownloadData release];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    UIImage *image = [UIImage imageWithData:activeDownloadData]; // Создаем из данных каритнку
    if (delegate != nil) {
        [delegate appImageDidLoad:image index:index]; // Вызываем метод делегата
    }
    else {
        activeDownloadData = nil;
        NSLog(@"Can't find delegate for ImageLoader");
    }
    [activeDownloadData release];
    [connection release];
}


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

@end

Остается только вставить изображение в нужный view.
Один из вариантов я привожу ниже. Он использует свойство лоадера index что бы добраться до нужного view у которого свойство tag имеет такой же индекс.
Само собой в заголовочном файле нужно сделать наш класс делегатом для ImageLoader добавив в строку interface

...
- (void)viewDidLoad {
    [super viewDidLoad];
    viewIndex = 0;
    NSArray *imageUrls = [NSArray arrayWithObjects:  // Массив ссылок к картинкам
                          @"http://cdn5.iconfinder.com/data/icons/toys/128/teddy_bear_toy_1.png", 
                          @"http://cdn5.iconfinder.com/data/icons/toys/128/teddy_bear_toy_4.png", 
                          @"http://cdn5.iconfinder.com/data/icons/toys/128/teddy_bear_toy_5.png", nil];
    
    for (NSString *imgURL in imageUrls) {
        ImageLoader *loader = [[ImageLoader alloc] init]; // Создаем экземпляр загрузчика
        UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(10+(100*viewIndex), 50, 100, 100)];
        [imgView setTag:viewIndex]; // Ставим таг у ImgView в соотвествие с индексом картинки
        [[self view] addSubview:imgView]; 
        [imgView release];
        [loader setIndex:viewIndex]; // Задаем индекс для экземпляра загрузчика
        [loader setDelegate:self]; // Задаем делегатом этот же класс
        [loader loadImage:imgURL]; // Собственно грузим картинку
        [loader release]; 
        viewIndex++;
    }
}

- (void)appImageDidLoad:(UIImage* )image index:(int)index { // Метод делегата ImageLoader
    for (UIView *tmp in [self view].subviews) { // Для всех subview в текущем view ищем UIImageView c tag = index
        if ([tmp isKindOfClass:[UIImageView class]] && tmp.tag == index) {
            [(UIImageView *)tmp setImage:image]; // Задаем свойство изображения
        }
    }
}
...

Надеюсь материал кому-то будет полезен.
Tags:
Hubs:
Total votes 16: ↑14 and ↓2+12
Comments18

Articles