Текст Домашнего задания на английском языке доступен на iTunes в пункте “Developing iOS 8 app: Programming: Project 3″. Текст Домашнего задания на русском языке доступен на
Для выполнения этого Задания необходимо освоение Лекции 5, Лекции 6, Лекции 7 и Лекции 8.
В подсказке 4 Задания 3 предлагается некоторая методика разработки данного приложения, но каждый волен сам выбирать, какую часть приложения создавать в первую очередь. Поэтому обязательные пункты Задания 3 могут выполняться не в строгом порядке.
Код для Swift 1.2 находится на Github. Полное решение для Swift 2.0 можно посмотреть на Github.
Пункт 1 обязательный
Вы должны начать это Задание с кода вашего Задания 2, а не с какой-то демонстрации, находящейся на сайте. Изучение создания новых MVCs и segues требует опыта, поэтому не используйте copy / paste или редактирование уже существующей storyboard, на которой уже есть segues.
Это задание выполнено.
Пункт 2 обязательный
Переименуйте класс ViewController, c которым вы работали в Задании 1 и 2 в CalculatorViewController.
Переименование нужно выполнить в 3-х местах:
1. переименовать сам файл в Навигаторе с ViewController.swift на CalculatorViewController.swift
2. переименовать сам класс в файле CalculatorViewController.swift
3. заменить класс на storyboard c ViewController на CalculatorViewController в Инспекторе Идентичности (Identity Inspector)
Пункт 3 (только кнопка) обязательный
Добавьте новую кнопку к вашему пользовательскому интерфейсу калькулятора. Если вы ее нажимаете, то вы segues (“переезжаете”) на новый MVC (его вы должны будете написать), который строит график программы program в CalculatorBrain , сформированной во время нажатия кнопки с использованием размещенной в стэке M как независимой переменной. Например, если CalculatorBrain содержит sin(M), то вы рисуете синусоиду. Последующий ввод информации в калькулятор не должен оказывать эффект на график (до тех пор, пока графическая кнопка не будет снова нажата).
В данном пункте задания нам не надо точно выстраивать пользовательский интерфейс, поэтому поместим кнопку в верхнем левом углу, отодвинув немного в сторону наш дисплей. Это потребует некоторой дополнительной работы с системой Autolayout. Создадим зазор (Trailing Space to: 0) между кнопкой «Graph» и нашем дисплеем, в котором установлено значение 0. Величина зазора равна Standard Value. Для кнопки «Graph» создадим повышенный Hugging приоритет ( равный 300), чтобы она не растягивалась по горизонтали, а позволила это сделать дисплею.
Ограничения (constraints) для дисплея также не содержат «магических» чисел.
Далее я буду добавлять новый ViewController для моего графического MVC и устанавливать на него пользовательский View.
Следуем демонстрационному примеру, приведенному Полом Хэгарти в Лекции 5, применяя все его приемы к нашей задачи.
Идем в Палитру Объектов и первым же элементом этой палитры будет View Controller. Перетягиваем его на storyboard и с помощью цепочки File > New > File > Cocoa Touch Class создаем subclass и называем его GraphViewController. Устанавливаем класс для View Controller в Инспекторе Идентичности (Identity Inspector) в Xcode.
Пункты 7 (частично), 10 обязательные
7. Как часть вашей реализации (implementation), вам нужно написать обощенный (generic) y (x) графический UIView. Другими словами, UIView, который рисует графики, должен быть сконструирован таким способом, что он является полностью независимым от калькулятора (и мог бы использоваться в других совершенно различных приложениях для рисования графика y (x) ).
10. Ваш графический View должен быть @IBDesignable и его масштаб должен быть @IBInspectable. Оси на графическом View должны появиться на storyboard в инспектируемом (inspected) масштабе.
Теперь нам нужно добавить custom (пользовательское, далее я буду использовать слово custom для обозначения “пользовательское”, так как оно короче) View на наш Graph View Сontroller.
Как я буду это делать? Я иду в Палитру Объектов и вытягиваю оттуда обобщенный (generic) UIView.
Располагаем его на экране, так, чтобы появились пунктирные голубые направляющие линии и мой View заполнил бы собой весь мой экранный фрагмент.
Далее использую опцию “Reset to Suggested Constraints” (установка предлагаемых ограничений) системы Autolayout. Ограничения установились без «магических» чисел и теперь нам нужен custom (пользовательский) subclass UIView. Давайте сделаем это с помощью меню File > New > File … и создаем subclass класса UIView. Мы назовем его GraphView.
Устанавливаем класс GraphView для пользовательского View в Инспекторе Идентичности (Identity Inspector) в Xcode.
Загружаем для этого проекта класс AxesDraw, который рисует оси графика, с сайта Stanford.
В новом классе GraphView, который является subclass UIView, рисуем пока только оси, используя contentScaleFactor данного прибора и координаты центра (graphCenter) нашего пользовательского View, а также другие свойства, которые определены ниже:
Стараемся максимально возможно вынести код за пределы функции drawRect, которая все время будет участвовать в «перерисовке»: создаем экземпляр axesDrawer класса AxesDraw c заданным цветом осей, вычисляем центр graphCenter нашего View. Свойство contentScaleFactor можно установить только в drawRect, когда будет известно на каком приборе мы запускаем наше приложение.
Специфицируем наш класс GraphView с помощью директивы @IBDesignable, и Xcode автоматически замечает это и рисует прямо на storyboard наши оси. Директивы @IBInspectable дают возможность появится свойствам scale, lineWidth, color непосредственно в Инспекторе Атрибутов. Теперь мы можем устанавливать нами изобретенные свойства прямо на storyboard.
Устанавливаем режим перерисовке нашего пользовательского View в Redraw.
Создаем с помощью CTRL-перетягивания segue типа Show с идентификатором «Show Graph» от кнопки «Graph» на Calculator View Controller до Graph View Controller.
На Graph View Controller появился заголовок, который частично закрыл наш график. Нам необходимо скорректировать сам GraphView и его установки Autolayout. Для этого «оттягиваем» верхнюю границу GraphView вниз до голубой пунктирной линии и используем Reset to Suggested Contstraints для выбранного элемента.
Выделяем наш Calculator View Controller и вставляем его в Navigation Controller c помощью меню Editor -> Embed in -> Navigation Controller
Теперь на Calculator View Controller появился заголовок, который частично закрыл наш интерфейс. Необходимо привлечь для корректировки настроек Autolayout нашего старого друга — Схему UI (Document Outline)
Теперь получим приложение, в котором дальше можно наращивать функциональные возможности Графического калькулятора
Запускаем приложение, нажимаем кнопку «Graph«, получаем график, на котором только одни только оси.
Код для этого этапа для Swift 1.2 находится на Github. Полное решение для Swift 2.0 можно посмотреть на Github.
Пункт 11 обязательный
Ваш графический View должен поддерживать следующие жесты:
Pinching (изменение масштаба, zooming, целого графика, включая оси, в / за пределами графика)
Panning (перемещение целого графика, включая оси, вслед за передвижениями пальцев по экрану)
Double-tapping (перемещение origin графика в точку, где вы дважды “тапнули”)
Будем добавлять «жесты» в GraphViewController, а функции обработки жестов будут в GraphView, так как жесты связаны с установкой его UI элементов. Но прежде создадим outlet для GraphView с помощью CTRL-перетягивания от GraphView в код.
Добавлять жесты будем в setter, в Наблюдателе Свойства (Property Observer) didSet { } нашего вновь созданного свойства graphView.
Заметим, что жесты можно добавить и на storyboard. Этот вариант рассмотрен в Лекции 6. Поместим обработки жестов scale, originMove, origin в класс GraphView, оставив их non-private.
Код для Swift 1.2 находится на GitHub. Полное решение для Swift 2.0 можно посмотреть на Github.
Пункты 7 (полностью), 8, 9
7. Как часть вашей реализации (implementation), вам нужно написать обощенный (generic) y (x) графический UIView. Другими словами, UIView, который рисует графики, должен быть сконструирован таким способом, что он является полностью независимым от калькулятора (и мог бы использоваться в других совершенно различных приложениях для рисования графика y (x) ).
8. Графический View не должен владеть (то есть запоминать) данные, представляемые графически. Он должен использовать делегирование для получения данных в случае необходимости.
9. Ваш графический калькулятор должен обладать способностью графически представлять разрывные свойства функций (то есть он должен рисовать линии только от/ к точкам, которые для фиксированного значения M program, задающая рисование графиков, оценивает как Double (то есть не nil) что соответствует .isNormal или .isZero).
Мы должны научить наш GraphView рисовать графики в отсутствии данных. Будем использовать для этого делегирование и действовать по плану, изложенному в Лекции 6.
Это план 5 шагов :
- Создаем protocol, который по существу является типом
- Добавляем public weak свойство delegate (или dataSourceDelegate), тип которого протокол
- Используем свойство delegate (или dataSourceDelegate) внутри класса, который делегирует кому-то то, что указано в протоколе
- Класс, который вызвался быть делегатом, объявляет, что подтверждает протокол и устанавливает у себя свойство delegate (или dataSourceDelegate)
- Класс-Делегат реализует методы и свойства протокола
Итак, нам необходим протокол, единственной целью которого является получение данных для рисования внутри View, потому что оно не может иметь собственные данные. Называем протокол GraphViewDataSource, и в нем будет всего одна функция зависимости y (x).
Создаем в GraphView свойство dataSource, тип которого будет протокол делегирования
Используем свойство dataSource для построения зависимости y = f(x)
GraphViewController объявляет, что он реализует протокол GraphViewDataSource и устанавливает самого себя (self) как делегата этого протокола. Затем он реализует этот протокол.
Мы задали функцию общего вида y = cos (x) *x.
запускаем приложение и получаем общий график. Жесты для изменения масштаба и перемещения начала координат работают.
Код для Swift 1.2 находится на Github. Полное решение для Swift 2.0 можно посмотреть на Github.
Пункты 3 (полностью), 4 и 6 обязательные
3. Добавьте новую кнопку к вашему пользовательскому интерфейсу калькулятора. Если вы ее нажимаете, то вы segues (“переезжаете”) на новый MVC (его вы должны будете написать), который строит график программы program в CalculatorBrain , сформированной во время нажатия кнопки с использованием размещенной в стэке M как независимой переменной. Например, если CalculatorBrain содержит sin(M), то вы рисуете синусоиду. Последующий ввод информации в калькулятор не должен оказывать эффект на график (до тех пор, пока графическая кнопка не будет снова нажата).
4. Никакому из ваших MVCs в этом Задании не разрешается появляться в non-private APICalculatorBrain.
6. В любое время, пока график находится на экране, должно быть также показано описание (description) того, что рисуется на графике, например, если строится график sin(M), то строка “sin(M)” должна показываться где-то на экране.
Моделью для графического MVC будет программа для калькулятора, которая будет передана при нажатии кнопки «Graph». В GraphViewController Модель представлена свойством program
Интерпретировать эту программу будет «локальный калькулятор» brain
При установке Модели program извне, «локальный калькулятор» brain принимает программу program и готов к использованию в методе
func y(x: CGFloat) -> CGFloat?
протокола GraphViewDataSource
Попутно устанавливается заголовок графика. С использованием принятой извне program и «локального калькулятора» brain реализуется метод протокола GraphViewDataSource
Модель program устанавливается в CalculatorViewController в методе prepareForSegue, универсальную реализацию которого предложил профессор Пол Хэгерти в Лекции 7. Универсальность в том смысле, что данный код работает как в случае, когда segue идет от кнопки «Graph» к непосредственно на Graph View Controller, так и через Navigation Controller
Запускаем приложение и набираем на калькуляторе следующую последовательность 1⏎ M⏎ ÷ cos M⏎. Не обращаем внимание на сообщение «M не установлена», так как нам важна формула нашего графика, а не расчетный результат в одной точке, и нажимаем кнопку «Graph»
Код для Swift 1.2 находится на Github. Полное решение для Swift 2.0 можно посмотреть на Github.
Пункт 5 обязательный
На iPad и в ландшафтном режиме на iPhone6+ устройствах, график должен быть (или может быть) на экране одновременно с вашим пользовательским интерфейсом существующего калькулятора (например, в Split View). На других iPhones график следует “выталкивать” (“push”) на экран через Navigation Controller.
Добавляем Split View Controller на storyboard и убираем «сопутствующие» View Controllers. Сделаем Сalculator View Controller — Master, а Graph View Controller — Detail. Добавим еще один Navigation Controller для Detail. И segue теперь у нас будет не Show, а ShowDetail (лучше уничтожить старый segue и создать новый, не забульте указать идентификатор segue «Show Graph»):
Запускаем на iPad.
Код для Swift 1.2 находится на Github. Полное решение для Swift 2.0 можно посмотреть на Github.