|
Июл
22
|
С помощью представления с подробным отображением (drill down table view) можно вывести на экран нижележащие уровни для нужных объектов. Для выбранной строки это будет новое табличное представление с относящимися к выбранному объекту данными. Процесс повторяется до тех пор, пока для элемента не останется категорий нижнего уровня. Это происходит при отображении детализированного представления, не являющегося “UITableView“.
Создание проекта
В XCode создайте новый проект, выбрав опцию “Navigation-Based Application“. Для своего я выбрал имя “DrillDownApp“, которое и будет использоваться в уроке.
Один единственный “UITableView”
Вместо создания трех различных объектов “UITableView” для отображения трех различных уровней воспользуемся всего одним (и одним контроллером табличного представления). По духу такой подход близок веб-программированию, где для вывода списка продуктов достаточно одной страницы.
Структура потока данных
Прежде чем браться за код, разберемся со специфичной структурой потока данных в приложении. В приведенной ниже таблице показано перемещение данных при выборе отдельного элемента.
| First Level | Second Level | Third Level | Fourth Level | Fifth Level | Sixth Level |
| Item 1 | Screen A | Detail View | N/A | N/A | N/A |
| Item 2 | Screen B | Screen C | Detail View | N/A | N/A |
| Item 3 | Screen D | Screen E | Screen F | Detail View | N/A |
| Item 4 | Screen G | Screen H | Screen I | Screen J | Detail View |
При запуске приложения видим следующие элементы: “Item 1“, “Item 2“, “Item 3” и”Item 4“. При выборе элемента “Item 1” табличное представление выводит экран “Screen A“, а при выборе “Screen A“ — загружает детализированное представление. То же происходит с “Item 2“, который загружает табличное представление с единственным значением “Screen B” -> “Screen C” -> Детализированное представление. Далее по тому же принципу для оставшихся значений первого табличного представления.
Создание источника данных
Исходными данными для этого урока выступит файл XML или plist. Выполнив “File -> New File” создайте новый файл в папке “Resource“, (в MAC OS X) выберите “Other -> Property List” и сохраните его, присвоив имя “Data.plist“. Для начала добавим значения к этому файлу списка свойств.
Выбрав элемент “Root“, создайте новый элемент, щелкнув на стрелке справа.
![]()
Присвойте элементу имя “Rows“, а в качестве типа укажите “Array“. В этом массиве будут храниться все элементы начального уровня вместе с дочерними записями. Пользователь получает возможность, выбрав в табличном представлении элемент, загрузить другое табличное представление со всеми записями, относящимися к выбранному первым элементу. Для этого массиву потребуется словарь с полями как минимум двух типов — строка и массив. Строка предназначена для значения ячейки, а массив — для дочерних записей, где, в свою очередь, тоже будет содержаться n-ное количество словарей (n — число дочерних записей конкретного элемента).
Создайте новый элемент “Rows” и в качестве типа укажите для него “Dictionary” (ключ для словаря менять не нужно). Выбрав словарь, создайте еще один элемент: тип данных — “string“, ключ — “Title“. Имя ключа должно быть единым во всем файле списка свойств — этот ключ будет хранить значение ячейки, отображаемой в “UITableViewCell”. В качестве значения указываем “Item 1“, поскольку это первый выводимый в табличном представлении элемент. Из приведенной выше таблицы нам известно, что выбор первого элемента перезагружает табличное представление и выводит “Screen A“. Под “Item 1” создаем еще одно поле, меняем его тип на “Array” и задаем ключ “Children“. Под элементом “Children” создаем еще один объект с типом “Dictionary“, в котором будет храниться исключительно строка “Screen A” с ключом “Title“.При выборе “Screen A” мы загружаем детализированное представление — на этом уровне нет необходимости добавлять дочерние записи. Внесите в список свойств оставшиеся элементы таблицы. В итоге список должен выглядеть следующим образом.
Наполнение источника данных
Сформировав источник данных, наполним его с помощью делегата приложения. Поскольку типом корневого элемента “data.plist” является “Dictionary“, создадим переменную типа “NSDictionary“, которая будет содержать все данные из файла списка свойств. Заголовочный файл делегата приложения меняется следующим образом:
[code='objc']
//DrillDownAppDelegate.h
@interface DrillDownAppAppDelegate : NSObject {
UIWindow *window;
UINavigationController *navigationController;
NSDictionary *data;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet UINavigationController *navigationController;
@property (nonatomic, retain) NSDictionary *data;
@end
[/code]
Переменная “data” содержит источник данных, который также синтезируется в файле реализации и освобождается в методе “dealloc” (код не указан).
Вот как вносятся данные в методе “applicationDidFinishLaunching“:
[code='objc']
//DrillDownAppDelegate.m
- (void)applicationDidFinishLaunching:(UIApplication *)application {
NSString *Path = [[NSBundle mainBundle] bundlePath];
NSString *DataPath = [Path stringByAppendingPathComponent:@"data.plist"];
NSDictionary *tempDict = [[NSDictionary alloc] initWithContentsOfFile:DataPath];
self.data = tempDict;
[tempDict release];
// Настройка и отображение окна
[window addSubview:[navigationController view]];
[window makeKeyAndVisible];
}
[/code]
Мы считываем файл “data.plist” и все данные в нем в словарь посредством сообщения “initWithContentsOfFile“, представленного в классе “NSDictionary“.
Вывод данных в табличном представлении
Поскольку для отображения разноуровневых данных будет использоваться одно табличное представление, потребуется массив для хранения данных текущего уровня, строка для выбранного на родительском уровне элемента и целочисленное значение, сообщающее о том, первый это уровень или нет. Ниже приведен отредактированный заголовочный файл “RootViewController“:
[code='objc']
//RootViewController.h
@interface RootViewController : UITableViewController {
NSArray *tableDataSource;
NSString *CurrentTitle;
NSInteger CurrentLevel;
}
@property (nonatomic, retain) NSArray *tableDataSource;
@property (nonatomic, retain) NSString *CurrentTitle;
@property (nonatomic, readwrite) NSInteger CurrentLevel;
@end
[/code]
Свойство “tableDataSource” содержит источник данных текущего уровня и имитирует файл списка свойств. Массив содержит n-ное количество словарей, где n—число словарей на данном уровне. В словаре будут храниться заголовок и массив с дочерними записями текущего элемента. Все свойства уже синтизированы, а “tableDataSource” и “CurrentTitle” освобождены в методе “dealloc“.
Вот как меняется метод “viewDidLoad“:
[code='objc']
//RootViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
if(CurrentLevel == 0) {
//Инициализация нашего источника табличных данных
NSArray *tempArray = [[NSArray alloc] init];
self.tableDataSource = tempArray;
[tempArray release];
DrillDownAppAppDelegate *AppDelegate = (DrillDownAppAppDelegate *)[[UIApplication sharedApplication] delegate];
self.tableDataSource = [AppDelegate.data objectForKey:@"Rows"];
self.navigationItem.title = @”Root”;
}
else
self.navigationItem.title = CurrentTitle;
}
[/code]
При значении “CurrentLevel“, равном нулю (например, при запуске приложения), массив инициализируется и заполняется из делегата приложения. Здесь получаем массив с установленным на “Rows” ключом, который будет содержать данные начального уровня и дочерние записи для каждого элемента. Заголовок элемента навигации в данном случае устанавливается на “Root“. Если нет, для заголовка задается переменная “CurrentTitle“, а “tableDataSource” остается без изменений. Два этих свойства указываются в методе “tableView:didSelectRowAtIndexPath“, который будет работать с ними позже.
Отображение данных
Метод “tableView:numberOfRowsInSection” возвращает подсчет массива:
[code='objc']
//RootViewController.m
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.tableDataSource count];
}
[/code]
Измененный метод “tableView:cellForRowAtIndexPath” выглядит следующим образом:
[code='objc']
//RootViewController.m
- (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];
}
// Настройка ячейки…
NSDictionary *dictionary = [self.tableDataSource objectAtIndex:indexPath.row];
cell.text = [dictionary objectForKey:@"Title"];
return cell;
}
[/code]
Из созданного нами источника данных мы знаем, что в каждом массиве есть минимум по одному элементу типа “Dictionary” и с ключом “Title“, являющимся выводимой в ячейке табличного представления строкой. После создания ячейки “UITableView” получаем из массива словарь и значение для ключа “Title“, настроенного на текстовое свойство ячейки.
Выбор строки
При выборе строки вызывается метод “tableView:didSelectRowAtIndexPath“, выглядящий следующим образом:
[code='objc']
//RootViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//Получаем словарь выбранного источника данных.
NSDictionary *dictionary = [self.tableDataSource objectAtIndex:indexPath.row];
//Получаем дочерние записи текущего элемента.
NSArray *Children = [dictionary objectForKey:@"Children"];
if([Children count] == 0) {
}
else {
//Готовим для “tableview”.
RootViewController *rvController = [[RootViewController alloc] initWithNibName:@”RootViewController” bundle:[NSBundle mainBundle]];
//Прирост текущего представления
rvController.CurrentLevel += 1;
//Настройка заголовка;
rvController.CurrentTitle = [dictionary objectForKey:@"Title"];
//Помещаем новое табличное представление в стэк
[self.navigationController pushViewController:rvController animated:YES];
rvController.tableDataSource = Children;
[rvController release];
}
}
[/code]
Сначала получаем словарь для выбранной строки, а потом помещаем в массив соответствующие дочерние записи. Если таковых для текущего уровня нет, на этом пока все. Если есть, загружаем то же самое табличное представление с другим источником данных. Инициализируем прежний контроллер “RootViewcontroller“, приращиваем значение “CurrentLevel“, чтобы оно отличалось от нуля, настраиваем текст в выбранной ячейке на “CurrentTitle“, помещаем контроллер представления в верхнюю часть стека, задаем для нового источника данных свойство “tableDataSource“.
Попробуйте выполнить эти шаги, и получите возможность переходить на нижележащие уровни при выборе строки.
Загрузка детализированного представления
Создайте новое представление (в редакторе интерфейсов IB) и присвойте ему имя “DetailView“. Не забудьте, что представления хранятся в папке “Resources“. В Xcode создайте новый “UIViewController” в папке “Classes” и присвойте ему имя “DetailViewController“. Для относящегося к “DetailView” объекта “File’s owner” укажите класс “DetailViewController” и свяжите переменную outlet с объектом в представлении (в nib-файле).
В относящемся к “DetailViewController” методе “viewDidLoad” настройте заголовок представления на “Detail View“. Вот как при этом будет выглядеть код:
[code='objc']
//DetailViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.title = @”Detail View”;
}
[/code]
Когда у элемента не останется дочерних записей, загрузим детализированное представление. Вот как при этом изменится метод “tableView:didSelectRowAtIndexPath“:
[code='objc']
//RootViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//Получаем словарь выбранного источника данных
NSDictionary *dictionary = [self.tableDataSource objectAtIndex:indexPath.row];
//Получаем дочерние записи текущего элемента.
NSArray *Children = [dictionary objectForKey:@"Children"];
if([Children count] == 0) {
DetailViewController *dvController = [[DetailViewController alloc] initWithNibName:@”DetailView” bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:dvController animated:YES];
[dvController release];
}
else {
//Готовим для “tableview”.
RootViewController *rvController = [[RootViewController alloc] initWithNibName:@”RootViewController” bundle:[NSBundle mainBundle]];
//Прирост текущего представления
rvController.CurrentLevel += 1;
//Настройка заголовка;
rvController.CurrentTitle = [dictionary objectForKey:@"Title"];
//Помещаем новое табличное представление в стек
[self.navigationController pushViewController:rvController animated:YES];
rvController.tableDataSource = Children;
[rvController release];
}
}
[/code]
Заключение
С помощью предложенной методики к элементу добавляется любое количество дочерних записей без редактирования кода. Для занесения данных на шестой уровень не потребуется создавать еще одно табличное представление — достаточно внести в источник данных новый элемент, а остальные действия выполнит код. Здесь используется n-ное количество объектов табличного представления, включая пример от Apple. Удачи в программировании!


Последние комментарии
Всем ! Заходите на Фан-Футбол...
Windows Phone MarketPlace -...
Планируем приобрести дробемет и...
Как добавить в...
currentTitle = [[NSMutableString alloc] init];...