Задание 4. Решение — дополнительный пункт 6.

Screen Shot 2015-07-13 at 1.36.03 PM

Это третье и последнее продолжение решения Задания 4.
Начало находится в посте Задание 4. Решение — обязательные пункты 1 — 4. 
Первое продолжение находится в посте Задание 4. Решение — обязательные пункты 5 — 10. 
Второе продолжение находится в посте Задание 4. Решение — дополнительные пункты 1 — 5. 
Текст Задания  4 на английском языке доступен на  iTunes в пункте “Developing iOS 8 app: Programming: Project 4″.  Текст Домашнего задания на русском  языке доступен на 

Задание 4 iOS 8.pdf

Необходимо освоение Лекции 7, Лекции 9Лекции 10,  и Лекции 11
В этом посте подробно описывается выполнение дополнительного пункта 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, как и в случае с таблицей), но это двухмерная ячейка и прототип этой ячейки тоже будет двухмерным. Мы изменим некоторые настройки по сравнению с теми, которые пришли из шаблона
Screen Shot 2015-07-12 at 4.52.33 PM
В прототипе ячейки мы разместим изображение Image View и индикатор активности (activity indicator), задавая им необходимые ограничения (constraints) для системы Autolayout.
Screen Shot 2015-07-12 at 5.01.15 PM
Screen Shot 2015-07-12 at 5.08.15 PMДля ячейки, также как и в случае с таблицей, нужно задать идентификатор (reuse identifier) ячейки.
Screen Shot 2015-07-12 at 5.13.30 PM
Давайте добавим два новых класса к нашему проекту: один — для Collection View Controller и назовем его ImageCollectionViewController (superclassUICollectionViewController), другой — для прототипа ячейки Collection View Cell и назовем его ImageCollectionViewCell (superclass — UICollectionViewCell).
Screen Shot 2015-07-12 at 5.38.30 PM
Screen Shot 2015-07-12 at 5.42.43 PM
Начнем размещать вначале код для ячейки в классе  ImageCollectionViewCell. Создим outlets для  изображение Image View и индикатор активности (activity indicator)
Screen Shot 2015-07-12 at 5.56.44 PM
Мы уже имели дело с изображениями и их выборкой из «сети» в классах ImageViewController и ImageTableViewCell. Так что код очень похож на код этих классов. Public API этого класса будет imageURL: NSURL?  и кэш cache:NSCache? для кэширования изображений. Вычисляемая переменная image введена для удобства манипулирования свойством image в imageVIew и позволяет очень удобно остановить индикатор активности («колесико»).  Выборка изображения происходит либо из кэша cache, куда оно попадает при загрузке изображения из «сети» и выбрасывается по показателю “стоимости” изображения, либо заново загружается из «сети» с использованием параллельных очередей.
Screen Shot 2015-07-12 at 6.51.03 PM
Теперь рассмотрим класс  ImageCollectionViewController, который приходит с готовым кодом шаблона.
Согласно подказке № 6a для дополнительных пунктов

Шаблон, который вы получите при создании subclassUICollectionViewController, вызывает registerClass в viewDidLoad. УДАЛИТЕ ЭТУ СТРОКУ КОДА. Вместо этого вы будете устанавливать класс UICollectionViewCells на storyboard. Если вы не уничтожите этот вызов registerClass, то он переопределит все, что вы задали на storyboard, так как  viewDidLoad вызывается после того, как выполнена загрузка со storyboard.

Мы удаляем эту строку, так как мы уже провели необходимую работу на storyboard, то нам нужно только зафиксировать идентификатор для ячейки Collection View в структуре констант
Screen Shot 2015-07-12 at 10.09.52 PM
Затем мы будем использовать его в реализации методов UICollectionViewDataSource (тот же самый механизм, что и UITableViewDataSource). Но сначала мы должны определиться с public API и Моделью для нашего Collection View. Это будут разные структуры и нам понадобятся некоторые преобразования. Public API  очень простой — это двухмерный массив всех выбранных по заданному критерию твитов в коренном фрагменте
Screen Shot 2015-07-13 at 8.43.10 AM
а вот Модель для Collection Views будет представлять собой одномерный массив структур TweetMedia
Screen Shot 2015-07-13 at 8.45.18 AM
В структуру TweetMedia, конечно, должно войти изображение вместе со своим соотношением сторон ( Aspect Ratio), а также твит, которому оно принадлежит, ведь согласно 6 дополнительному пункту Задания 4 после выбора изображения мы должны показать соответствующий твит. Поэтому «приклеим» к каждому изображению твит:
Screen Shot 2015-07-13 at 8.46.31 AM
Теперь мы можем представить методы Data Source для Collection View
Screen Shot 2015-07-13 at 8.53.09 AM
В последнем методе мы видим, что public API для прототипа ячейки ImageCollectionViewController лучше сделать не просто imagURL, необходимое для загрузки изображения, а целиком структуру TweetMedia. Поэтому вернемся к классу  ImageCollectionViewCell, обслуживающему наш прототип ячейки и изменим его public API.
Screen Shot 2015-07-13 at 9.02.20 AM
Конечно, при загрузке изображения нам не нужна вся структура TweetMedia, но при «переезде» на другой View Controller для отображения соответствующего твита она нам пригодится в методе prepareForSegue.
Screen Shot 2015-07-13 at 9.25.51 AM
До сих пор наша работа с Collection View ничем не отличалась от работы с Table View. Но у Collection View есть UICollectionViewDelegateFlowLayout, который отвечает за расположение ячеек на экране. Все установки для этого делегата были произведены в самом начале нашего решения пункта 6, но я напомню как они выглядят
Screen Shot 2015-07-12 at 4.52.33 PM
Опираясь на эти установки, Collection View может выполнить показ на экране наших изображений, но нам нужна небольшая настройка наших изображений, связанная с различными значениями соотношения сторон ( Aspect Ratio), поэтому мы реализуем один метод  UICollectionViewDelegateFlowLayout
Screen Shot 2015-07-13 at 9.49.14 AM
В ImageCollectionViewController осталось выполнить преобразование структуры public API в структуру Модели для Collection View, то есть нам нужно преобразовать var tweets: [[Tweet]] в var images = [TweetMedia](). Выполняем это с помощью функций flatMap и map для массивов.
Screen Shot 2015-07-13 at 10.00.06 AM
Массив tweets — двухмерный и поддерживает твиты из различных загрузок (downloads) в отдельных подмассивах. Во-первых, мы должны превратить этот двухмерный массив в одномерный с помощью функции flatMap (начиная со Swift 1.2). Эта процедура называется «выпрямлением» (flatten) массива массивов. Отдельный твит может иметь множество изображений ( images). Для каждого твита создаем массив новых структур данных, содержащих индивидуальные изображения    (images) – или ничего. Преобразуем их с помощью функции map в новый двумерный массив, содержащий эти подмассивы и, наконец, «выпрямляем «, принимая во внимание, что есть твиты без изображений.
Осталось добавить распознаватель жестов pinch для изменения масштаба представления изображений. Это уже знакомая нам процедура: добавляем переменную scale, представляющую масштаб, добавляем распознаватель жестов в методе «жизненного» цикла viewDidLoad c указанием обработчика жеста zoom: и учитываем масштаб при задании размера изображения
Screen Shot 2015-07-13 at 10.48.40 AM
. . . . . . . . . . . .
Screen Shot 2015-07-13 at 10.53.42 AM
На этом мы закончили с ImageCollectionViewController, осталось определить его взаимодействие с другими экранными фрагментами и мы возвращаемся на storyboard.
Добавим на корневом фрагменте Search Tweeter кнопку с изображением «камеры», но сделаем это в коде RootTweetTableViewController.
Screen Shot 2015-07-13 at 11.08.45 AM
При нажатии на эту кнопку мы «переезжаем» на вновь созданный  ImageCollectionViewController, передавая ему все выбранные твиты в качестве public API.
Screen Shot 2015-07-13 at 11.11.23 AM
Обратите внимание, что segue идет не от ячейки таблицы, а от полного View Controller.
Screen Shot 2015-07-13 at 11.16.06 AM
Корневой фрагмент будет выглядеть следующим образом

Screen Shot 2015-07-14 at 9.46.53 AM

Далее от нас требуют, чтобы при выборе какого-то изображения мы «переезжали на View Controller, показывающий соответствующий твит. Чтобы не загромождать storyboard стрелками, создадим абсолютную копию Tweets from Mentions Search и segue к нему от ячейки  Collection View к Tweet from Сollection View.
Screen Shot 2015-07-13 at 11.30.15 AM
Класс  TweetTableViewController, обслуживающий в том числе и Tweet from Сollection View, нуждается в небольшом дополнении, связанным с тем, что в его public API входит не только текстовое поле searchText для поиска твитов, но и двухмерный массив твитов tweets = [[Tweet]](), который мы можем устанавливать извне, как в нашем случае с Collection Views, когда кликая на изображении, мы передаем TweetTableViewController массив твитов, состоящий из одного твита. В этом случае нам не нужно выполнять выборку данных и метод refresh(), нам нужно только разместить наши данные в массиве  tweets в Table View.
Screen Shot 2015-07-13 at 1.12.18 PM
Добавляем segue от скопированного Tweet from Сollection View к таблице меншенов Mentions. Этот segue уже есть в коде клааса  TweetTableViewController с определенным именем Show Mentions и мы его используем для идентификации этого segue.
Screen Shot 2015-07-13 at 1.21.58 PM
Запускаем приложение — получаем нужный результат.
Screen Shot 2015-07-13 at 1.36.03 PM
Screen Shot 2015-07-13 at 1.39.35 PMScreen Shot 2015-07-13 at 1.42.06 PM

Код для Swift 1.2 находится на GitHub.  Код для Swift 2.0 находится на Github.

Один комментарий к “Задание 4. Решение — дополнительный пункт 6.

  1. Первый раз использую CollectionView — благодарю вас за такой любопытный пример 🙂
    Интересное сочетание функции flatMap и map для массивов, пришлось немного попотеть чтобы разобраться.
    Жест Pinch сначала не заработал- я понял не вызывается метод sizeforitematindexpath. Оказалось, что не был принят протокол UICollectionViewDelegateFlowLayout в ImageCollectionViewController и написание самого метода (названий параметров) изменилось.
    От себя я добавил:
    — кнопку с изображением «камеры», с переходом на CollectionView также и в сцену «Tweets from Mentions Search»

Обсуждение закрыто.