Задание 4. Решение — обязательные пункты 5 — 10.

Screen Shot 2015-07-14 at 8.11.31 AMЭто продолжение решение Задания 4. Начало находится в посте Задание 4. Решение — обязательные пункты 1 — 4.
Текст Задания  4 на английском языке доступен на  iTunes в пункте “Developing iOS 8 app: Programming: Project 4″.  Текст Домашнего задания на русском  языке доступен на 

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

Необходимо освоение Лекции 7, Лекции 9Лекции 10 и Лекции 11. Если вы выполните хотя бы все обязательные пункты Задания 4, то работа с TableView, segue и представлением изображения с «подкачкой» данных в параллельной не main queue очереди (concurrent queue) не будет для вас проблемой никогда.
В этом посте подробно описывается выполнение обязательных пунктов 5-10.
Код для Swift 1.2 находится на Github.  Код для Swift 2.0 находится на Github.

Пункты 5 и 6 обязательные

5. Если пользователь выбирает какой-то hashtag или user в таблице “mentions”, созданной в вышеприведенном Обязательном пункте № 2,  то вы должны куда-то “переехать”  (segue), чтобы показать результаты поиска в Twitter этого hashtag или user. Это должен быть поиск именно hashtags или  users, а не просто строки с именем hashtag или user (например, поиск  “#stanford”, а не “stanford”). View Controller, куда вы “переедите” (segue), должен работать точно также, как ваш главный View Controller, показывающий твиты  (TweetTableViewController).

6. Если пользователь кликает на “меншене” url в вашем вновь созданном View Controller, вы должны открыть этот url в Safari (смотри раздел “Подсказки”, приведенный ниже, и узнай как это сделать).

Используем подсказку №15, которая говорит

15. Когда вы кликаете на user или hashtag в вашем “mentions” MVC, вы можете “переехать” (segue) на TableViewController “список твитов” с помощью либо нормального “Showsegue либо с помощью “Unwindsegue. Вам решать, какой из них приведет к созданию лучшего пользовательского интерфейса.

Мы выбираем вариант «переезда» с помощью нормального “Show” segue от прототипа ячейки Keyword Cell к копии TweetTableViewController и задаем идентификатор segue «From Keyword». Скопированный Controller имеет заголовок Tweets.
Screen Shot 2015-07-07 at 5.42.55 PM
В классе MentionsTableViewController выполняем метод prepareForSegue
Screen Shot 2015-07-07 at 7.05.33 PM
Однако в случае с url, который также подпадает под прототипа ячейки Keyword Cell, мы должны открыть этот url в Safari, то есть предотвратить срабатывание segue. Это можно сделать с помощью метода
Screen Shot 2015-07-07 at 7.21.13 PM
Скопированный Controller Tweets должен показывать меншены. В коде класса  TweetTableViewController это предусмотрено с помощью segue с идентификатором «Show Mentions«. Поэтому давайте создадим такой segue. Достаточно придерживаться имени, указанного в коде, и у вас все заработает.
Screen Shot 2015-07-07 at 7.38.23 PM

Пункты 7 обязательный

Если пользователь кликнет на изображении (image) в вашем вновь созданном View Controller, “переезжайте” на новый MVC, который позволит пользователю прокручивать (scroll) изображение и изменять его масштаб. Когда изображение впервые появляется в MVC, оно должно быть показано в увеличенном масштабе (но со своим нормальным соотношением сторон (aspect ratio)) и так, чтобы занять как можно больше экранного пространства без “белых зазоров” вокруг изображения.

Используя подсказки №21 и 22, копируем ImageViewController из проекта «Cassini» на нашу storyboard и создаем нормальный “Show” segue от прототипа ячейки Image Cell к только что скопированному ImageViewController. Задаем идентификатор segue «Show Image«.
Screen Shot 2015-07-07 at 9.09.31 PM
В классе MentionsTableViewController выполняем метод prepareForSegue
Screen Shot 2015-07-07 at 10.09.23 PM
Согласно подказкам № 21 и 22 нам следует добавить в ImageViewController автоматическую “подгонку” (autozooming-to-fit) изображения под полный размер экрана путем изменения масштаба. Что это значит? Давайте посмотрим на рисунок.
Screen Shot 2015-07-08 at 7.20.40 PM
На вышеприведенном рисунке мы «подгоняем» вертикальный размер изображения под вертикальный размер экрана, и если при этом сохраняется соотношение сторон, то масштаб изображения увеличивается. Как подсчитать масштаб? Рассчитываем две величины:

  • масштаб по вертикали = H экрана /H изобр. ≈ 1.2
  • масштаб по горизонтали = W экрана /W изобр.≈ 0.6

Берем большее значение 1.2 делаем «подгонку» по вертикали. Это означает, что по вертикали скроллинга не будут, скроллить можно по горизонтали, если вы хотите посмотреть все изображение.
Теперь посмотрим другую ситуацию, которая может появиться при ландшафтном режиме просмотра.
Screen Shot 2015-07-08 at 7.49.43 PM
Опять рассчитываем две величины:

масштаб по вертикали = H экрана /H изобр. ≈ 0.6
масштаб по горизонтали = W экрана /W изобр.≈ 1.1

Берем большее значение 1.1 делаем «подгонку» по горизонтали.Это означает, что по горизонтали скроллинга не будут, скроллить можно по вертикали, если вы хотите посмотреть все изображение.
Как видно из рисунков, желательно иметь «подгонку» не только по масштабу, но и по центру изображения. Все это обеспечивается методом zoomScaleToFit и переменной autoZoomed
Screen Shot 2015-07-09 at 6.28.25 PM
Переменная autoZoomed устанавливается в true при установке image и тут же запускается режим автоматической «подгонки»
Screen Shot 2015-07-09 at 6.34.38 PM
Удовлетворяя подсказке № 22 Задания 4, запускаем режим автоматической «подгонки» при изменении геометрии прибора, то есть в методе viewDidLayoutSubviews «жизненного цикла» View Controller.
Screen Shot 2015-07-09 at 6.39.10 PM
Переменная  autoZoomed будет сбрасываться, как только пользователь сам начнет выполнять zooming, то есть в методе scrollViewWillBeginZooming делегата ScrollView
Screen Shot 2015-07-09 at 6.43.50 PM
Запускаем приложение, находим твит с image и кликаем на этом image.

Screen Shot 2015-07-14 at 7.12.00 AM

Все прекрасно работает.

Пункты 8 обязательный

Сохраняйте недавние 100 поисков в Twitter, которые пользователь выполнил в вашем приложении. Добавьте UITabBarController к вашему приложению с одной закладкой для поиска (то есть ваш главный UI) и второй закладкой, показывающей недавние поисковые термины, используемые для поиска в вашей таблице (они должны быть уникальны и первыми должны быть самые новые). Когда пользователь кликает на поисковом термине во второй закладке, “переезжайте” (segue) (оставаясь в той же самой закладке) куда-то, чтобы показать самые свежие твиты, соответствующие этому поисковому термину. Запомните эти недавно выполненные поиски в постоянном хранилище NSUserDefaults так, чтобы ваше приложение  не забывало их в случае повторного старта приложения.

В подсказке № 23 Задания 4 предлагается идея создания глобального “хранилища” для недавних поисковых терминов в NSUserDefaults. Мы создаем класс RecentSearches в котором использована идея работы с массивом, сохраняемым в NSUserDefaultsчерез вычисляемую переменную. Реализацию этой идеи профессор продемонстрировал в Лекции 7, рассматривая демонстрационный пример «Psychologist Popover«. В нашем случае public API для класса  RecentSearches будет массив строк searches: [String] недавнего поиска, который хранится в NSUserDefaults.
Screen Shot 2015-07-09 at 9.12.22 PM
Мы должны дополнить этот класс двумя  public методами: один из них добавляет строку поиска в массив searches, удовлетворяя требованиям уникальности и  временной актуальности добавляемой строки, другой должен уметь удалять элемент массива с определенным индексом.
Screen Shot 2015-07-09 at 9.23.59 PM
Добавлять строки поиска мы будем, когда задается строка поиска в классе TweetTableViewController и мы приступаем к ее обработке.
Screen Shot 2015-07-09 at 9.35.50 PM
Теперь мы можем использовать массив searches для отображения недавних поисковых терминов. Для этого перетягиваем Table View Controller из палитры объектов, создаем новый subclass UITableViewController, который назовем RecentTableViewController, и не забываем выставить  этот класс на  storyboard для вновь созданного Table View Controller. Эту операцию мы делали много раз, и я не буду на ней подробно останавливаться. Для таблицы недавних поисковых терминов public API является экземпляр recents созданного нами класса RecentSearches, который будет поставлять нам недавние поисковые термины виде массива  строк recents.searches.
Screen Shot 2015-07-09 at 9.49.40 PM
Как только нам становится известен массив отображаемых данных в таблице, задача отображения сводится к реализации методов DataSource, которые мы ниже представим. Но у этой таблице есть «изюминка». Дело в том, что этот массив нам никто не дает, а он постоянно обновляется в хранилище NSUserDefaults. Чтобы извлечь оттуда самые свежие данные, мы должны «перезагружать» таблицу при каждом новом появлении ее на экране. Это лучше всего делать в методе viewWillApear «жизненного цикла» View Controller.
Реализации методов DataSource опирается на массив recents.searches.
Screen Shot 2015-07-09 at 10.18.17 PM
Когда пользователь кликает на поисковом термине во второй закладке, нас просят “переезжать” (segue) (оставаясь в той же самой закладке) на Table View Controller, чтобы показать самые свежие твиты, соответствующие этому поисковому термину. Это будет тот же самый Table View Controller, на который мы «переезжали» с таблицы меншенов в обязательном пункте 5. Код для метода prepareForSeque тоже стандартный
Screen Shot 2015-07-09 at 10.26.28 PM
С учетом добавления UITabBarController, storyboard будет выглядеть как представлено на рисунках. Оказалось, что storyboard трудно уместить на одном рисунке. Поэтому она показана на двух рисунках с небольшим смещение влево.
Screen Shot 2015-07-10 at 4.12.55 PM
Screen Shot 2015-07-10 at 4.13.17 PM
Код для Swift 1.2 находится на Github.  Код для Swift 2.0 находится на Github.

Пункт 9 обязательный

Сетевые запросы никогда не должны блокировать mainthread в вашем приложении.

К счастью, мы уже позаботились об этом. Метод fetchTweets класса TwitterRequest выполняет свой handler за пределами main thread. В ImageViewController использовалась многопотоковая обработка, которая подробно объяснялась на Лекции 9. Однако загрузка миниатюры tweetProfileImageView  в пользовательском классе TweetTableViewCell для отображения твита в ячейке таблицы все еще происходит с блокировкой main queue. Добавим специальную функцию для загрузки этого изображения в параллельной очереди, как мы поступили при загрузке изображения в классе ImageViewController
Screen Shot 2015-07-10 at 1.02.25 PM
Отметим важность строки, выделенной синим цветом. Он позволяет предотвратить обработку данных, пришедших из сети и потерявших актуальность. Дело в том, что cells таблицы UITableView создаются только для видимых ячеек, и затем они используются повторно по мере того, как данные приходят на экран и уходят с экрана. Так что при скроллинге нашей таблицы твитов, данные о миниатюре могут подгрузиться, а сама ячейка уже «ушла» с экрана и нет смысла размещать ее на UI.
Эта функция используется при обновлении пользовательского интерфейса ячейки
Screen Shot 2015-07-10 at 1.13.47 PM
Код для Swift 1.2 находится на Github. Код для Swift 2.0 находится на Github.

Пункт 10 обязательный

Ваше приложение должно работать правильно как в портретном, так и в ландшафтном режимах на любом iPhone (это приложение только для iPhone).

Мы везде правильно использовали механизм Autolayout, поэтому выполнение этого пункта обеспечивается автоматически.

Задание 4. Решение — обязательные пункты 5 — 10.: 9 комментариев

  1. Пришлось разобраться и использовать TwitterKit. Мигрировал на новый Swift, кое-что изменил и всё заработало.

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