Алексей Патосин "Как можно доработать UITableViewController,...

Preview:

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 steve@apple.com

Добавить поле

Сейчас онлайн

Ringo Star

5 мин

10 мин

15 мин

Полчаса

Jimmy Page

Показывать всегда

Телефон +7(800)123-666-7

Email steve@apple.com

Добавить поле

Сейчас онлайн

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 steve@apple.com

Сейчас онлайн

Телефон +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

Thank you!

alexey.patosin.rualexey@patosin.ru@gn0meavp

SmartTableViewController :http://bit.ly/N8hcMe

Recommended