ПРОГРАММИРОВАНИЕ ПОД IPHONE, IPAD OBJECTIVE-C часть 2

Закачка данных

Операционная система iOS на удивление просто умеет работать с интернетом. В этом примере я покажу сразу два способа как это делать.

 

 

Создайте новый проект на основе представления (View-based Application) и назовите его Internet. Для демонстрации того, что данные с интернета таки попали в телефон проведем эксперимент с картинкой. То есть, наше приложение будет скачивать картинку и показывать ее на экране устройства. За отображение картинок отвечает класс UIImageView. Добавим экземпляр этого класса в интерфейс нашего рабочего класса (InternetViewController). Кроме UIImageView добавим метод, который будет вызываться при нажатии на кнопку «скачать». Весь код выглядит так:

 

<code data-result="[object Object]">#import &lt;UIKit/UIKit.h&gt;

@interface ViewController : UIViewController

@property (nonatomic, retain) IBOutlet UIImageView *imageView;

- (IBAction)easyDownload;

@end</code>

 

Теперь перейдем в реализацию нашего класса InternetViewController. В первую очередь следует синтезировать методы и организовать очистку памяти для переменной imageView (я надеюсь, вы не забыли как это делается). Затем напишем реализацию нашего метода easyDownload. Я назвал этот метод так, потому, что это самый простой способ получить данные с сети (обычно этим способом пользуются индусы, не следует им уподобляться).

 

<code data-result="[object Object]">- (IBAction)easyDownload {
    NSURL *url = [NSURL URLWithString:@"http://www.imaladec.net/img/logo.png"];
    UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
    imageView.image = image;
}</code>

 

В этом методе мы создаем NSURL со строки. Затем используем метод dataWithContentsOfURL класса NSData. Этот метод возвращает нам данные, которые потом мы превращаем в картинку с помощью метода imageWithData класса UIImage. И в конце полученную картинку устанавливаем нашему imageView, который показывает нам ее на экране устройства.

 

Теперь осталось связать наш код с интерфейсом. Как это делается я уже описывал в уроке Знакомство с GUI. В результате вы у вас должен получится примерно такой вид файла InternetViewController.xib.

 

 

Если вы запустите приложение — увидите небольшую «заторможенность» в момент получения данных с сети. Дело в том, что это простой способ, но он блокирует интерфейс. Когда вы нажимаете на кнопку закачки — программа как бы зависает на время, пока все данные не будут получены. А как известно — время получения данных зависит от объема данных и от скорости соединения. То есть, если вы попытаетесь закачать эту картинку через GPRS или если фотография будет большой — возникнет ощущение, что программа вообще перестала работать и ее нужно запускать снова. Но нам в помощь приходит NSURLConnection. Он не блокирует интерфейс, он умеет возвращать ошибки, если прервана связь с интернетом или возникают какие-то другие ошибки. Давайте рассмотрим как он это делает.

 

В первую очередь следует изменить интерфейс нашего класса:

 

<code data-result="[object Object]">#import &lt;UIKit/UIKit.h&gt;

@interface ViewController : UIViewController

@property (nonatomic, retain) IBOutlet UIImageView *imageView;
@property (nonatomic, retain) NSMutableData *imageData;

- (IBAction)easyDownload;
- (IBAction)complexDownload;

@end</code>

 

Как видите, изменений не много, я добавил одну переменную (imageData она будет хранить порции данных, которые мы получаем), добавил для этой переменной свойства (для облегчения работы с памятью). И добавил еще один метод (он будет привязан к кнопке старта закачки).

 

В реализации класса так же как и для imageView я синтезировал методы доступа и организовал очистку памяти для imageData. Затем добавил реализацию метода complexDownload.

 

<code data-result="[object Object]">- (IBAction)complexDownload {
    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
    NSURL *url = [NSURL URLWithString:@"http://www.imaladec.net/img/logo.png"];
    NSURLRequest *theRequest=[NSURLRequest requestWithURL:url
                                              cachePolicy:NSURLRequestUseProtocolCachePolicy
                                          timeoutInterval:60.0];

    NSURLConnection *theConnection = [NSURLConnection connectionWithRequest:theRequest delegate:self];
    if (theConnection) {
        self.imageData = [NSMutableData data];
    } else {
        [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
        NSLog(@"Connection failed");
    }
}</code>

 

Да, кода здесь по-больше. В первой строчке этого метода я включаю индикатор активности сети. Дело в том, что, когда пользователь нажем на кнопку старта скачивания — ничего не произойдет, файл сразу не скачается и у пользователя сложиться впечатление, что эта кнопка вообще не работает. Чтобы показать, что программа что-то делает существует NetworkActivityIndicator (индикатор активности сети), вы наверняка уже видели его, когда использовали сафари на iPhone/iPad. Затем, как и в первом случае мы создаем ссылку со строки, а на основании ссылки создаем запрос. Кроме ссылки, при создании запроса в метод requestWithURL мы передаем еще два параметра — метод кэширования и интервал времени, в течении которого программа будет ждать получения первых данных (интервал задаем в секундах).

 

После создания запроса мы создаем экземпляр класса NSURLConnection в метод инициализации которого и передаем наш запрос. Затем следует проверка валидности нашего соединения. Если соединение получилось — инициализируем нашу переменную imageData, в противном случае — выводим ошибку в консоль. И в самом конце очищаем из памяти наше соединение.

 

Когда первые данные с сети поступят на устройство — в программе вызовется метод didReceiveData.

 

<code data-result="[object Object]">- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [imageData appendData:data];
}</code>

 

То есть мы не получим сразу всю картинку, мы ее получаем частичками (пакетами), а в методе didReceiveData собираем пакеты данных в одину переменную imageData.

 

Когда все данные будут получены вызовется метод connectionDidFinishLoading.

 

<code data-result="[object Object]">- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    UIImage *image = [UIImage imageWithData:imageData];
    imageView.image = image;
    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
}</code>

 

В этом методе мы с полученных данных создаем картинку и устанавливаем эту картинку нашему imageView. После того как программа выполнила свою задачу мы убираем индикатор активности сети.

 

Если по какой-то причине данные невозможно получить с сети — NSURLConnection вернет ошибку. В методе didFailWithError мы выводим эту ошибку в консоль.

 

<code data-result="[object Object]">- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    NSLog(@"%@", error);
    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
}</code>

 

Теперь осталось лишь связать метод complexDownload с действием нажатия кнопки (для этого метода я добавил отдельную кнопку) и получать удовольствие от результата.

 

В конце хочу предупредить об одной особенности работы с NSURLConnection. Представьте ситуацию, когда у вас есть массив ссылок на разные картинки и вы эти картинки хотите вывести в таблицу (в том порядке, в котором они хранятся в массиве). Если вы в цикле будете создавать соединения то в методе didReceiveData вы получите данные с разных файлов картинок. В результате на экран будет выведена просто каша из пакетов разных файлов. Для исправления этой ситуации следует создать сабкласс для класса NSURLConnection, экземпляры которого будут иметь ключ, этот клю вы будете присваивать в момент создания запроса, и данные, которые будут приходить — тоже будут иметь свой ключ, по которому вы будете их сортировать. Но реализация данного способа выходит за рамки этой статьи, здесь я лишь привел простой пример работы с сетью.

 

Исходный код можно скачать здесь.