iOS. Приемы программирования Нахавандипур Вандад
— (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView{
return self.dictionaryOfNumbers.allKeys.count;
}
— (NSInteger) tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section{
NSString *sectionNameInDictionary =
self.dictionaryOfNumbers.allKeys[section];
NSArray *sectionArray = self.dictionaryOfNumbers[sectionNameInDictionary];
return sectionArray.count;
}
— (UITableViewCell *) tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = nil;
cell = [tableView dequeueReusableCellWithIdentifier: CellIdentifier
forIndexPath: indexPath];
NSString *sectionNameInDictionary =
self.dictionaryOfNumbers.allKeys[indexPath.section];
NSArray *sectionArray = self.dictionaryOfNumbers[sectionNameInDictionary];
NSNumber *number = sectionArray[indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:@"%lu",
(unsigned long)[number unsignedIntegerValue]];
return cell;
}
— (NSString *) tableView:(UITableView *)tableView
h2ForHeaderInSection:(NSInteger)section{
return self.dictionaryOfNumbers.allKeys[section];
}
Навигационная кнопка связана с селектором deleteOddNumbersSection:. Этот метод нам сейчас предстоит запрограммировать. Цель метода, как видно из его названия[2], — найти раздел, соответствующий всем нечетным числам в источнике данных, найти табличный вид, а потом удалить искомый раздел и из таблицы, и из источника данных. Вот как это делается:
— (void) deleteOddNumbersSection:(id)paramSender{
/* Сначала удаляем раздел из источника данных. */
NSString *key = SectionOddNumbers;
NSInteger indexForKey = [[self.dictionaryOfNumbers allKeys]
indexOfObject: key];
if (indexForKey == NSNotFound){
NSLog(@"Could not find the section in the data source.");
return;
}
[self.dictionaryOfNumbers removeObjectForKey: key];
/* Затем удаляем раздел из табличного вида. */
NSIndexSet *sectionToDelete = [NSIndexSet indexSetWithIndex: indexForKey];
[self.tableViewNumbers deleteSections: sectionToDelete
withRowAnimation: UITableViewRowAnimationAutomatic];
/* Наконец, убираем с навигационной панели кнопку,
так как она нам больше не понадобится. */
[self.navigationItem setRightBarButtonItem: nil animated: YES];
}
Все довольно просто. Теперь, когда пользователь нажмет кнопку на навигационной панели, раздел Odd Numbers (Нечетные числа) исчезнет из табличного вида. Как видите, в процессе удаления раздела табличный вид анимируется. Это происходит потому, что мы передали анимационный тип UITableViewRowAnimationAutomatic параметру withRowAnimation: метода deleteSections: withRowAnimation: табличного вида. Теперь запустите приложение в эмуляторе iOS и выполните Debug — Toggle Slow Animations (Отладка — Включить медленную анимацию). Потом попробуйте нажать кнопку на навигационной панели и посмотрите, что происходит. Как видите, удаление сопровождается медленной анимацией (движением). Красиво, правда? Когда удаление завершится, приложение будет выглядеть, как на рис. 4.16.
Рис. 4.16. Раздел, содержащий нечетные числа, удален из табличного вида
Вы уже знаете, как удалять разделы из табличных видов. Перейдем к удалению ячеек. Мы собираемся изменить функциональность навигационной кнопки так, чтобы при ее нажатии во всех разделах табличного вида удалялись все ячейки, содержащие числовое значение больше 2. Таким образом, мы удалим все четные и нечетные числа больше 2. Итак, изменим навигационную кнопку в методе viewDidLoad контроллера вида:
— (void)viewDidLoad {
[super viewDidLoad];
self.barButtonAction =
[[UIBarButtonItem alloc]
initWithTitle:@"Delete Numbers > 2"
style: UIBarButtonItemStylePlain
target: self
action:@selector(deleteNumbersGreaterThan2:)];
[self.navigationItem setRightBarButtonItem: self.barButtonAction
animated: NO];
self.tableViewNumbers = [[UITableView alloc]
initWithFrame: self.view.frame
style: UITableViewStyleGrouped];
self.tableViewNumbers.autoresizingMask =
UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight;
self.tableViewNumbers.delegate = self;
self.tableViewNumbers.dataSource = self;
[self.view addSubview: self.tableViewNumbers];
}
На рис. 4.17 показано, как выглядит приложение при запуске в эмуляторе iPhone.
Рис. 4.17. Кнопка, удаляющая все ячейки с числами больше 2
Теперь кнопка навигационной панели связана с селектором deleteNumbersGreaterThan2:. Селектор — это метод, реализованный в контроллере вида. Но прежде, чем перейти к его программированию, определим, что этот метод должен сделать.
1. Найти оба массива с нечетными и четными числами в источнике данных и собрать индексные пути (типа NSIndexPath) чисел больше 2. Позже мы будем пользоваться этими индексными путями для удаления соответствующих ячеек в табличном виде.
2. Удалить все числа больше 2 из источника данных — как из словаря для нечетных чисел, так и из словаря для четных.
3. Удалить из табличного вида соответствующие ячейки. Индексные пути к этим ячейкам мы собрали на первом этапе.
4. Удалить кнопку с навигационной панели. Эта кнопка больше не понадобится, ведь ячейки уже удалены и из источника данных, и из табличного вида. В качестве альтернативы при желании можете просто отключить эту кнопку. Но мне кажется, что для удобства пользователя кнопку лучше просто удалить, поскольку отключенная кнопка все равно будет ему совершенно бесполезна.
— (void) deleteNumbersGreaterThan2:(id)paramSender{
NSMutableArray *arrayOfIndexPathsToDelete =
[[NSMutableArray alloc] init];
NSMutableArray *arrayOfNumberObjectsToDelete =
[[NSMutableArray alloc] init];
/* Шаг 1: собираем объекты, которые мы хотим удалить из
источника данных, а также их индексные пути. */
__block NSUInteger keyIndex = 0;
[self.dictionaryOfNumbers enumerateKeysAndObjectsUsingBlock:
^(NSString *key, NSMutableArray *object, BOOL *stop) {
[object enumerateObjectsUsingBlock:
^(NSNumber *number, NSUInteger numberIndex, BOOL *stop) {
if ([number unsignedIntegerValue] > 2){
NSIndexPath *indexPath =
[NSIndexPath indexPathForRow: numberIndex
inSection: keyIndex];
[arrayOfIndexPathsToDelete addObject: indexPath];
[arrayOfNumberObjectsToDelete addObject: number];
}
}];
keyIndex++;
}];
/* Шаг 2: удаляем объекты из источника данных. */
if ([arrayOfNumberObjectsToDelete count] > 0){
NSMutableArray *arrayOfOddNumbers =
self.dictionaryOfNumbers[SectionOddNumbers];
NSMutableArray *arrayOfEvenNumbers =
self.dictionaryOfNumbers[SectionEvenNumbers];
[arrayOfNumberObjectsToDelete enumerateObjectsUsingBlock:
^(NSNumber *numberToDelete, NSUInteger idx, BOOL *stop) {
if ([arrayOfOddNumbers indexOfObject: numberToDelete]
!= NSNotFound){
[arrayOfOddNumbers removeObject: numberToDelete];
}
if ([arrayOfEvenNumbers indexOfObject: numberToDelete]
!= NSNotFound){
[arrayOfEvenNumbers removeObject: numberToDelete];
}
}];
}
/* Шаг 3: удаляем все ячейки, соответствующие объектам. */
[self.tableViewNumbers
deleteRowsAtIndexPaths: arrayOfIndexPathsToDelete
withRowAnimation: UITableViewRowAnimationAutomatic];
[self.navigationItem setRightBarButtonItem: nil animated: YES];
}
После того как пользователь нажмет кнопку на навигационной панели, все ячейки, в которых содержатся числа больше 2, будут удалены из источника данных. Табличный вид и все приложение станут выглядеть как на рис. 4.18.
Рис. 4.18. Мы удалили все ячейки, в которых содержались числа больше 2
См. также
Раздел 1.2.
4.9. Использование UITableViewController для удобства при создании табличных видов
Постановка задачи
Требуется возможность быстро создавать табличные виды.
Решение
Используйте контроллер вида UITableViewController, который по умолчанию предоставляется с табличным контроллером вида.
Обсуждение
В инструментарии iOS SDK есть очень удобный класс UITableViewController, который предоставляется с заранее заготовленным экземпляром табличного вида. Чтобы пользоваться всеми его преимуществами, всего лишь потребуется создать новый класс, наследующий от указанного. Здесь я подробно опишу все этапы создания нового проекта Xcode, использующего табличный контроллер вида.
1. На панели меню Xcode выберите File-New-Project (Файл-Новый-Проект).
2. Убедитесь, что в левой части экрана выбрана категория iOS. Затем перейдите в подкатегорию Application (Приложение). В правой части экрана выберите шаблон Empty Application (Пустое приложение), а потом нажмите кнопку Next (Далее) (рис. 4.19).
Рис. 4.19. Создание нового пустого приложения, в котором позже будет находиться табличный контроллер
3. На следующем экране просто выберите название для вашего проекта. Кроме того, убедитесь, что вся информация у вас на экране, кроме Organization Name (Название организации) и Company Identifier (Идентификатор компании), в точности соответствует той, что приведена на рис. 4.20. Как только все будет готово, нажмите кнопку Next (Далее).
Рис. 4.20. Конфигурирование нового пустого приложения в Xcode
4. На следующем экране вам будет предложено сохранить приложение на диске. Просто сохраните приложение в месте, которое кажется вам целесообразным, и нажмите кнопку Create (Создать).
5. В Xcode выберите меню File-New-File (Файл-Новый-Файл).
6. Убедитесь, что в левой части диалогового окна категория iOS выбрана в качестве основной и при этом также выбрана подкатегория Cocoa Touch. Далее в правой части диалогового окна выберите класс Objective-C (рис. 4.21).
Рис. 4.21. Создание нового класса для табличного вида с контроллером
7. На следующем экране вам будет предложено выбрать суперкласс для нового класса. Это очень важный этап. Убедитесь, что в качестве суперкласса задан UITableViewController. Удостоверьтесь, что все остальные настройки у вас точно такие же, как и у меня на рис. 4.22. Когда все будет готово, нажмите кнопку Next (Далее).
Рис. 4.22. Задаем суперкласс для нового объекта, который станет контроллером табличного вида
8. На следующем экране вы сможете сохранить табличный контроллер вида в проекте. Сохраните его как класс ViewController и нажмите кнопку Create (Создать).
9. В файле реализации делегата вашего приложения обязательно импортируйте заголовочный файл этого контроллера вида, а затем создайте экземпляр этого класса и установите его в качестве корневого контроллера вида приложения, как показано далее:
#import «AppDelegate.h»
#import «ViewController.h»
@implementation AppDelegate
— (BOOL) application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
ViewController *controller = [[ViewController alloc]
initWithStyle: UITableViewStylePlain];
self.window = [[UIWindow alloc]
initWithFrame: [[UIScreen mainScreen] bounds]];
self.window.rootViewController = controller;
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
Теперь, если вы попытаетесь скомпилировать проект, компилятор выдаст вам следующие предупреждения[3]:
ViewController.m:47:2: Potentially incomplete method implementation.
ViewController.m:54:2: Incomplete method implementation.
Итак, необходимо иметь в виду, что компилятор выдает определенные предупреждения, об устранении которых придется позаботиться в файле реализации контроллера вида. Открыв этот файл, вы увидите, что Apple вставила в шаблон класса табличного контроллера вида макрокоманды #warning — инструкции для компилятора (именно они приводят к тому, что на экран выводятся показанные ранее предупреждения). Одно из предупреждений находится в методе numberOfSectionsInTableView:, другое — в методе tableView: numberOfRowsInSection:. Мы видим на экране предупреждения, потому что не запрограммировали логику для этих методов. Минимальная информация, необходимая табличному контроллеру вида, — это количество разделов для отображения, количество строк для отображения, а также объект ячейки, который должен отображаться в каждой из строк. Мы не видим никаких предупреждений, связанных с отсутствием реализации объекта ячейки, но только потому, что Apple по умолчанию предоставляет формальную реализацию этого метода, создающую за вас пустые ячейки.
По умолчанию контроллер табличного вида является источником данных и делегатом табличного вида. Вам не придется отдельно указывать источник данных и делегат для этого табличного вида.
Перейдем к файлу реализации табличного контроллера вида и убедимся, что у нас есть массив строк (просто для примера), которыми мы можем заполнить табличный вид:
#import «ViewController.h»
static NSString *CellIdentifier = @"Cell";
@interface ViewController ()
@property (nonatomic, strong) NSArray *allItems;
@end
@implementation ViewController
— (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle: style];
if (self) {
// Специальная инициализация
self.allItems = @[
@"Anthony Robbins",
@"Steven Paul Jobs",
@"Paul Gilbert",
@"Yngwie Malmsteen"
];
[self.tableView registerClass: [UITableViewCell class]
forCellReuseIdentifier: CellIdentifier];
}
return self;
}
— (void) viewDidLoad{
[super viewDidLoad];
}
— (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
— (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section{
return self.allItems.count;
}
— (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath{