Главная » iPhone » Таблицы iPhone

0

Таблицы являются базой для большинства списков выбора в iPhone. Голосовая почта, последние звонки и даже электронная почта — все они используют богатые функциональные возможности класса uiTable для отображения своих списков элементов. Помимо того что класс uiTable является простым инструментом выбора из списка, он еще и содержит встроенную функциональность добавления раскрытий, проведения для удаления, анимации, меток и даже изображений.

Создание таблиц

Таблица (table) имеет три основных составляющих: сама таблица, столбцы таблицы и ячейки таблицы. Данные таблицы помещаются в очередь из привязки данных (data binding) таблицы. Привязка данных — это интерфейс, используемый таблицей для помещения в очередь информации о том, какие данные выводить на экран, например, имена файлов, сообщения электронной почты и т. д. Источник данных (data source) — это объект, отвечающий за эту очередь. После того как таблица будет создана, к ней должен быть прикреплен источник данных, чтобы таблица могла что-либо отображать. Он вызывается всякий раз, когда таблица перезагружается, или же путем прокручивания в область просмотра попадают новые ячейки, и сообщает таблице, какие столбцы и строки с данными в них необходимо отобразить.

Наследование UiTable

Таблица как свой собственный источник данных вполне подходит для большинства специфических применений. Это позволяет классу таблицы и данным таблицы быть аккуратно обернутыми в один класс. Чтобы это сделать, унаследуйте объект uiTable для создания нового класса для ваших данных. В приведенном далее примере создается подкласс под названием МуТаЫе. Для обеспечения функциональности части класса, касающейся таблицы, подменяются методы базового класса, используемые для инициализации и разрушения объекта:

@ interface МуТаЫе : UiTable {

}

-(id)initwithFrame:(struct CGRect)rect; -(void) dealloc;

Чтобы добавить часть класса, касающуюся источника данных, используются два метода, чтобы заставить привязку данных таблицы загрузить данные: numberOfRowsinTable и cellForRow. Поскольку таблица выступает в роли своего собственного источника данных, то вы должны написать эти методы в вашем подклассе, где методы будут отвечать за возврат данных столбцов и строк для таблицы.

-   (int)numberOfRowsinTable:(UITable *)_table;

-   (UITableCell *)table:(UITable *)table cellForRow:(int)row

column:(UITableColumn *)col;

Мы рассмотрим эти методы в разд. "Привязка данных" дачее в этой главе. Подмена методов UITable

После создания подкласса UITable должны быть подменены методы инициализации и уничтожения. Это позволит подклассу при создании определить собственные столбцы и стиль и корректно освободить все ресурсы, созданные им для себя.

Методом инициализации для объекта UITable является initwithFrame:

-   (id)initwithFrame:(struct CGRect)rect { self = [ super initwithFrame: rect ]; if (nil != self) {

/* Добавьте сюда дополнительный код инициализации */

}

}

Поскольку таблица выступает в роли собственного источника данных, то можно использовать метод initwithFrame для определения столбцов таблицы. Столбец создается как класс UITableColumn и имеет внутри таблицы свой заголовок, идентификатор и ширину. Объект UITableColumn также используется многими производными классами, например, сортировщиками, обсуждаемыми в главе 7:

UITableColumn *myColumn = [ [ UITableColumn alloc ] initWithTitle: ©"Column 1" identifier:@"columnl"

width: rect.size.width

];

[ self addTableColumn: myColumn ];

Чтобы создать замкнутый класс таблицы, объект uiTable может быть назначен в качестве своего собственного источника данных. Чтобы привязать se как источник данных, выполните setDataSource: [ self setDataSource: self ];

Подмените метод dealloc так, чтобы вы могли добавить код для освобождения всех ресурсов, которые должны освобождаться при уничтожении объекта. Когда объект MyTable уничтожен, метод dealloc должен освободить его столбцы и любые другие ресурсы, которые он занимал. Кроме того, потребуется вызвать метод dealloc его суперкласса, чтобы освободить все ресурсы, созданные внутренне классом uiTable:

-   (void)dealloc {

[ myColumn release ]; [ super dealloc ];

}

Привязка данных

Как было упомянуто в разд. "Наследование UiTable"ранее в этой главе, привязка данных для uiTable состоит из двух методов, предоставляющих данные строк для таблицы. Метод numberOfRowsinTable просто возвращает целое число, отражающее количество строк данных в таблице. Значение используется объектом таблицы для задания количества строк:

-   (int)numberOfRowsinTable:(UiTable *)_table { return 3;

}

Второй метод, ceilForRow, возвращает отдельные строки из таблицы. Он вызывается для каждой строки всякий раз, когда строка попадает в поле зрения. Отдельные строки порождаются от класса UlTableCell. Приведенный далее пример определяет метод ceilForRow, создающий ячейку из класса UlTableCell, определяет ее заголовок, исходя из номера строки, и возвращает ячейку:

-   (UlTableCell *)table:(UiTable *jtable ceilForRow:(int)row

column:(UITableColumn *)col

{

UISimpleTableCell *cell = [ [ UISimpleTableCell alloc ] init ]; NSString *title;

title = [ [ NSString alloc ] initWithFormat: @"Row %d", row ]; t cell setTitle: title ]; return [ cell autorelease ];

}

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

Метки

Метки (labels) являются миниатюрными классами видов, которые могут быть добавлены к ячейкам таблицы, чтобы добавить ячейке таблицы еще больше декорирующего текста. Для различных целей существуют различные классы меток. Например, метка Web-вида имеет вид серого прозрачного овала, содержащего текст. В приведенном далее примере создается класс uiWebViewLabel с такими отступами, чтобы он отображался в левом верхнем углу ячейки:

UiWebViewLabel *label = [ [ UiWebViewLabel alloc ] initwithFrame:

CGRectMake(0.0, 3.0f, 320.0, 20.0) ]; [ label setText: @"My UlWebView" ]; [ cell addSubview: label ];

Доступны следующие классы меток (табл. 3.5).

Таблица 3.5

Класс

Описание

UITextLabel

Отображает простой текст в области просмотра

UITextLabelField

Предоставляет окно ввода текста

Таблица 3.5 (окончание)

Класс

Описание

UIDateLabel

Отображает дату в ее области просмотра

UIWebViewLabel

Отображает текст на серой прозрачной поверхности

Раскрытия

Раскрытия (disclosures)— это значки, отображаемые в правой части ячейки таблицы, чтобы указать на то, что там существует еще один уровень информации, который отображается при выделении ячейки. Это весьма распространено на настольных компьютерах в интерфейсах, подобных iTunes, где пользователь сначала выбирает жанр, затем исполнителя и в конце — песню.

Чтобы разрешить применение раскрытия для конкретной ячейки таблицы, воспользуйтесь методом setShowDisclosure: [ cell setShowDisclosure: YES ]?

Стиль раскрытия также может быть изменен:

[ cell setDisclosureStyle: 0 ];

Доступны следующие стили раскрытия (табл. 3.6).

Таблица 3.6

Стиль

Описание

0

Черная стрелка

1

Синий кружок с белой стрелкой

Ячейки с изображениями и текстовые ячейки

Рядом с текстом строк таблицы могут отображать изображения. Это требует использования различных типов ячеек таблицы под названием

UIImageAndTextTableCell. В примере класса МуТаЫе в методе cellForRow должен возвращаться этот тип объекта вместо uisimpleTableCeii: UIImageAndTextTableCell *cell =

[ [ UIImageAndTextTableCell alloc ] init ];

UllmageView *image = [ [ Ullmage alloc ]

initWithConrentsOfFile: @"/path/to/file.png" ]; [ cell setTitle: @"My row, now with image!" ]; [ cell setlmage: image ]; return ( cell autorelease ];

Для загрузки файла изображения используется класс ui image. Этот класс подробно рассмотрен в главе 7.

Этот тип ячейки данных позволяет также с помощью метода setAlignment менять выравнивание изображения и текста:

[ c’ell setAlignment: 2 ];

Поддерживаются следующие стили выравнивания (табл. 3.7).

Таблица 3.7

Стиль

Описание

0

Изображение и текст выравниваются по левому краю с разделителем поля

1

Изображение и текст выравниваются по левому краю без разделителя поля

2

Изображение выравнивается по левому краю, текст — по центру

3

Изображение выравнивается по левому краю, текст — по правому

ti зависимости от размера загружаемого изображения может потребоваться изменение высоты строки, чтобы вместить все изображение. Метод setRow- ight является частью базового класса uiTable и может быть задан при инициализации таблицы:

self setRowHeight: 64 ];

1оскольку эти настройки — настройки уровня таблицы, то заданная высота -гроки повлияет на все ячейки одинаково.

Провести, чтобы удалить

"ъект uiTable обладает встроенной логикой перехватывания проводимых 1саний и отображения подтверждений на удаление. Это позволяет пользова- ?w ю проводить пальцами по строке, которую он хочет удалить.

Чтобы перехватить удаляющие касания, подмените метод swipe в подклассе, порожденном от uiTable:

- (int)swipe:(int)type withEvent:(struct _GSEvent *)event; {.

CGPoint point = GSEventGetLocationlnWindow (event)'; CGPoint offset = _startOffset;

point.x += offset.x; point.у += offset.у; int row = [ self rowAtPoint:point ];

[ [ self visibleCellForRow: row column:0 ] _showDeleteOrInsertion: YES withDisclosure: NO animated: YES isDelete: YES

andRemoveConfirmation: YES

];

return [ super swipe:type withEvent:event ];

} .:-*

В метод swipe передается событие касания, которое обрабатывается платформой Graphics Services (см. главу 4). Вызов метода GSEventGetLocationlnWindow этой платформы определяет точку на экране, в которой произошло касание. Поскольку возвращается точка на экране (а не в рамках окна), то во внимание необходимо принимать смещение положения окна на экране. Например, если окно отображается ниже 48-точечной панели навигации, то необходимо учитывать это при вертикальном смещении. В данном примере для пересчета координат экрана в координаты окна используется переменная startoffset.

После того как координаты области касания будут определены, вычисляется количество строк путем передачи этих координат методу rowAtPoint, принадлежащему классу uiTable. Он возвращает количество строк в виде целого числа.

Далее, с помощью метода visibleCellForRow класса uiTable определяется сама ячейка таблицы. Он возвращает указатель на выбранную ячейку таблицы, чей метод _showDeleteOrlnsertion может быть вызван для отображения подтверждения на удаление. Оно имеет вид кнопки удаления красного цвета.

Чтобы запретить пользователю удалять строки из таблицы, этот метод должен просто возвращать, не предпринимая никаких действий.

Когда пользователь подтвердит, что хочет удалить строку (путем нажатия кнопки удаления), вызывается внутренний метод wiliDeleteRow. Если какая-либо строка удалена, то эта строка должна быть удалена еще и из источника данных, иначе она будет отображена снова после того, как пользователь прокрутит ее на экране. Кроме того, могут быть еще и дополнительные операции, которые должно выполнить приложение, например, удаление файла или сообщения, связанного с ячейкой:

-   (void)_willDeleteRow:(int)row forTableCell:(id)cell viaEdge:(int)edge animateOthers:(BOOL)animate

{

/* Здесь выполните любые операции удаления */

[ fileList removeObjectAtlndex: row ]; [ super _willDeleteRow: row forTableCell: cell viaEdge: edge animateOthers: animate

Выбор элемента

чогда пользователь выделяет какую-либо ячейку таблицы, вызывается метод аблицы tableRowSelected. Данный метод должен быть подменен, чтобы "рабатывать выделение, например, при открытии файла или запуске прило-

-  ^ния. Количество строк можно получить с помощью метода selectedRow ‘лзового класса:

-   ;void)tableRowSelected:(NSNotification *)notification

r.t index = [ self selectedRow ];

/* Был выбран файл. Здесь сделайте с этим что-нибудь. */

}

После того как найден порядковый номер строки, как показано выше, вы можете использовать его для ссылки на действительное значение строки из ваших собственных данных и произвести над ним соответствующие действия. Например, если ваша таблица является массивом файлов, то вы будете использовать порядковый номер для ссылки на корректное имя файла в вашем массиве. И тогда вы сможете загрузить файл или выполнить какие-либо другие операции, ради которых ваше приложение и задумывалось.

Пример: проводник файлов

В этом примере мы создадим собственный класс uiTable для общего использования в качестве проводника файлов. Данный проводник будет читать каталог, передаваемый с помощью метода setPath, и отображать все имеющиеся в нем файлы с расширением, также передаваемым с помощью метода setExtension. Для большей наглядности примера была добавлена функциональность удаления, но чтобы предотвратить ошибочное удаление файлов в процессе экспериментирования с данным примером, она удаляет файлы только из списка, а не из файловой системы iPhone.

Данный пример может быть легко добавлен к любому приложению iPhone для отображения списка файлов путем включения следующего кода в класс вида приложения:

#import "FileTable.h"

FileTable *fileTable = ( [ FileTable alloc ] initWithFrame: rect ]; [ fileTable setPath: @"/Applications" ]; [ fileTable setExtension: @"app" ]; [ fileTable reloadData ]; [ self addSubview: fileTable ];

Полное приложение, показанное в листингах 3.13—3.18, может быть скомпилировано из пакета инструментов с помощью следующей командной строки:

$ arm-apple-darwin9-gcc -о MyExample MyExample.m FileTable.m DeletableCell.m

-lobjc -framework CoreFoundation -framework Foundation -framework UIKit

#import <CoreFoundation/CoreFoundation.h> #import <UIKit/UIKit.h> #import "FileTable.h"

@interface MainView : UlView

{

FileTable *fileTable;

}

-    (id)initWithFrame:(CGRect)frame;

-    (void)dealloc; @end

^interface MyApp : UIApplication

{

UlWindow *window; MainView *mainView;

}

-    (void)applicationDidFinishLaunching:(NSNotification *)aNotification; 3end

‘import "MyExample.h"

. r.t main (int argc, char **argv)

NSAutoreleasePool *autoreleasePool =

[ [ NSAutoreleasePool alloc ] init ]; int returnCode = UIApplicationMain(argc, argv, @"MyApp", @"MyApp"); . autoreleasePool release ]; return returnCode;

? -;_lementation MyApp

-    (void)applicationDidFinishLaunching:(NSNotification *)aNotification { window = [ [ UlWindow alloc ] initWithContentRect:

[ UIHardware fullScreenApplicationContentRect ]

];

CGRect rect = [ UIHardware fullScreenApplicationContentRect ]; rect.origin.x = rect.origin.у = O.Of;

mainView = [ [ MainView alloc ] initwithFrame: rect ];

[ window setContentView: mainView ]; [ window orderFront: self ]; [ window makeKey: self ]; [ window _setHidden: NO ];

}

@end

^implementation MainView

-    (id)initwithFrame:(CGRect)rect {

self = [ super initwithFrame: rect ]; if (nil != self) {

fileTable = [ [ FileTable alloc ] initwithFrame: rect ]; [ fileTable setPath: /Applications" ]; [ fileTable setExtension: @"app" ]; [ fileTable reloadData ]; [ self addSubview: fileTable ];

}

return self;

}

-    (void)dealloc {

[ self dealloc ]; [ super dealloc ];

}

@end

#import <UIKit/UIKit.h> #import <UIKit/UISimpleTableCell.h> #import <UIKit/UIImageAndTextTableCell.h> #import <UIKit/UITableColumn.h> #import <UIKit/UIImage.h>

#import <GraphicsServices/GraphicsServices.h>

@interface FileTable : UITable

{

NSString *path; NSString *extension; NSMutableArray *fileList; UITableColumn *colFilename; UITableColumn *colType;

}

-    (id)initwithFrame:(struct CGRect)rect;

-    (void)setPath:(NSString *)_path;

-    (void)setExtension:(NSString *)_extension;

-    (void)reloadData;

-    (int)swipe:(int)type withEvent:(struct _GSEvent *)event;

-    (int)numberOfRowsInTable:(UITable *)_table;

-    (UITableCell *)table:(UITAble *)table cellForRow:(int)row

column:(UITableColumn *)col;

-    (void)_willDeleteRow:(int)row forTableCell:(id)cell viaEdge:(int)edge ir.imateOthers: (ВСЮЬ) animate;

«-.rport "FileTable.h" • „rport "DeletableCell.h"

-    (void)dealloc; Send

@implementation FileTable

- (id)initWithFrame:(struct CGRect)rect { self = [ super initWithFrame: rect ]; if (nil != self) {

colFilename = [ [ UITableColumn alloc ] • initWithTitle: @"Filename" identifier:@"filename" width: rect.size.width – 75

];

[ self addTableColumn: colFilename ];

colType = [ [ UITableColumn alloc ] initWithTitle: @"Type" identifier:@"type" width: 75

];

[ self addTableColumn: colType ];

[ self setSeparatorStyle: 1 ];

[ self setDelegate: self

[ self setDataSource: self ];

t self setRowHeight: 64 ];

fileList = [ [ NSMutableArray alloc] init ];

}

return self;

}

- (void) setPath:(NSString *)_path { path = [ _path copy ];

-     (void) setExtension:(NSString *)_extension { extension = [ _extension copy ];

}

-     (void) reloadData {

NSFileManager *fileManager = [ NSFileManager defaultManager ]; NSDirectoryEnumerator *dirEnum; NSString *file;

if ([ fileManager fileExistsAtPath: path ] == NO) { return;    t

)

[ fileList removeAHObjects ] ;

dirEnum = [ [ NSFileManager defaultManager ] enumeratorAtPath: path ]; while ((file = [ dirEnum nextObject ])) ( if ([ file hasSuffix: extension ] = YES) { t fileList addObject: file ];

}

}

[ super reloadData ];

int)numberOfRowsinTable:(UiTable *)_table { return [ fileList count ];

UlTableCell *)table:(UiTable *)table ceilForRow:(int)row . ciumn:(UITableColumn *)col

f (col == colFilename) { CeletableCell *cell = [ [ DeletableCell alloc ] init ];

[ cell setTable: self ]; UllmageView *image = [ [ Ullmage alloc ] initWithContentsOfFile: [ [ NSString alloc ]

initWithFormat: @"/Applications/?;®/icon.png", [ fileList objectAtIndex: row ] ] ]; [ cell setTitle: [ [ fileList objectAtIndex: row ] stringByDeletingPathExtension ]]; [ cell setlmage: image ]; [ cell setShowDisclosure: YES ]; [ cell setDisclosureStyle: 3 ]; return [ cell autorelease ]; } else if (col = colType) {

DeletableCell *cell = [ [ DeletableCell alloc ] init ] ; [ cell setTable: self ]; [ cell setTitle: extension ]; return [ cell autorelease ] ;

•}

}

- (int)swipe:(int)type withEvent:(struct GSEvent *)event; {

CGPoint point= GSEventGetLocationlnWindow(event) ; CGPoint offset = _startOffset;

if (point.x < 100 || point.x > 200) { point.x += offset.x; point.у += offset.у; int row = [ self rowAtPoint:point ];

[ [ self visibleCellForRow: row column: 1 ] _showDeleteOrInsertion: YES withDisclosure: NO animated: YES isDelete: YES

andRemoveConfirmation: YES

Г;

return [ super swipe:type withEvent:event ];

}

}

-     (void)_willDeleteRow:(int)row forTableCell:(id)cell viaEdge:(int)edge animateOthers:(BOOL)animate

{

[ fileList removeObjectAtIndex: row ];

[ super _willDeleteRow: row forTableCell: cell viaEdge: edge animateOthers: animate ];

}

-     (void)tableRowSelected:(NSNotification *)notification {

NSString *fileName = [ fileList objectAtlndex: [ self selectedRow ] J; /* Бьш выбран файл. Здесь сделайте с этим что-либо. */

}

-     (void)dealloc {

[ colFilename release ]; [ colType release ]; [ fileList release ]; [ super dealloc ] ;

}

#import "FileTable.h"

(^interface DeletableCell : UIImageAndTextTableCell

{

@end

FileTable *table;

}

- (void)setTable:(FileTable *)_table; @end

#import "DeletableCell.h" @implementation DeletableCell

-    (void)removeControlWillHideRemoveConfirmation:(id)fp8

{

[ self _showDeleteOrInsertion:NO withDisclosure:NO animated:YES isDelete:YES

andRemoveConfirmation:YES

];

}

-    (void)_willBeDeleted {

int row = [ table _rowForTableCell: self ]; /* Сделайте что-нибудь; эта строка удаляется. */

}

-    (void)setTable:(FileTable *)_table { table = _table;

}

0end

Как это работает

Класс FileTable работает следующим образом:

1.     Когда вызывающий класс размещает новый объект FileTable, то он вызывает его метод initwithFrame.

2.     Это приводит к созданию двух объектов UITableColumn, которые становятся столбцами таблицы: Filename и Туре. Этот класс также определяет себя как источник данных и задает некоторые эстетические свойства.

3.      Методы таблицы файлов setPath и setExtension используются для задания нужного пути и расширения отображаемых файлов.

4.      Вызывающий класс вызывает метод reloadData объекта. Это приводит к тому, что таблица файлов генерирует список файлов с указанным расширением в каталоге по указанному пути. Этот список хранится в массиве fileList. Затем вызывается метод reloadData суперкласса, который посредством метода numberOfRowsinTable ставит в очередь количество строк из источника данных.

5.     Как только таблица файлов готова к отображению ячеек, она снова вызывает свою привязку данных, чтобы получить данные для каждой ячейки в виде. Для каждого столбца и каждой строки в виде вызывается метод cellForRow, который возвращает соответствующие запросу ячейки строки.

6.     Если пользователь проводил пальцем вдоль строки, то вызывается метод swipe класса. В результате отображается кнопка подтверждения на удаление путем вызова метода visibleCellForRow с соответствующими свойствами.

Если пользователь подтверждает удаление, то вызывается метод wiliDeleteRow ячейки таблицы. В результате строка удаляется из источника данных, поэтому она больше не будет отображаться на экране, даже если ячейку прокрутить на экране.

Для дальнейшего изучения

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

"J Встройте эту таблицу в пример "Hello, World!", приведенный в начале главы. Вместо отображения объекта uiTextview построЯте объект FileTable из последнего примера. Убедитесь в том, что при компиляции

вы импортировали (#import) заголовок класса и включили его в исходный список.

?         Измените данный пример, добавив метод, разрешающий или запрещающий удаления. Это должно изменить поведение метода swipe так, что при запрещенном удалении он станет преждевременно возвращать.

?         Используйте этот пример для отображения приложений в папке /Applications. Измените этот пример, чтобы использовать объект UIImageAndTextTableCell для отображения значка приложения рядом с его именем.

?         Проверьте наличие прототипов UITable. h, UISimpleTableCell.h, UIImageAndTextTableCell.h и UITableColumn.h в каталоге вашего пакета инструментов. Вы найдете их в папке /toolchain/sys/usr/incIude/UIKit/.

?         Проверьте различные типы меток. Прототипы UITextFieldLabel.li. UITextLabel.h, UIDateLabel.h и UIWebViewLabel.h можно найти в каталоге вашего пакета инструментов /tooIchain/sys/usr/include/UIKit/.

Источник: Здзиарски Дж. iPhone. Разработка приложений с открытым кодом: Пер„с англ. — 2-е изд., перераб. и доп. — СПб.: БХВ-Петербург, 2009. — 368 е.: ил.

По теме:

  • Комментарии