Это третье и последнее продолжение решения Задания 4. Необходимо освоение Лекции 7, Лекции 9, Лекции 10, и Лекции 11.
Начало находится в посте Задание 4. Решение — обязательные пункты 1 — 4.
Первое продолжение находится в посте Задание 4. Решение — обязательные пункты 5 — 10.
Второе продолжение находится в посте Задание 4. Решение — дополнительные пункты 1 — 5.
Текст Задания 4 на английском языке доступен на iTunes в пункте “Developing iOS 8 app: Programming: Project 4″. Текст Домашнего задания на русском языке доступен на
В этом посте подробно описывается выполнение дополнительного пункта 6.
Код для Swift 1.2 находится на Github. Код для Swift 2.0 находится на Github.
Пункт 6 дополнительный
Добавьте некоторый UI элемент, который показывает новый ViewController, отображающий UICollectionView всех первых изображений (image) (или, если хотите, всех images) во всех твитах, которые удовлетворяют условиям поиска. Когда пользователь кликает на изображении (image) в этом UICollectionView, “переезжайте” (segue) на View Controller, который покажет этот твит.
Добавляем на storyboard Collection View Controller. У него есть ячейка для размещения изображений ( cell, как и в случае с таблицей), но это двухмерная ячейка и прототип этой ячейки тоже будет двухмерным. Мы изменим некоторые настройки по сравнению с теми, которые пришли из шаблона
В прототипе ячейки мы разместим изображение Image View и индикатор активности (activity indicator), задавая им необходимые ограничения (constraints) для системы Autolayout.
Для ячейки, также как и в случае с таблицей, нужно задать идентификатор (reuse identifier) ячейки.
Давайте добавим два новых класса к нашему проекту: один — для Collection View Controller и назовем его ImageCollectionViewController (superclass — UICollectionViewController), другой — для прототипа ячейки Collection View Cell и назовем его ImageCollectionViewCell (superclass — UICollectionViewCell).
Начнем размещать вначале код для ячейки в классе ImageCollectionViewCell. Создим outlets для изображение Image View и индикатор активности (activity indicator)
Мы уже имели дело с изображениями и их выборкой из «сети» в классах ImageViewController и ImageTableViewCell. Так что код очень похож на код этих классов. Public API этого класса будет imageURL: NSURL? и кэш cache:NSCache? для кэширования изображений. Вычисляемая переменная image введена для удобства манипулирования свойством image в imageVIew и позволяет очень удобно остановить индикатор активности («колесико»). Выборка изображения происходит либо из кэша cache, куда оно попадает при загрузке изображения из «сети» и выбрасывается по показателю “стоимости” изображения, либо заново загружается из «сети» с использованием параллельных очередей.
Теперь рассмотрим класс ImageCollectionViewController, который приходит с готовым кодом шаблона.
Согласно подказке № 6a для дополнительных пунктов
Шаблон, который вы получите при создании subclassUICollectionViewController, вызывает registerClass в viewDidLoad. УДАЛИТЕ ЭТУ СТРОКУ КОДА. Вместо этого вы будете устанавливать класс UICollectionViewCells на storyboard. Если вы не уничтожите этот вызов registerClass, то он переопределит все, что вы задали на storyboard, так как viewDidLoad вызывается после того, как выполнена загрузка со storyboard.
Мы удаляем эту строку, так как мы уже провели необходимую работу на storyboard, то нам нужно только зафиксировать идентификатор для ячейки Collection View в структуре констант
Затем мы будем использовать его в реализации методов UICollectionViewDataSource (тот же самый механизм, что и UITableViewDataSource). Но сначала мы должны определиться с public API и Моделью для нашего Collection View. Это будут разные структуры и нам понадобятся некоторые преобразования. Public API очень простой — это двухмерный массив всех выбранных по заданному критерию твитов в коренном фрагменте
а вот Модель для Collection Views будет представлять собой одномерный массив структур TweetMedia
В структуру TweetMedia, конечно, должно войти изображение вместе со своим соотношением сторон ( Aspect Ratio), а также твит, которому оно принадлежит, ведь согласно 6 дополнительному пункту Задания 4 после выбора изображения мы должны показать соответствующий твит. Поэтому «приклеим» к каждому изображению твит:
Теперь мы можем представить методы Data Source для Collection View
В последнем методе мы видим, что public API для прототипа ячейки ImageCollectionViewController лучше сделать не просто imagURL, необходимое для загрузки изображения, а целиком структуру TweetMedia. Поэтому вернемся к классу ImageCollectionViewCell, обслуживающему наш прототип ячейки и изменим его public API.
Конечно, при загрузке изображения нам не нужна вся структура TweetMedia, но при «переезде» на другой View Controller для отображения соответствующего твита она нам пригодится в методе prepareForSegue.
До сих пор наша работа с Collection View ничем не отличалась от работы с Table View. Но у Collection View есть UICollectionViewDelegateFlowLayout, который отвечает за расположение ячеек на экране. Все установки для этого делегата были произведены в самом начале нашего решения пункта 6, но я напомню как они выглядят
Опираясь на эти установки, Collection View может выполнить показ на экране наших изображений, но нам нужна небольшая настройка наших изображений, связанная с различными значениями соотношения сторон ( Aspect Ratio), поэтому мы реализуем один метод UICollectionViewDelegateFlowLayout
В ImageCollectionViewController осталось выполнить преобразование структуры public API в структуру Модели для Collection View, то есть нам нужно преобразовать var tweets: [[Tweet]] в var images = [TweetMedia](). Выполняем это с помощью функций flatMap и map для массивов.
Массив tweets — двухмерный и поддерживает твиты из различных загрузок (downloads) в отдельных подмассивах. Во-первых, мы должны превратить этот двухмерный массив в одномерный с помощью функции flatMap (начиная со Swift 1.2). Эта процедура называется «выпрямлением» (flatten) массива массивов. Отдельный твит может иметь множество изображений ( images). Для каждого твита создаем массив новых структур данных, содержащих индивидуальные изображения (images) – или ничего. Преобразуем их с помощью функции map в новый двумерный массив, содержащий эти подмассивы и, наконец, «выпрямляем «, принимая во внимание, что есть твиты без изображений.
Осталось добавить распознаватель жестов pinch для изменения масштаба представления изображений. Это уже знакомая нам процедура: добавляем переменную scale, представляющую масштаб, добавляем распознаватель жестов в методе «жизненного» цикла viewDidLoad c указанием обработчика жеста zoom: и учитываем масштаб при задании размера изображения
. . . . . . . . . . . .
На этом мы закончили с ImageCollectionViewController, осталось определить его взаимодействие с другими экранными фрагментами и мы возвращаемся на storyboard.
Добавим на корневом фрагменте Search Tweeter кнопку с изображением «камеры», но сделаем это в коде RootTweetTableViewController.
При нажатии на эту кнопку мы «переезжаем» на вновь созданный ImageCollectionViewController, передавая ему все выбранные твиты в качестве public API.
Обратите внимание, что segue идет не от ячейки таблицы, а от полного View Controller.
Корневой фрагмент будет выглядеть следующим образом
Далее от нас требуют, чтобы при выборе какого-то изображения мы «переезжали на View Controller, показывающий соответствующий твит. Чтобы не загромождать storyboard стрелками, создадим абсолютную копию Tweets from Mentions Search и segue к нему от ячейки Collection View к Tweet from Сollection View.
Класс TweetTableViewController, обслуживающий в том числе и Tweet from Сollection View, нуждается в небольшом дополнении, связанным с тем, что в его public API входит не только текстовое поле searchText для поиска твитов, но и двухмерный массив твитов tweets = [[Tweet]](), который мы можем устанавливать извне, как в нашем случае с Collection Views, когда кликая на изображении, мы передаем TweetTableViewController массив твитов, состоящий из одного твита. В этом случае нам не нужно выполнять выборку данных и метод refresh(), нам нужно только разместить наши данные в массиве tweets в Table View.
Добавляем segue от скопированного Tweet from Сollection View к таблице меншенов Mentions. Этот segue уже есть в коде клааса TweetTableViewController с определенным именем Show Mentions и мы его используем для идентификации этого segue.
Запускаем приложение — получаем нужный результат.
Код для Swift 1.2 находится на GitHub. Код для Swift 2.0 находится на Github.
Первый раз использую CollectionView — благодарю вас за такой любопытный пример 🙂
Интересное сочетание функции flatMap и map для массивов, пришлось немного попотеть чтобы разобраться.
Жест Pinch сначала не заработал- я понял не вызывается метод sizeforitematindexpath. Оказалось, что не был принят протокол UICollectionViewDelegateFlowLayout в ImageCollectionViewController и написание самого метода (названий параметров) изменилось.
От себя я добавил:
— кнопку с изображением «камеры», с переходом на CollectionView также и в сцену «Tweets from Mentions Search»