Материал: iOS сессия ответы

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

}

func simulate() {

delegate?.dateSimulatorDidStart(self, a:a, b: b)

println("Off to dinner...")

a.Speak()

b.Speak()

println("Walking back home...")

a.TellJoke?()

b.TellJoke?()

delegate?.dateSimulatorDidEnd(self, a:a, b:b)

}

}

Файл playground с самого начала и до этого момента.

Теперь, сохраните свой файл и давайте вернемся к площадке, которую мы сохранили раньше для того, чтобы модернизировать наш класс TipCalculatorModel. Самое время вписать его в таблицу!

Таблицы, делегаты и источники данных

Сейчас, вы понимаете концепции протоколов и делегатов, и готовы к использованию таблиц (table views) вашего приложения.

Так уж получилось, что у таблиц (table views) есть свойство delegate, и вы можете установить его для класса, который подчиняется UITableViewDelegates. Это протокол с кучей опциональных методов. К примеру, есть метод, который определяет какой ряд выбран в таблице или, когда запущено ее редактирование.

Так же, таблицы имеют дополнительное свойство dataSource, которое вы так же можете установить для класса, который подчиняется UITableViewDataSource. Разница в том, что это свойство не уведомляет класс, а запрашивает данные. Например, сколько рядов в таблице.

Свойство delegate опционально, но dataSource обязательно. Что ж, начнем с того, что зададим источник данных для вашего калькулятора чаевых.

Одна из крутых штук игровых площадок в том, что вы можете прототипизировать(это слово-опционал 8]) и визуализировать элементы (к примеру UITableView). Это удобный способ посмотреть на работу элемента до того, как интегрировать его в проект.

Вновь убедитесь, что вы на площадке, с обновленным и улучшенным классом TipCalculatorModel. Добавьте этот код в самый низ файла:

// 1

import UIKit

// 2

class TestDataSource : NSObject {

// 3

let tipCalc = TipCalculatorModel(total: 33.25, taxPct: 0.06)

var possibleTips = Dictionary<Int, (tipAmt:Double, total:Double)>()

var sortedKeys:[Int] = []

// 4

override init() {

possibleTips = tipCalc.returnPossibleTips()

sortedKeys = sorted(Array(possibleTips.keys))

super.init()

}

}

Давайте все последовательно разберем.

  • Для использования таких классов UIKit как UITableView, сначала вам нужно импортировать фреймворк UIKit. Если у вас появляется ошибка, то идите в File Inspector (View\Utilities\Show File Inspector) и установите Platform на iOS.

  • Одно из требований реализации UITableViewDataSource в том, что ваш класс расширяет NSObject (непосредственно, либо через промежуточные классы).

  • Здесь инициализируем калькулятор и создаем пустой словарь возможных чаевых, а также массив отсортированных ключей. Обратите внимание, что possibleTips и sortedKeys - переменные, а не константы, потому что пользователю необходимо, чтобы значения менялись.

  • Метод init - присваиваете двум переменным исходные значения. Обратите внимание, что вы должны обозначить метод как override, так как, вы переписываете метод класса NSObject.

У вас есть база, что ж давайте подчиним наш класс протоколу UITableViewDataSource. Чтобы это сделать, мы должны прописать имя-протокол в объявлении класса:

class TestDataSource: NSObject, UITableViewDataSource {

Затем, добавьте вот этот новый метод:

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return sortedKeys.count }

Это один из двух необходимых методов, которые мы должны реализовать для подчинения протоколу UITableViewDataSource. Он запрашивает у вас количество рядов в каждой секции этой таблицы. Эта таблица, будет иметь всего одну секцию, так что вы возвращаете количество значений в sortedKeys (количество возможных процентов).

Следующим, добавим другой метод, который так же обязателен:

// 1

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

// 2

let cell = UITableViewCell(style: UITableViewCellStyle.Value2, reuseIdentifier: nil)

// 3

let tipPct = sortedKeys[indexPath.row]

// 4

let tipAmt = possibleTips[tipPct]!.tipAmt

let total = possibleTips[tipPct]!.total

// 5

cell.textLabel?.text = "\(tipPct)%:"

cell.detailTextLabel?.text = String(format:"Tip: $%0.2f, Total: $%0.2f", tipAmt, total)

return cell

}

Идем как обычно, секция за секцией:

  • Этот метод вызывается для каждой строки-таблицы. Вы должны вернуть view (элемент), который является рядом таблицы, который в свою очередь, является подклассом UITableViewCell.

  • Вы можете создать ячейки таблицы со встроенным стилем или создать свой собственный подкласс своего стиля. Если быть конкретным, здесь мы создаем ячейки со стандартным стилем UITableViewCellStyle.Value2. Мы можем сократить эту запись до .Value2, благодаря выводу типов, но мы оставим длинный вариант для большей очевидности.

  • Один из параметров этого метода indexPath, который является простой коллекцией рядов и секций для этой ячейки. Так как у нас всего одна секция, то вы используете ряд для вытаскивания подходящего процента чаевых для отображения из массива sortedKeys.

  • Далее, вы хотите создать переменную для каждого элемента в кортеже, для процентов чаевых. Вспомните из предыдущего урока что, когда вы получите доступ к элементу в словаре, у вас появится опциональное значение, так как, вполне возможно, что нет ничего в словаре для данного конкретного ключа. Тем не менее, вы уверены, что для этого ключа что-то есть. В таком случае, используйте восклицательный знак (!) для принудительного извлечения значения.

  • Встроенный UITableViewCell имеет два встроенных свойства для описания ярлыка или этикетки: textLabel, detailTextLabel. Как раз здесь, вы можете их установить и вернуть ячейку.

И наконец, вы можете протестировать ваш табличный вид приложения, добавив следующий код в конец вашего файла playground (площадки):

let testDataSource = TestDataSource()

let tableView = UITableView(frame:CGRect(x: 0, y: 0, width: 320, height: 320), style:.Plain)

tableView.dataSource = testDataSource

tableView.reloadData()

Это создает вид таблицы заданного размера, и устанавливает источник данных для нового класса. Затем, он вызывает reloadData(), чтобы обновить вид таблицы.

Наведите вашу мышь на боковую панель площадки, на последнюю строку, и нажмите на появившийся глаз. Вы должны увидеть симпатичное превью вашей таблицы, с вашими значениями чаевых!

Файл playground'а до этого момента

Мы закончили с прототипом вашего нового и улучшенного TipCalculatorModel, и новым табличным видом приложения. Теперь, самое время интегрировать ваш код в проект!

  1. Создание и отображение данных в ячейках (Creating custom table cell)

(Курсера)

Здесь, в нашем контроллере вида, у нас есть табличный вид, хорошо? Но табличное представление еще не загружается данными. Поэтому для того, чтобы все это работало должным образом, нам нужно ввести нечто, называемое протоколами и делегатами dun. Ну, табличное представление, оно должно знать, где находится источник данных, Эй, мне нужны некоторые данные, дайте их мне. Где я его возьму? А также есть и другие вещи, такие как: Эй, мне нужно показать ячейки на экране, мне нужно показать что-то на экране и заполнить его данными. Таким образом, пользователю будет на что посмотреть. Ну, сейчас он этого не делает,так что давайте сделаем это. И мы делаем это, внедряя некоторые протоколы. Поставьте запятую после UIViewController, и мы введем UItableviewDataSource и uitableviewdelegate, хорошо? Это два протокола, и хотя он говорит "источник данных" и "делегат", это протоколы, протокол Swift. И они действительно основаны на коде objective-c, который был написан давным-давно, но в Swift они переведены как протоколы. Итак, это протоколы в Swift. И мы хотим сказать, что мы будем придерживаться набора правил, и мы поговорим о протоколах позже более подробно и подробно. Так что пока просто следуйте за мной. Итак, UITableViewDataSource и UITableViewDelegate. Ладно, я сохраню этот файл здесь. Хорошо, и обычно они кричали бы на меня. Ладно, там вы идете. И поэтому, если мы нажмем этот маленький красный значок здесь, новый с xcode 9-это дополнительное предварительное заполнение функций, которые вам нужны, чтобы заставить это работать. Поэтому нажмите на эту маленькую красную стрелочку, и она скажет, что тип CategoriesVC не соответствует протоколу UITableViewDataSource. Вы хотите добавить заглушки протоколов? Так что это говорит о том, что вы не слушаете правила. Вы приняли набор правил UITableViewDataSource и UITableViewDelegate, но вы не соблюдаете эти правила. Вы их еще даже не реализовали. Итак, я собираюсь нажать кнопку Исправить, что очень здорово, и это поместило их сюда, что немного раздражает. Поэтому я командую X. И то, что мы собираемся сделать, это положить их сюда, и мы собираемся сделать некоторую очистку здесь, так как текущая версия xcode, которая является бета-версией, показывает, что это доступно, и т. д. Это может исчезнуть в будущих версиях, я бы ожидал, что это произойдет, потому что это довольно грязно и уродливо. Но избавьтесь также и от доступного, и от этого, доступного здесь. Итак, он реализовал две функции, необходимые для правильной работы. Хорошо, количество строк и разделов, так сколько же строк вы хотите показать в табличном представлении? Он должен знать это заранее. А затем cell for row index path, хорошо, cellForRowAt indexPath. И это говорит о том, что давайте создадим ячейку, многоразовую ячейку на этом конкретном пути. Итак, пункт номер десять показывает ячейку и помещает этот тип данных. Именно здесь вы фактически вводите данные в табличное представление элемента номер один, два, три, четыре и т. д. Хорошо. Так сколько же строк в этом разделе? Ну, это довольно просто, все, что нам нужно сделать, это получить целое число и то, что просто хочет знать, сколько строк представления таблицы. Мы уже знаем, что это сколько, четыре? У нас на экране четыре. Теперь мы могли бы просто сказать return four, но это обман, и это не тот способ, которым вы бы писали код, настоящий код, надежный код. Поэтому мы собираемся вернуть DataService. instance. Мы собираемся вызвать на нем эту функцию. instance. Ладно, вот эта переменная. А потом мы вызовем функцию getCategories. Итак, то, что мы делаем, - это захватываем категории. Ну, а что же категории возвращают? Ну, он возвращает массив, так что мы знаем, что получаем массив прямо здесь. Ну, это запрос на int, так как же мы получаем общее количество элементов в массиве? Простой. Вызов функции подсчета точек. Итак, то, что мы здесь делаем, мы говорим: верните общее количество элементов в этом массиве. Ладно, достаточно просто. Это все, что мы делаем, возвращая этот счет. Но как насчет этого парня? Как на самом деле захватить ячейку и поместить в нее данные? Ну, я думаю, что мы должны сначала написать некоторый код в нашей ячейке табличного представления, прежде чем мы действительно реализуем эту функцию здесь. Итак, что мы собираемся сделать, так это перейти сюда, в нашу точку зрения, в нашу ячейку категории. То, что я хочу сделать, это удалить этот набор функций, выбранный, мне это не нужно, и мне не нужно это пробуждение от zip-файла или файла наконечника прямо здесь. Мы собираемся создать здесь свою собственную функцию. Мы назовем это "обновленные просмотры", хорошо? В основном обновите представления о ячейке. И мы пройдем в категорию, какой бы ни была текущая категория, хорошо? И опять же, это еще одна причина, по которой вы должны иметь многоразовые вещи. В противном случае нам пришлось бы иметь заголовок здесь и имя изображения здесь, и мы будем передавать два разных элемента снова и снова. Где в данном случае мы передаем только один многоразовый объект, который хорошо написан кодом и защищен. Поэтому мы собираемся обновить представления. В принципе, каждый раз, когда мы хотим показать ячейку на экране, мы собираемся вызвать эту функцию updateViews и передать в категорию, и она будет обновлять представления. Итак, давайте покажем вам, как это просто здесь, хорошо? Итак, мы собираемся сказать categoryImage.image, поэтому мы собираемся установить на нем изображение, равное UIImage, и мы собираемся сделать то, что говорит image named. И все, что мы собираемся сделать, это передать имя изображения, которое мы задаем в категории. Так категория.имя_образа. Это так просто. CategoryImage, конечно, categoryTitle-это следующий,. text равен category. title. Мы сделали это так же слишком, помнишь? Наша категория здесь, у нее есть название изображения, и у нее есть название удивительно, удивительно. Все, что нам нужно сделать, это захватить то, что мы уже установили, прямо здесь, и это так просто, мы будем обновлять представления каждый раз, легко-просто. Итак, давайте теперь вернемся к нашим категориям VC и к тому, как мы можем реализовать эту функцию здесь. Позвольте мне дать ему немного больше места, чтобы вы могли видеть, над чем мы здесь работаем. Давайте закроем эту тему. Итак, здесь, в нашем табличном представлении, мы собираемся сказать: если let cell, давайте попробуем создать ячейку, равную tableView.dequeueReusableCell с идентификатором. Мы еще не создали этот идентификатор. Так что мы собираемся принять его имя. Мы будем называть это как CategoryCell. Хорошо, и мы собираемся использовать его как категориальную ячейку. Итак, мы захватываем табличное представление, которое работает с этим делегатом, и вызываем dequeueReusableCell, и это функция, которую вы вызываете, чтобы получить многоразовую ячейку. Знаете ли вы, что на табличном представлении на IOS, хорошо, что это похоже на Android. посмотрите, как видны один, два, три, четыре элемента. Знаете ли вы, что, если бы у вас была сотня предметов, они не были бы здесь спрятаны? Там будет всего пять или шесть камер вместе. И вот что происходит: он снимает тот, что находится внизу, которого больше нет на экране, и кладет его обратно наверх, чтобы захватить предмет, который все еще находится в памяти. Поэтому ему не нужно воссоздавать его заново и замедлять работу вашего приложения. И это просто, так что эй, я достал для тебя твой старый сотовый. Какие данные мы должны поместить в него, чтобы обновить его? Это все. Что? Да, он просто захватывает исходную ячейку, которую вы уже использовали, и вы обновляете ее новыми данными, вот как это работает. Итак, то, что мы здесь делаем, - это захват многоразовой ячейки. Хорошо, называется CategoryCell, который мы еще не установили. И теперь все, что нам нужно сделать, это захватить категорию для этой строки, а затем поместить ее в функцию обновления представлений. Итак, когда вы говорите "пусть категория", как вы думаете, как мы можем захватить соответствующую категорию? То, что я хочу, чтобы вы сделали, хорошо, самостоятельно, это приостановили это видео, и я хочу, чтобы вы выяснили, как захватить правильную категорию для правильной строки и сохранить ее в этой переменной. Ты это сделал? Я знаю, что некоторые из вас это делали, а некоторые нет, потому что так всегда бывает. Но для тех, кто сделал это, Поздравляю, если вы поняли это, если вы этого не сделали, это нормально, потому что мы собираемся сделать это прямо сейчас. Итак, пусть категория равна. Ладно, ты готова? Мы собираемся сказать, DataService.Инстанции.getCategories. Итак, как нам получить нужную категорию в нужном ряду? Что же нам делать? Ну, мы просто берем элемент из массива, index path. row. Ладно, тебе это действительно кажется странным? Ладно, ты такой же, как и то, что происходит, Марк, это безумный синтаксис. Когда вы преподавали массивы, вы никогда не учили нас ставить квадратные скобки после скобок, это не имеет никакого смысла для меня. Ну, это потому, что мы возвращаем массив из функции. Так что это было бы равносильно тому, чтобы сделать вот это. Я мог бы сказать Давай категорий, в порядке, равен Датасервис.инстанции.getCategories. Таким образом, он хранит массив внутри категории здесь или в массиве категорий. И тогда то, что я мог бы сделать, я мог бы просто, я мог бы сказать категории, как так хорошо. Ты такой, что теперь это начинает казаться мне знакомым, потому что это массив, и ты хватаешь индекс из этого массива. Но зачем это делать, если мы можем сделать это намного быстрее с помощью одной строки кода. Итак, мы хватаем категории, а затем мы хватаем конкретный предмет из этой категории, легко-просто, хорошо. И если это не легко, не волнуйтесь, это просто требует большой практики, хорошо. Ты будешь записывать это снова и снова, хорошо? Повторение кода, код каждый день, изучение каждый день. Давайте пойдем дальше и обновим ячейку прямо сейчас, так что cell. updateViews и она ищет категорию, поэтому мы переходим в категорию, которую только что захватили. Ладно? И это тоже выглядит очень хорошо. Теперь многие разработчики действительно испытывают искушение сделать какой-нибудь неприятный код, который идет примерно так, вместо того чтобы просто обновлять представления и позволять им управлять самим собой. Им нравится делать такие вещи. Поэтому они хотели бы сказать: cell.imageView.image равно, category.imageName.

Так что то же самое мы сделали в нашем представлении, но им нравится делать это здесь с точки зрения таблицы, и это плохая практика. Вы всегда хотите, чтобы ваши взгляды были самодостаточными, взгляды должны управлять собой. Контроллер представления должен говорить, что происходит на экране в разное время, но каждое представление, которое у вас есть, должно управлять самим собой, если оно имеет какое-либо значение или значение, каковым является ячейка представления таблицы. Поэтому никогда не делайте этого, никогда не манипулируйте ячейками табличного представления из ячейки табличного представления для пути индекса строки. Это отвратительно, и я ничему этому не учу, и если ты этому научился, то не от меня. Так что делайте это так, это гораздо лучше. Итак, что мы собираемся сделать дальше, так это вернуть эту ячейку, потому что функция tableView cell for row at index path хочет, чтобы мы вернули ячейку. В противном случае, давайте просто вернем пустую ячейку, вернем CategoryCell мы сделаем пустую ячейку вот так.

  1. Принципы работы с Core Data (Working With Core Data, Saving to Core Data, Fetching data from Core Data, Deleting items from Core Data)

Core Data — это мощный и гибкий фреймворк для хранения и управления графом вашей модели, который заслуженно занимает свое место в арсенале любого iOS-разработчика. Наверняка вы, как минимум, слышали об этом фреймворке, и не один раз, и если по каким-то причинам вы его еще не используете, — то самое время начать это делать. Так как голая теория, как правило, довольно скучна и плохо усваивается, рассматривать работу c Core Data мы будем на практическом примере, создавая приложение. Такие распространенные примеры работы с Core Data, как «Список дел» и им подобные, на мой взгляд, не слишком подходят, так как используют всего одну сущность и не используют взаимосвязи, что является существенным упрощением работы с данным фреймворком. В данной статье мы разработаем приложение, где будет использоваться несколько сущностей и взаимосвязей между ними. Предполагается, что читатель знаком с основами разработки под iOS: знает Storyboard и понимает MVC, умеет использовать базовые элементы управления. Я сам переключился на iOS недавно, поэтому, возможно, в статье есть ошибки, неточности или игнорирование best practices, просьба за это сильно не пинать, лучше аргументированно ткнуть носом, чем поможете мне и другим начинающим iOS-разработчикам. Я буду использовать Xcode 7.3.1 и iOS 9.3.2, но все должно работать и в других версиях.

Общие сведения о Core Data

Как было сказано выше, Core Data — это фреймворк для хранения и управления объектным графом вашей модели данных. Конечно, управлять и, тем более, хранить данные можно и без Core Data, но с этим фреймворком это намного приятнее и удобнее. На мой взгляд, важно понять основные компоненты и принцип работы Core Data сразу целиком. То есть кривая обучения предполагает порог входа, немного выше среднего, если так можно выразиться. Ключевыми компонентами Core Data, которые используются всегда, являются следующие:

  • managed object model (управляемая объектная модель) — фактически это ваша модель (в парадигме MVC), которая содержит все сущности, их атрибуты и взаимосвязи;

  • managed object contexts (контекст управляемого объекта) — используется для управления коллекциями объектов модели (в общем случае, может быть несколько контекстов);

  • persistent store coordinator (координатор постоянного хранилища) — посредник между хранилищем данных и контекстом, в которых эти данные используются, отвечает за хранение данных и их кэширование

Конечно, Core Data не отграничивается только этими компонентами (некоторые другие мы рассмотрим ниже), но эти три составляют основу фреймворка и очень важно понять их назначение и принцип работы. Давайте, продолжим рассмотрение Core Data на примере. Создайте новый проект на основе шаблона Single View Application и на странице выбора опций нового проекта поставьте флажок «Use Core Data». При установке данного флажка Xcode добавит в проект пустую модель данных и некоторое количество программного кода для работы с Core Data. Разумеется, можно начать использовать Core Data уже в существующем проекте: в этом случае надо самостоятельно создать модель данных и написать соответствующий программный код.

65.GET-запросы (Working with GET requests)

Методы GET и POST в HTTP и HTTPS — два самых популярных метода, используемых для передачи данных с клиента на сервер с использованием протокола HTTP (протокол передачи гипертекста). И GET, и POST могут использоваться для отправки запроса и получения ответа, но между ними существует значительная разница. Два часто используемых метода для запроса-ответа между клиентом и сервером:

GET - запрашивает данные из указанного ресурса;

POST - отправляет данных, подлежащие обработке, на указанный ресурс.

Перевод GET и POST в буквальном смысле означает получение и постобработку

GET-запрос (get (англ.) — получать) — это передача данных с помощью ссылки (в ссылке).

Перейдите по следующему адресу (это для наглядного объяснения): http://calendarin.net/calendar.php?year=2016 Обратите внимание на адресную строку браузера: calendarin.net/calendar.php?year=2016 Основной файл называется calendar.php, за ним следует вопросительный знак (?) и параметр «year» со значением «2016». Так вот, всё, что следует за вопросительным знаком, это и есть GET-запрос. Всё просто. Чтобы передать не один параметр, а несколько, то их нужно разделить амперсандом (&). Пример: calendarin.net/calendar.php?year=2016&display=work-days-and-days-off