Radial 50 - “Арканоид” нового поколения Распродажи по случаю дня рождения App Store
Июл 10

В четвертом уроке мы освоим поиск по содержимому табличного представления, как обычно воспользовавшись кодом предыдущего урока. Вот как в итоге будет выглядеть приложение:

Добавление панели поиска

Для начала добавим к сегментированному представлению панель поиска. Двойным щелчком на файле “RootViewController.nib” откройте редактор интерфейсов Interface Builder. Перетащите в nib-файл объект “UISearchBar“. В XCode откройте файл “RootViewController.h” и задекларируйте переменную типа “UISearchBar” с названием “searchBar” и ключевым словом “IBoutlet” (для отображения в IB). Соедините переменную с объектом “UISearchBar” и настройте делегата “UISearchBar” на объект “File’s Owner“. Тем самым мы уведомляем “RootViewController” обо всех событиях, генерируемых на панели поиска. Вот как при этом будет выглядеть заголовочный файл (я добавил переменных и вспомогательный метод для поиска):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//файл "RootViewController.h"
@interface RootViewController : UITableViewController {

NSMutableArray *listOfItems;
NSMutableArray *copyListOfItems;
IBOutlet UISearchBar *searchBar;
BOOL searching;
BOOL letUserSelectRow;
}

- (void) searchTableView;
- (void) doneSearching_Clicked:(id)sender;

@end

Панель поиска добавляется к заголовку табличного представления в методе “viewDidLoad“:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//файл "RootViewController.m"
- (void)viewDidLoad {
[super viewDidLoad];

//Инициализация массива.
listOfItems = [[NSMutableArray alloc] init];

NSArray *countriesToLiveInArray = [NSArray arrayWithObjects:@"Iceland", @"Greenland", @"Switzerland", @"Norway", @"New Zealand", @"Greece", @"Rome", @"Ireland", nil];
NSDictionary *countriesToLiveInDict = [NSDictionary dictionaryWithObject:countriesToLiveInArray forKey:@"Countries"];

NSArray *countriesLivedInArray = [NSArray arrayWithObjects:@"India", @"U.S.A", nil];
NSDictionary *countriesLivedInDict = [NSDictionary dictionaryWithObject:countriesLivedInArray forKey:@"Countries"];

[listOfItems addObject:countriesToLiveInDict];
[listOfItems addObject:countriesLivedInDict];

//Инициализация копии массива.
copyListOfItems = [[NSMutableArray alloc] init];

//Настройка заголовка
self.navigationItem.title = @"Countries";

//Добавление панели поиска
self.tableView.tableHeaderView = searchBar;
searchBar.autocorrectionType = UITextAutocorrectionTypeNo;

searching = NO;
letUserSelectRow = YES;
}

Помимо прочего, мы указали полезные для поиска булевы переменные — подробнее о них чуть ниже. Панель поиска добавляется к свойству табличного представления “tableHeaderView“. Результаты поиска хранятся в переменном массиве “copyListOfItems” — при поиске он выступает источником данных. Запустите приложение, и увидите, что в верхней части табличного представления появилась панель поиска. Освобождается она в методе “dealloc” (исходный код здесь не приводится).

Как будет работать поиск

Прежде чем переходить к коду, поговорим о том, что нам предстоит сделать. Когда пользователь щелчком на текстовом поле панели начинает поиск, происходят перечисленные ниже операции.
1. Устанавливаем “”searching” переменную на YES.
2. Устанавливаем переменную “letUserSelectRow” на NO, поскольку не хотим, чтобы пользователь выбирал строку при пустом поле поиска.
3. Отключаем прокрутку табличного представления. Делается это во избежание ошибки, имеющей место при прокрутке табличного представления после щелчка по поисковой панели.
4. Справа на панели выводим кнопку “Done” (Готово).
5. Запускаем поиск одновременно с началом ввода пользователем символов, на этот раз позволяя ему выбрать строку.
6. Используем другой источник данных для связывания таблицы, который будет отображать результаты поиска.
7. Выводим результаты поиска общим списком, без группирования.
8. По щелчку на кнопке “Done” скрываем клавиатуру и завершаем поиск.

Обработка событий

Первое событие — вызывающее клавиатуру касание пользователем панели поиска. При этом вызывается метод “searchBarTextDidBeginEditing“, а табличное представление переходит в поисковый режим. Код при этом выглядит так:

1
2
3
4
5
6
7
8
9
10
11
12
//файл "RootViewController.m"
- (void) searchBarTextDidBeginEditing:(UISearchBar *)theSearchBar {

searching = YES;
letUserSelectRow = NO;
self.tableView.scrollEnabled = NO;

//Добавление кнопки "Done".
self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemDone
target:self action:@selector(doneSearching_Clicked:)] autorelease];
}

Устанавливаем переменную “searching” на YES, чтобы перевести табличное представление в поисковый режим. Для переменной “letUserSelectRow” задано значение NO — выбрать строку пользователь не может. Отключена и прокрутка табличного представления — это поможет, когда над представлением появится оверлей. Задачи выполняются в методе “tableView:willSelectRowAtIndexPath“, а код при этом выглядит так:

1
2
3
4
5
6
7
8
//файл "RootViewController.m"
- (NSIndexPath *)tableView :(UITableView *)theTableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {

if(letUserSelectRow)
return indexPath;
else
return nil;
}

Приведенный выше код не дает пользователю выбрать строку, если верна переменная “letUserSelectRow“.

Метод “doneSearching_Clicked” вызывается при щелчке на методе “Done“. Код метода будет представлен далее в уроке.

Поиск по табличному представлению

Метод “searchBar:textDidChange” вызывается, как только пользователь начинает вводить текст в поле поиска. Вот как при этом выглядит код:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//файл "RootViewController.m"
- (void)searchBar:(UISearchBar *)theSearchBar textDidChange:(NSString *)searchText {

//Сначала удаляем все объекты.
[copyListOfItems removeAllObjects];

if([searchText length] > 0) {

searching = YES;
letUserSelectRow = YES;
self.tableView.scrollEnabled = YES;
[self searchTableView];
}
else {

searching = NO;
letUserSelectRow = NO;
self.tableView.scrollEnabled = NO;
}

[self.tableView reloadData];
}

Приведенный выше код насыщен событиями — предлагаю разобрать его по строкам:

1. При поиске сначала мы очищаем использующийся в качестве источника данных массив в табличное представление.
2. Если на панели поиска есть текст, продолжаем поиск вызовом метода “searchTableView“. В этот раз позволяем пользователю выбрать строку с просмотром детализированного представления.
3. Метод “searchTableView” отвечает за поиск по табличному представлению на основе набранного текста и за сбор результатов поиска в массив “copyListOfItems“.
4. При пустой панели поиска отключается прокрутка, переменная поиска устанавливается на NO, пользователю не разрешается выбирать строку.
5. Последним шагом обновляем табличное представление.

Прежде чем разобраться в том, как именно табличное представление выводит результаты поиска, рассмотрим, что происходит при щелчке на кнопке поиска, и проанализируем метод “searchTableView“.

Вызывается метод “searchBarSearchButtonClicked“. Вот как при этом выглядит код с простым обращением к методу “searchTableView“.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//файл "RootViewController.m"
- (void) searchBarSearchButtonClicked:(UISearchBar *)theSearchBar {

[self searchTableView];
}

- (void) searchTableView {

NSString *searchText = searchBar.text;
NSMutableArray *searchArray = [[NSMutableArray alloc] init];

for (NSDictionary *dictionary in listOfItems)
{
NSArray *array = [dictionary objectForKey:@"Countries"];
[searchArray addObjectsFromArray:array];
}

for (NSString *sTemp in searchArray)
{
NSRange titleResultsRange = [sTemp rangeOfString:searchText options:NSCaseInsensitiveSearch];

if (titleResultsRange.length > 0)
[copyListOfItems addObject:sTemp];
}

[searchArray release];
searchArray = nil;
}

Посмотрим, что происходит в методе “searchTableView“. Сначала создаем временный массив поиска, в который попадут все объекты из начального источника данных. Циклически перемещаясь по словарям, добавляем все объекты массива в “searchArray“. После этого просматриваем все элементы массива “searchArray“, сравнивая их с ищущимся текстом. При опознании в одной из стран текста с панели поиска добавляем строку в “copyListOfItems“.

Завершение поиска

Приведенный ниже метод “doneSearching_Clicked” вызывается при щелчке пользователем на кнопке “Done“:

1
2
3
4
5
6
7
8
9
10
11
12
13
//файл "RootViewController.m"
- (void) doneSearching_Clicked:(id)sender {

searchBar.text = @"";
[searchBar resignFirstResponder];

letUserSelectRow = YES;
searching = NO;
self.navigationItem.rightBarButtonItem = nil;
self.tableView.scrollEnabled = YES;

[self.tableView reloadData];
}

Скрываем клавиатуру, даем пользователю возможность выбрать строку, для переменной “searching” задаем ложное значение, скрываем правую кнопку на панели.

Вывод результатов поиска

Результаты поиска хранятся в массиве “copyListOfItems“, табличное представление обновляется вызовом “reloadData“. При поиске переменная “searching” устанавливается на YES — мы знаем, что результаты нужно вывести на экран. Для их отображения откорректируем ряд методов, включая “numberOfSectionsInTableView“, “tableView:numberOfRowsInSection“, “tableView:titleForHeaderInSection“, “tableView:cellForRowAtIndexPath” и “tableView:didSelectRowAtIndexPath“. Если для переменной “searching” задано значение YES, отображаются данные из “copyListOfItems“. В противном случае информация выводится прежним способом. Ниже приводится код для всех перечисленных методов:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
//файл "RootViewController.m"
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {

if (searching)
return 1;
else
return [listOfItems count];
}

// Настройка количества строк в табличном представлении.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

if (searching)
return [copyListOfItems count];
else {

//Число ожидаемых строк зависит от раздела.
NSDictionary *dictionary = [listOfItems objectAtIndex:section];
NSArray *array = [dictionary objectForKey:@"Countries"];
return [array count];
}
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {

if(searching)
return @"";

if(section == 0)
return @"Countries to visit";
else
return @"Countries visited";
}

// Настройка внешнего вида ячеек табличного представления.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

static NSString *CellIdentifier = @"Cell";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}

// Настройка ячейки...

if(searching)
cell.text = [copyListOfItems objectAtIndex:indexPath.row];
else {

//Сначала получаем объект-словарь.
NSDictionary *dictionary = [listOfItems objectAtIndex:indexPath.section];
NSArray *array = [dictionary objectForKey:@"Countries"];
NSString *cellValue = [array objectAtIndex:indexPath.row];
cell.text = cellValue;
}

return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

//Получаем выбранную страну.

NSString *selectedCountry = nil;

if(searching)
selectedCountry = [copyListOfItems objectAtIndex:indexPath.row];
else {

NSDictionary *dictionary = [listOfItems objectAtIndex:indexPath.section];
NSArray *array = [dictionary objectForKey:@"Countries"];
selectedCountry = [array objectAtIndex:indexPath.row];
}

//Инициализируем контроллер детализированного представления с последующим отображением.
DetailViewController *dvController = [[DetailViewController alloc] initWithNibName:@"DetailView" bundle:[NSBundle mainBundle]];
dvController.selectedCountry = selectedCountry;
[self.navigationController pushViewController:dvController animated:YES];
[dvController release];
dvController = nil;
}

При поиске в “numberOfSectionsInTableView” возвращаем 1, поскольку результаты отображаются без разбивки по разделам. В “tableView:numberOfRowsInSection” возвращаем итоговое число для массива с результатами поиска. В методе “tableView:titleForHeaderInSection” возвращаем текст “Search Results” (результаты поиска). В методе “tableView:cellForRowAtIndexPath” получаем данные из массива поиска результатов. Та же логика распространяется на получение выбранной страны в методе “tableView:didSelectRowAtIndexPath“.

Добавление оверлея

Попробуйте запустить поиск по приложению с контактами и увидите, что поверх табличного представления выводится серый оверлей, а при касании пользователем представления поиск отменяется. Мы можем поступить так же, добавив над табличным представлением оверлей.

Внесем к ресурсам еще одно представлением с именем “OverlayView“. В XCode создаем контроллер представления с именем “OverlayViewController“. В редакторе IB открываем nib-файл, устанавливаем для “File’s Owner” класс “OverlayViewController” и создаем соответствующие связи. Не забудьте убедиться, что представление способно реагировать на события.

Теперь добавим над табличным представлением оверлей. Делается это в методе “searchBarTextDidBeginEditing” со следующим кодом:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//файл "RootViewController.m"
- (void) searchBarTextDidBeginEditing:(UISearchBar *)theSearchBar {

//Добавляем представление оверлея.
if(ovController == nil)
ovController = [[OverlayViewController alloc] initWithNibName:@"OverlayView" bundle:[NSBundle mainBundle]];

CGFloat yaxis = self.navigationController.navigationBar.frame.size.height;
CGFloat width = self.view.frame.size.width;
CGFloat height = self.view.frame.size.height;

//Параметры x = origion на оси x, y = origon на оси y.
CGRect frame = CGRectMake(0, yaxis, width, height);
ovController.view.frame = frame;
ovController.view.backgroundColor = [UIColor grayColor];
ovController.view.alpha = 0.5;

ovController.rvController = self;

[self.tableView insertSubview:ovController.view aboveSubview:self.parentViewController.view];

searching = YES;
letUserSelectRow = NO;
self.tableView.scrollEnabled = NO;

//Добавление кнопки "Done".
self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemDone
target:self action:@selector(doneSearching_Clicked:)] autorelease];
}

Мы отредактировали заголовочный файл “RootViewController.h“, чтобы добавить переменную “ovController” (тип “OverLayViewController“). Заголовочный файл добавляется в верхнюю часть файла “RootViewController.m“.

Для начала инициализируем контроллер представления оверлея с именем nib-файлаOverlayView“. Произведем некоторые расчеты, выяснив, где заканчивается панель поиска — оверлей не должен перекрывать панель. Настраиваем фрейм представления, который будет перерисовывать содержимое экрана. Настраиваем цвет фона и прозрачность. В следующей строке кода свойству “rvController” присваивается self. С помощью метода “insertSubView” представление вставляется поверх табличного.

В методе “OverlayViewController” мы задекларировали переменную “rvController” (тип “RootViewController“). Объясняется это потребностью в ссылке на контроллер корневого представления из контроллера оверлея. Ниже представлены заголовочный файл и файл реализации “OverlayController“:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//файл "OverlayViewController.h"
@class RootViewController;

@interface OverlayViewController : UIViewController {

RootViewController *rvController;
}

@property (nonatomic, retain) RootViewController *rvController;

@end

//файл "OverlayViewController.m"
#import "OverlayViewController.h"
#import "RootViewController.h"

@implementation OverlayViewController

@synthesize rvController;

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

[rvController doneSearching_Clicked:nil];
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning]; // Освобождается представление, если для него отсутствует superview
// Освобождаем все лишнее, включая данные в кэш-памяти
}

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

Теперь рассмотрим метод “touchesBegan:withEvent“, вызывающийся при касании пользователем оверлея. При этом выполняется запрос метода “doneSearching_Clicked“, определенного в “RootViewController” (такую возможность обеспечила нам ссылка на контроллер корневого представления).

Теперь, когда при щелчке на панели поиска появляется оверлей, необходимо скрывать его с началом поиска. Редактируем метод “searchBar:textDidChange“:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//файл "RootViewController.m"
- (void)searchBar:(UISearchBar *)theSearchBar textDidChange:(NSString *)searchText {

//Сначала удаляем все объекты.
[copyListOfItems removeAllObjects];

if([searchText length] > 0) {

[ovController.view removeFromSuperview];
searching = YES;
letUserSelectRow = YES;
self.tableView.scrollEnabled = YES;
[self searchTableView];
}
else {

[self.tableView insertSubview:ovController.view aboveSubview:self.parentViewController.view];

searching = NO;
letUserSelectRow = NO;
self.tableView.scrollEnabled = NO;
}

[self.tableView reloadData];
}

Как только пользователь начинает поиск, удаляем представление, возвращая его при пустой панели поиска. При щелчке на кнопке “Done” оверлей исчезает и высвобождается. Вот как изменится код для “doneSearching_Clicked“:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//файл "RootViewController.m"
- (void) doneSearching_Clicked:(id)sender {

searchBar.text = @"";
[searchBar resignFirstResponder];

letUserSelectRow = YES;
searching = NO;
self.navigationItem.rightBarButtonItem = nil;
self.tableView.scrollEnabled = YES;

[ovController.view removeFromSuperview];
[ovController release];
ovController = nil;

[self.tableView reloadData];
}

Оверлей удаляется, высвобождается и обнуляется. Запустите приложение, проверив работу функции поиска.

Заключение

Поиск по табличному представлению — хорошая опция, если для выбора строки приходится долго прокручивать список. Удачи в программировании!

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

Текст оригинальной статьи на английском языке [здесь]

Уважаемые читатели, данный материал был переведен и подготовлен к публикации проектом LookApp.ru, при публикации на другом сайте ссылка на LookApp.ru обязательна.

1 звезда2 звезд3 звезд4 звезд5 звезд (3 голосов, средний: 5.00 из 5)
Загрузка ... Загрузка ...


Оставьте комментарий