Словарь в качестве datasource для UITableView
Задача
В подавляющем большинстве случаев для заполнения данными не группированной UITableView (т.е. таблицы без секций) мы используем массив — Array. Однако, для заполнения UITableView с секциями, в которой нужно будет указывать наименование секции, а в различных секциях выводить различное количество строк, нужно использовать словарь Dictionary, и при этом произвести некоторые манипуляции, чтобы можно было без проблем получать данные, необходимые от UITableViewDatasource
Пример из жизни: есть словарь, у которого ключи — это месяца года, а значения — это массивы с именами людей, у которых дни рождения в указанном месяце. Хорошо, когда для каждого месяца в словаре имеются данные по людям и это количество людей одинаковое для каждого месяца. Но может же быть, что в январе ни у кого Дня Рождения нет, и тогда, если выводить все секции и строки в таблице «в лоб», можно получить заголовок и отсутствие строк для этого заголовка. Не по фен-шую… И количество строк для каждой секции определяется количеством элементов массива имен.
Также, думаю читатель понимает, что для работы с таблицами нужно оперировать такими понятиями как indexPath. А значения номера строки и секции изменяется от 0 до 11 (это секция — для месяца) и от 0 до N (это количество имен в массиве). Если дни рождения есть только в марте, мае и августе (это третий, пятый и восьмой месяц), то количество секций буде всего лишь три и индекс у них будет от 0 до 2 включительно. Думаю понятно что индекс 2 не совпадает с порядковым номером месяца 8. И вот тут встает вопрос — как извлекать данные из словаря, если из индексация не совпадает с индексацией строк и секций.
Решение
Для решения подобной задачи мы преобразуем словарь с данными в массив и уже будем работать с этим массивом.
Входные данные:
// // proSwift.ru // var bDays = [ "Март": ["Андрей", "Сергей", "Юлия"], "Июнь": ["Дмитрий", "Роман"], "Август": ["Мария", "Павел"]]
Создадим структуру для хранения данных словаря, и добавим массив этих структур. Этот массив и будет нашим источником данных для таблицы.
// // proSwift.ru // struct Objects { //Вспомогательная структура var sectionName : String! var sectionObjects : [String]! } var objectArray = [Objects]() // Источник данных
Нужно преобразовать данные из словаря в массив источника данных. Сделать это можно, например во viewDidLoad()
// // proSwift.ru // override func viewDidLoad() { super.viewDidLoad() for (key, value) in bDays { objectArray.append(Objects(sectionName: key, sectionObjects: value)) } }
Что же мы сделали?
Для каждой пары ключ-значение их словаря bDays мы создали объект вспомогательной структуры (Object), и присвоили его свойствам sectionName и sectionObjects соответственно ключ словаря и значения по этому ключу. А затем этот объект мы добавили в массив объектов для источника данных.
Теперь количество объектов в массиве соответствует количеству секций таблицы, в свойстве sectionName хранится заголовок для заголовка секции, а в свойстве sectionObjects — массив с именами для таблицы. Количество элементов в массиве имен — количество срок для конкретной секции.
Давайте это все напишем языком Swift 3 для UITableView
// // proSwift.ru // // MARK: - Table view data source // Получим количество секций в таблице override func numberOfSections(in tableView: UITableView) -> Int { return objectArray.count } // Получим количество строк для конкретной секции override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return objectArray[section].sectionObjects.count } // Получим заголовок для секции override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { return objectArray[section].sectionName } // Получим данные для использования в ячейке override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) // Configure the cell... cell.textLabel?.text = objectArray[indexPath.section].sectionObjects[indexPath.row] return cell }
Пример
А теперь сделаем небольшой проект, который бы реализовал весь вышеуказанный механизм.
Не буду описывать сам процесс создания проекта, только отмечу, что для отображения секций в таблице нужно указать стиль этой самой таблицы как Grouped
После компиляции и запуска проект будет выглядеть так:
Ссылка проекта на GitHub
Очень интересно, а как извлечь этот же массив если он находиться в Plist-e?
Спасибо за тему для следующей статьи. В самое ближайшее время напишу и про извлечение словаря из plist файла
Как и обещал, правда с задержкой, написал статью об извлечении данных из plist-файла. Прочитать можно тут: iOS plist преобразование в словарь