Upload
yandex
View
375
Download
0
Embed Size (px)
DESCRIPTION
Yandex Mobile Camp в Санкт-Петербурге, 30 мая 2012Алексей Патосин, iOS-разработчик, Yota Lab.Тема: Как можно доработать UITableViewController, чтобы облегчить себе жизньТезисы: Рассмотрим проблемы, с которыми сталкивался каждый, работая с таблицами в Cocoa Touch. Как можно улучшить существующий подход, как элегантно избавиться от «копипасты» и сфокусироваться только на том, какие данные и в какой структуре показывать. Разработанный подход позволяет свести написание кода при создании таких таблиц и форм к минимуму.
Citation preview
Как можно доработать UITableViewController
чтобы облегчить себе жизнь
Патосин АлексейYotaLab
Yandex Mobile Camp Санкт-Петербург
Agenda1. Проблемы работы с таблицами под iOS
2. У нас есть решение
3. We need to go deeper
1
Давайте поговорим о Cocoa таблицах
Таблицы вокруг нас
3
Какие бывают таблицы?
1. Списки однородных элементов 2. Формы (1-... сущностей)
(всё не так страшно) (сейчас немного поковыряем)
4
Сложные формы
Ночной кошмар, если таблица динамическая
Показывать всегда
Телефон +7(800)123-666-7
Email [email protected]
Добавить поле
Сейчас онлайн
Ringo Star
5 мин
10 мин
15 мин
Полчаса
Jimmy Page
Показывать всегда
Телефон +7(800)123-666-7
Email [email protected]
Добавить поле
Сейчас онлайн
5 мин
10 мин
15 мин
Полчаса
Ringo Star
Если выбрано 15 минут и больше,то тут появляется этот текст
5
Типичные способы управления сложной таблицей
– Гроздь switch/case
– State machine
– Разруливание вариантов гроздями наследников
6
Минусы использования UITableViewController
(обо что спотыкался каждый)
– Настройка таблиц через UITableViewControllerDelegate
– Заполнение и обновление таблицы через UITableViewControllerDataSource
– Работа с ячейкой
(хочется задать все параметры в конструкторе/билдере)
(мы ненавидим indexPath)
(ячейка “не знает”, что на неё нажали)
7
Чего хочется?
Чего хочется?
– Абстрагироваться от работы с таблицей и работать с данными
– Хочется, чтобы таблица стала более “smart” - таблица знала когда себя обновлять - ячейки знали когда себя обновлять - модель знала, что происходит в ячейках
– Хочется реинвентить дата биндинг под iOS
9
“smart” ячейка таблицы
Хочется, чтобы ячейка стала самостоятельным элементом и знала:
10
“smart” ячейка таблицы
Хочется, чтобы ячейка стала самостоятельным элементом и знала:
model view
+7 800 123 6667
что показывать (model > view)
10
“smart” ячейка таблицы
Хочется, чтобы ячейка стала самостоятельным элементом и знала:
model view
+7 800 123 6667
model view
+7 800 123
что обновлять (view changed > model changed)
10
“smart” ячейка таблицы
Хочется, чтобы ячейка стала самостоятельным элементом и знала:
model view
+7 800 123 6667
model view
+7 800 123
model view
+7 800 123 4567
когда обновлять (model changed > view changed)
10
Houston, we have a solution
Структура таблицы
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean consectetuer.
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean consectetuer.
Abra
Cadabra
Caramba
12
Структура таблицы
sectionLorem ipsum dolor sit amet, consectetuer
adipiscing elit. Aenean consectetuer.
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean consectetuer.
Abra
Cadabra
Caramba
12
Структура таблицы
sectionLorem ipsum dolor sit amet, consectetuer
adipiscing elit. Aenean consectetuer.
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean consectetuer.
Abra
Cadabra
Caramba
12
Структура таблицы
sectioncellsLorem ipsum dolor sit amet, consectetuer
adipiscing elit. Aenean consectetuer.
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean consectetuer.
Abra
Cadabra
Caramba
12
Структура таблицы
sectioncells
headerTitleheaderViewheaderHeightfooterTitlefooterViewfooterHeight
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean consectetuer.
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean consectetuer.
Abra
Cadabra
Caramba
12
Структура таблицы
sectioncells
headerTitleheaderViewheaderHeightfooterTitlefooterViewfooterHeight
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean consectetuer.
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean consectetuer.
Abra
Cadabra
Caramba
12
Структура таблицы
sectioncells
cellActionsheaderTitleheaderViewheaderHeightfooterTitlefooterViewfooterHeight
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean consectetuer.
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean consectetuer.
Abra
Cadabra
Caramba
12
Мы выделили следующие типы ячеек
13
Мы выделили следующие типы ячеек
– Push
– State- Switch- Check
– Edit- Flexible edit
– Text
Добавить поле
Показывать всегда
10 мин
Email [email protected]
Сейчас онлайн
Телефон +7(800)123-666-7
12/32
13
SmartTableViewController
UITableViewController UITableViewControllerDelegate
UITableViewControllerDataSource
SmartTableViewController
TableSectionContainer
UITableViewCell
AbstractSmartTableViewCell
PushTableViewCell
AbstractStateTableViewCell
CheckTableViewCellSwitchTableViewCell
TextTableViewCell
EditTableViewCell
FlexibleEditTableViewCell
14
Какая главная особенность “smart” ячейки?
При создании ячейки действие задаётся через блок
Действия:- нажатие на ячейке- нажатие на переключателе- ввод текста в ячейке
[cell setCalledBlock:^{ NSLog(@"Hello there.."); }];
accessoryType появляется автоматически, если задан calledBlock 15
Какие вкусности есть у “smart” таблицы?
Оптимизированная работа с нотификациями NSNotification с использованием блока
[self addNotificationObserver:@"NotificationName" object:_jet usingBlock:^(NSNotification *notification){
//do smth } calledBlockImmediately:YES];
(сейчас увидим как это работает) 16
Какие вкусности есть у “smart” таблицы?
Нотификации очищаются самостоятельно, если не нужна другая логика
- (void) removeNotificationObservers{ [_notifications enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { [[NSNotificationCenter defaultCenter] removeObserver:obj]; }]; [_notifications removeAllObjects];}
- (void) dealloc{ [self removeNotificationObservers]; [_notifications release]; [super dealloc];}
нотификации храняться в словаре, следовательно один инстанс viewController не может хранить несколько нотификаций с одинаковыми именами 17
Как заполнять структуру таблицы
- (void)updateSections{ [super updateSections]; [sections addObject:[self pilotSection]]; [sections addObject:[self jetSection]]; [sections addObject:[self weaponSection]]; [self.tableView reloadData];}
18
Как заполнять структуру таблицы
- (void)updateSections{ [super updateSections];
[sections addObject:[self pilotSection]]; [sections addObject:[self jetSection]]; [sections addObject:[self weaponSection]]; [self.tableView reloadData];}
18
Как заполнять структуру таблицы
- (void)updateSections{ [super updateSections];
[sections addObject:[self pilotSection]]; [sections addObject:[self jetSection]]; [sections addObject:[self weaponSection]]; [self.tableView reloadData];}
18
Как заполнять структуру таблицы
- (void)updateSections{ [super updateSections];
[sections addObject:[self pilotSection]]; [sections addObject:[self jetSection]]; [sections addObject:[self weaponSection]]; [self.tableView reloadData];}
18
Как создавать секции
19
- (TableSectionContainer *)pilotSection{ TableSectionContainer *section = [TableSectionContainer sectionWithSectionId:pilotSectionId]; section.headerTitle = @"Pilot"; [section.cells addObject:[self pilotCell]]; [section setFooterTitle:@"pilot name and range are required"]; [section setFooterHeight:20]; return section;}
Как создавать секции
19
- (TableSectionContainer *)pilotSection{ TableSectionContainer *section = [TableSectionContainer sectionWithSectionId:pilotSectionId]; section.headerTitle = @"Pilot"; [section.cells addObject:[self pilotCell]]; [section setFooterTitle:@"pilot name and range are required"]; [section setFooterHeight:20]; return section;}
Как создавать секции
19
- (TableSectionContainer *)pilotSection{ TableSectionContainer *section = [TableSectionContainer sectionWithSectionId:pilotSectionId]; section.headerTitle = @"Pilot"; [section.cells addObject:[self pilotCell]]; [section setFooterTitle:@"pilot name and range are required"]; [section setFooterHeight:20]; return section;}
Как создавать секции
19
- (TableSectionContainer *)pilotSection{ TableSectionContainer *section = [TableSectionContainer sectionWithSectionId:pilotSectionId]; section.headerTitle = @"Pilot"; [section.cells addObject:[self pilotCell]]; [section setFooterTitle:@"pilot name and range are required"]; [section setFooterHeight:20]; return section;}
Как создавать секции
19
- (TableSectionContainer *)pilotSection{ TableSectionContainer *section = [TableSectionContainer sectionWithSectionId:pilotSectionId]; section.headerTitle = @"Pilot"; [section.cells addObject:[self pilotCell]]; [section setFooterTitle:@"pilot name and range are required"]; [section setFooterHeight:20]; return section;}
Как создавать ячейки
- (PushTableViewCell *)pilotCell{ PushTableViewCell *cell = [PushTableViewCell cellWithCellId:pilotCellId]; __block typeof(self) bself = self; [cell setCalledBlock:^{ [bself showPilotViewController]; }]; [self addNotificationObserver:@"PilotNameChanged" object:_jet.pilot usingBlock: ^(NSNotification *notification){ cell.textLabel.text = [NSString stringWithFormat:@"%@ %@", bself.jet.pilot.pilotName, [bself.jet.pilot pilotRangeDescription]]; } calledBlockImmediately:YES]; return cell;}
20
Как создавать ячейки
20
- (PushTableViewCell *)pilotCell{ PushTableViewCell *cell = [PushTableViewCell cellWithCellId:pilotCellId]; __block typeof(self) bself = self; [cell setCalledBlock:^{ [bself showPilotViewController]; }]; [self addNotificationObserver:@"PilotNameChanged" object:_jet.pilot usingBlock: ^(NSNotification *notification){ cell.textLabel.text = [NSString stringWithFormat:@"%@ %@", bself.jet.pilot.pilotName, [bself.jet.pilot pilotRangeDescription]]; } calledBlockImmediately:YES]; return cell;}
Как создавать ячейки
20
- (PushTableViewCell *)pilotCell{ PushTableViewCell *cell = [PushTableViewCell cellWithCellId:pilotCellId]; __block typeof(self) bself = self; [cell setCalledBlock:^{ [bself showPilotViewController]; }]; [self addNotificationObserver:@"PilotNameChanged" object:_jet.pilot usingBlock: ^(NSNotification *notification){ cell.textLabel.text = [NSString stringWithFormat:@"%@ %@", bself.jet.pilot.pilotName, [bself.jet.pilot pilotRangeDescription]]; } calledBlockImmediately:YES]; return cell;}
Как создавать ячейки
20
- (PushTableViewCell *)pilotCell{ PushTableViewCell *cell = [PushTableViewCell cellWithCellId:pilotCellId]; __block typeof(self) bself = self; [cell setCalledBlock:^{ [bself showPilotViewController]; }]; [self addNotificationObserver:@"PilotNameChanged" object:_jet.pilot usingBlock: ^(NSNotification *notification){ cell.textLabel.text = [NSString stringWithFormat:@"%@ %@", bself.jet.pilot.pilotName, [bself.jet.pilot pilotRangeDescription]]; } calledBlockImmediately:YES]; return cell;}
Как создавать ячейки
20
- (PushTableViewCell *)pilotCell{ PushTableViewCell *cell = [PushTableViewCell cellWithCellId:pilotCellId]; __block typeof(self) bself = self; [cell setCalledBlock:^{ [bself showPilotViewController]; }]; [self addNotificationObserver:@"PilotNameChanged" object:_jet.pilot usingBlock: ^(NSNotification *notification){ cell.textLabel.text = [NSString stringWithFormat:@"%@ %@", bself.jet.pilot.pilotName, [bself.jet.pilot pilotRangeDescription]]; } calledBlockImmediately:YES]; return cell;}
Как создавать ячейки
20
- (PushTableViewCell *)pilotCell{ PushTableViewCell *cell = [PushTableViewCell cellWithCellId:pilotCellId]; __block typeof(self) bself = self; [cell setCalledBlock:^{ [bself showPilotViewController]; }]; [self addNotificationObserver:@"PilotNameChanged" object:_jet.pilot usingBlock: ^(NSNotification *notification){ cell.textLabel.text = [NSString stringWithFormat:@"%@ %@", bself.jet.pilot.pilotName, [bself.jet.pilot pilotRangeDescription]]; } calledBlockImmediately:YES]; return cell;}
Как создавать ячейки
20
- (PushTableViewCell *)pilotCell{ PushTableViewCell *cell = [PushTableViewCell cellWithCellId:pilotCellId]; __block typeof(self) bself = self; [cell setCalledBlock:^{ [bself showPilotViewController]; }]; [self addNotificationObserver:@"PilotNameChanged" object:_jet.pilot usingBlock: ^(NSNotification *notification){ cell.textLabel.text = [NSString stringWithFormat:@"%@ %@", bself.jet.pilot.pilotName, [bself.jet.pilot pilotRangeDescription]]; } calledBlockImmediately:YES]; return cell;}
Некоторые нюансы специфичных ячеек
StateTableViewCell и её потомки Check и Switch
[cell setCalledBlock:^{ bself.jet.isOn = ! bself.jet.isOn; }];
21
Некоторые нюансы специфичных ячеек
[cell setCalledBlock:^{ bself.jet.pilotName = cell.valueTextField.text; }];
EditTableViewCell
22
Некоторые нюансы специфичных ячеек
FlexibleEditTableViewCell
typedef void(^CalledBlock)(void);typedef BOOL(^ShouldChangeCharactersInRange)(NSRange range, NSString *string);typedef BOOL(^TextFieldShouldReturnBlock)(void);typedef BOOL(^TextFieldShouldClearBlock)(void);
ShouldChangeCharactersInRange _shouldChangeCharactersInRangeBlock;TextFieldShouldReturnBlock _textFieldShouldReturnBlock;TextFieldShouldClearBlock _textFieldShouldClearBlock;CalledBlock _textFieldDidBeginEditingBlock;CalledBlock _textFieldDidEndEditingBlock;
23
Что есть ещё?
Ячейка помимо блоков может использовать классическую конструкцию delegate, selector, object1, object2
cell.delegate = self;cell.selector = @selector(doSmth:);cell.object1ToAction = obj;
24
Что есть ещё?
Ячейка может на событие вызывать viewController имя которого задано при её создании
cell.viewControllerNameForPush = @”PilotViewController”;
25
Что есть ещё?
Таблица содержит гибкий механизм обновления секций и ячеек
insertSection:atIndex:insertCell:inSection:atIndex:insertCells:inSection:atIndex:removeSection:removeCell:inSection:replaceSection:withSection:replaceCell:inSection:withCell:replaceSectionWithArrayOfSections:
(для обращения к нужной секции или ячейки используется её уникальный идентификатор, задаваемый при создании)
26