Статьи

Работа с UIRefreshControl

Когда несколько лет назад Лорен Бричтер представила идею «тянуть, чтобы освежить» в Tweetie 2, разработчики не сразу приняли эту гениальную и интуитивно понятную концепцию. Несмотря на то, что теперь Twitter владеет патентом на концепцию «pull to refresh», это не помешало Apple представить класс UIRefreshControl в iOS 6. Этот новый подкласс UIControl позволяет UIControl добавлять элемент управления «pull to refresh» в любую таблицу. просмотр контроллера в iOS 6.


Ссылка на класс UIRefreshControl коротка, намекая на то, как легко начать работу с этим добавлением фреймворка UIKit. Класс UIRefreshControl непосредственно происходит от UIControl , что означает, что настройка экземпляра UIRefreshControl мало чем отличается от создания и настройки любого другого элемента управления UIKit. После создания экземпляра класса UIRefreshControl вы назначаете его новому свойству refreshControl объекта контроллера табличного представления ( UITableViewController или его подкласса). Контроллер табличного представления заботится о правильном позиционировании и отображении элемента управления обновлением. Как и с любым другим подклассом UIControl , вы присоединяете пару целевого действия к определенному событию, UIControlEventValueChanged в случае UIRefreshControl .

Это не будет учебник по Mobiletuts + без примера, иллюстрирующего, как использовать класс UIRefreshControl в проекте. В оставшейся части этого руководства я покажу вам, как заполнить табличное представление списком твитов, извлеченных из API поиска Twitter. Запрос отправляется в API поиска Twitter, когда пользователь тянет табличное представление dow: pull-to-refresh.


Приложение, которое мы собираемся создать, запрашивает в Twitter Search API твиты о разработке для iOS . Запрос отправляется в API поиска Twitter, когда пользователь перемещает представление таблицы вниз, открывая элемент управления обновлением. Мы будем использовать фантастическую библиотеку AFNetworking для отправки нашего запроса в API поиска Twitter. AFNetworking также поможет нам асинхронно загружать и отображать изображения профиля.

Создайте новый проект в XCode, выбрав пустой шаблон приложения из списка шаблонов ( рисунок 1 ). Назовите свое приложение Pull-to-Refresh , введите идентификатор компании, установите iPhone для семейства устройств и установите флажок Использовать автоматический подсчет ссылок . Остальные флажки могут быть оставлены непроверенными для этого проекта (рисунок 2). Сообщите Xcode, где вы хотите сохранить проект, и нажмите кнопку « Создать» .

Новое в iOS 6: UIRefreshControl: Выбор шаблона проекта - рисунок 1
Новое в iOS 6: UIRefreshControl: Настройка нового проекта - рисунок 2

Установка AFNetworking с использованием Cocoapods очень проста . Однако в этом руководстве я покажу вам, как вручную добавить библиотеку AFNetworking в проект Xcode, чтобы убедиться, что мы все на одной странице. В любом случае это не так сложно.

Загрузите последний стабильный выпуск библиотеки со страницы проекта GitHub , распакуйте архив и перетащите папку с именем AFNetworking в ваш проект Xcode. Убедитесь, что установлен флажок Копировать элементы в папку целевой группы (если необходимо), и дважды проверьте, что библиотека добавлена ​​в цель Pull-to-Refresh (рисунок 3).

Новое в iOS 6: UIRefreshControl: добавление библиотеки AFNetworking - рисунок 3

Библиотека AFNetworking опирается на две платформы, с которыми новый проект XCode по умолчанию не связан, (1) Конфигурация системы и (2) Платформы мобильных базовых сервисов. Выберите свой проект в Навигаторе проектов , выберите цель « Потянуть для обновления» из списка целей и откройте вкладку « Фазы сборки » вверху. Разверните блок « Связать двоичные файлы с библиотеками» и добавьте обе платформы, нажав кнопку «плюс» (рисунок 4).

Новое в iOS 6: UIRefreshControl: Добавление необходимых фреймворков - рисунок 4

Чтобы закончить, добавьте оператор импорта для обеих платформ, а также AFNetworking в предварительно скомпилированный заголовочный файл проектов, как показано в фрагменте ниже. Это облегчит работу с AFNetworking, поскольку нам не нужно добавлять оператор импорта для каждого класса, в котором мы хотим использовать библиотеку.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
//
// Prefix header for all source files of the ‘Pull to Refresh’ target in the ‘Pull to Refresh’ project
//
 
#import <Availability.h>
 
#ifndef __IPHONE_3_0
#warning «This project uses features only available in iOS SDK 3.0 and later.»
#endif
 
#ifdef __OBJC__
    #import <UIKit/UIKit.h>
    #import <Foundation/Foundation.h>
    #import <MobileCoreServices/MobileCoreServices.h>
    #import <SystemConfiguration/SystemConfiguration.h>
 
    #import «AFNetworking.h»
#endif

UIRefreshControl предназначен для работы в сочетании с объектом контроллера табличного представления. Создайте новый подкласс UITableViewController ( File> New> File … ), выбрав шаблон класса Objective C из списка шаблонов ( рисунок 5 ). Дайте новому классу имя MTTweetsViewController и дважды проверьте, что это подкласс UITableViewController . Скажите Xcode, что он не должен создавать nib-файл для нового класса контроллера, сняв флажок с меткой XIB для пользовательского интерфейса (рисунок 6). Укажите местоположение для сохранения нового класса и нажмите кнопку « Создать» .

Новое в iOS 6: UIRefreshControl: создание класса TweetsViewController - рисунок 5
Новое в iOS 6: UIRefreshControl: Настройка класса TweetsViewController - рисунок 6

Прежде чем мы сможем добавить элемент управления обновлением в контроллер табличного представления, нам нужно создать экземпляр нового класса MTTweetsViewController . Обновите application:didFinishLaunchingWithOptions: метод в MTAppDelegate.m, как показано ниже. Реализация не должна содержать сюрпризов. Мы инициализируем экземпляр класса MTTweetsViewController и устанавливаем его как корневой контроллер окна приложения. Не забудьте добавить оператор импорта в верхней части MTAppDelegate.m, чтобы импортировать файл MTTweetsViewController класса MTTweetsViewController .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
— (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Initialize Tweet View Controller
    MTTweetsViewController *vc = [[MTTweetsViewController alloc] initWithStyle:UITableViewStylePlain];
 
    // Initialize Window
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
 
    // Configure Window
    [self.window setBackgroundColor:[UIColor whiteColor]];
    [self.window setRootViewController:vc];
 
    // Make Key and Visible
    [self.window makeKeyAndVisible];
 
    return YES;
}
1
#import «MTTweetsViewController.h»

Если вы запустите приложение в iPhone Simulator, вы должны увидеть пустое табличное представление. Элемент управления обновлением добавляется в метод viewDidLoad контроллера представления твитов. Как я упоминал ранее, добавить элемент управления обновлением очень просто. Взгляните на реализацию метода viewDidLoad показанную ниже. Мы инициализируем элемент управления обновлением и добавляем цель и действие для события UIControlEventValueChanged элемента управления обновлением. Наконец, элемент управления обновлением назначается refreshControl контроллера табличного представления. Конечно, свойство refreshControl также является новым для iOS 6.

01
02
03
04
05
06
07
08
09
10
11
12
— (void)viewDidLoad {
    [super viewDidLoad];
 
    // Initialize Refresh Control
    UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
 
    // Configure Refresh Control
    [refreshControl addTarget:self action:@selector(refresh:) forControlEvents:UIControlEventValueChanged];
 
    // Configure View Controller
    [self setRefreshControl:refreshControl];
}

Прежде чем мы снова соберем и запустим проект, нам нужно реализовать действие refresh: в файле реализации контроллера представления. Чтобы убедиться, что все настроено правильно, мы просто записываем сообщение на консоль. Создайте и запустите проект, чтобы увидеть элемент управления обновлением в действии.

1
2
3
4
— (void)refresh:(id)sender
{
    NSLog(@»Refreshing»);
}

Вы заметите, что элемент управления обновлением не исчезает после того, как он был показан контроллером табличного представления. Это то, что вам придется делать самостоятельно. Идея элемента управления обновлением в некоторой степени аналогична представлению индикатора активности UIActivityIndicatorView ( UIActivityIndicatorView ), то есть вы несете ответственность за его отображение и скрытие. Сокрыть элемент управления обновлением так же просто, как отправить ему сообщение endRefreshing . Обновите действие refresh: как показано ниже и снова запустите приложение в iPhone Simulator.

1
2
3
4
5
6
— (void)refresh:(id)sender {
    NSLog(@»Refreshing»);
 
    // End Refreshing
    [(UIRefreshControl *)sender endRefreshing];
}

Элемент управления обновлением немедленно исчезает после освобождения представления таблицы. Конечно, это делает управление обновлением совершенно бесполезным. Что мы сделаем, это отправим запрос в API поиска в Твиттере и скроем элемент управления обновлением, когда мы получим ответ (или когда истечет время ожидания запроса). AFNetworking делает это очень просто.


Мы будем хранить твиты, которые мы получаем от API поиска Twitter, в виде массива. Добавьте частное свойство с именем твиты в класс MTTweetsViewController как показано ниже.

1
2
3
4
5
6
7
#import «MTTweetsViewController.h»
 
@interface MTTweetsViewController ()
 
@property (strong, nonatomic) NSArray *tweets;
 
@end

Затем обновите numberOfSectionsInTableView: tableView:numberOfRowsInSection: и tableView:cellForRowAtIndexPath: методы, как показано ниже. Если вы раньше работали с табличными представлениями, это не должно быть слишком сложным для понимания.

1
2
3
— (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return self.tweets ?
}
1
2
3
— (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [self.tweets count] ?
}
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
— (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @»Cell Identifier»;
 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
 
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
    }
 
    // Fetch Tweet
    NSDictionary *tweet = [self.tweets objectAtIndex:[indexPath row]];
 
    // Configure Cell
    [cell.textLabel setText:[tweet objectForKey:@»text»]];
    [cell.detailTextLabel setText:[tweet objectForKey:@»from_user»]];
 
    // Download Profile Image Asynchronously
    NSURL *url = [NSURL URLWithString:[tweet objectForKey:@»profile_image_url»]];
    [cell.imageView setImageWithURL:url placeholderImage:[UIImage imageNamed:@»placeholder»]];
 
    return cell;
}

В tableView:cellForRowAtIndexPath: мы создаем новую ячейку (или удаляем из нее многократно используемую ячейку) и заполняем ее содержимым твита. Чтобы обеспечить плавную прокрутку табличного представления, мы загружаем изображение профиля пользователя асинхронно. Это очень легко сделать с помощью AFNetworking, поскольку оно дает нам setImageWithURL:placeholderImage: Что это делает, так это настройка представления изображения ячейки с помощью предоставленного изображения заполнителя при запросе изображения профиля пользователя в фоновом режиме. Чтобы сделать это, добавьте placeholder.png и [email protected] в ваш проект Xcode. Вы можете найти оба файла в исходных файлах этого урока.

Мы отправляем наш запрос в API поиска Twitter в действии refresh: . Посмотрите на обновленную реализацию ниже. Я не буду вдаваться в подробности того, как AFJSONRequestOperation класс AFJSONRequestOperation в этом учебном пособии, но я хочу объяснить, как работает поток запроса. После указания URL-адреса запроса ( NSURL ) и инициализации запроса URL-адреса ( NSURLRequest ) мы создаем операцию запроса JSON, передавая (1) запрос URL-адреса, (2) блок успеха и (3) блок сбоя JSONRequestOperationWithRequest:success:failure: Блок succes выполняется, если запрос был успешным, и дает нам ответ на запрос как экземпляр NSDictionary . Мы извлекаем массив запрашиваемых твитов, обновляем свойство tweets , перезагружаем табличное представление, чтобы показать твиты, и скрываем элемент управления обновлением, отправляя ему сообщение endRefreshing.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
— (void)refresh:(id)sender {
    // Create URL
    NSURL *url = [NSURL URLWithString:@»http://search.twitter.com/search.json?q=ios%20development&rpp=100&include_entities=true&result_type=mixed/»];
 
    // Initialize URL Request
    NSURLRequest *urlRequest = [[NSURLRequest alloc] initWithURL:url];
 
    // JSON Request Operation
    AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:urlRequest success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
        NSArray *results = [(NSDictionary *)JSON objectForKey:@»results»];
 
        if ([results count]) {
            self.tweets = results;
 
            // Reload Table View
            [self.tableView reloadData];
 
            // End Refreshing
            [(UIRefreshControl *)sender endRefreshing];
        }
 
    } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
        // End Refreshing
        [(UIRefreshControl *)sender endRefreshing];
    }];
 
    // Start Operation
    [operation start];
}

Если запрос не выполняется, мы скрываем только элемент управления обновлением. Конечно, было бы лучше проинформировать пользователя о том, что запрос не выполнен, но это будет сделано для нашего примера. Мы отправляем запрос, начиная операцию запроса JSON в конце действия refresh: .

Создайте и запустите проект еще раз, чтобы увидеть пример приложения в действии. Если изображения профиля не отображаются правильно, проверьте еще раз, что вы добавили изображения-заполнители, которые я упоминал ранее, в ваш проект.

Новое в iOS 6: UIRefreshControl: пример приложения в действии - рисунок 7

Есть много библиотек, которые пытаются имитировать оригинальную функциональность «тянуть, чтобы обновить», но приятно видеть, что Apple наконец-то приняла эту изящную концепцию и включила ее в инфраструктуру UIKit. Как вы могли заметить, в iOS 6 Apple уже UIRefreshControl класс UIRefreshControl для использования в некоторых своих собственных приложениях, таких как приложение Podcasts.