Если вы читали предыдущие уроки этой серии, вы уже должны хорошо понимать основы языка программирования Swift. Мы говорили о переменных, константах и функциях, а в предыдущем уроке мы рассмотрели основы объектно-ориентированного программирования в Swift.
В то время как игровые площадки являются отличным инструментом для игры со Swift и изучения языка, пришло время двигаться дальше и создать наш первый проект Swift в Xcode. На этом уроке мы собираемся создать основу для простого приложения, использующего Xcode и Swift. Попутно мы узнаем больше об объектно-ориентированном программировании, а также познакомимся с интеграцией Swift и Objective-C.
Предпосылки
Если вы хотите следовать за мной, убедитесь, что на вашем компьютере установлен Xcode 8.3.2 или выше. Xcode 8.3.2 доступен в магазине приложений Apple .
1. Настройка проекта
Шаг 1: выберите шаблон
Запустите Xcode 8.3.2 или выше и выберите New> Project … из меню File . В разделе iOS выберите шаблон приложения Single View и нажмите кнопку « Далее» .
Шаг 2. Настройка проекта
Назовите проект ToDo и установите Language в Swift . Убедитесь, что для устройства установлено iPhone, а флажки внизу сняты. Нажмите Далее, чтобы продолжить.
Скажите Xcode, где вы хотите сохранить свой проект, и нажмите « Создать» в правом нижнем углу, чтобы создать свой первый проект Swift.
2. Анатомия проекта
Прежде чем мы продолжим наше путешествие в Swift, давайте немного посмотрим, что Xcode создал для нас. Если вы новичок в разработке Xcode и iOS, то большая часть этого будет для вас новой. Однако, работая с проектом Swift, вы получите лучшее представление о том, как выглядят классы и структуры и как они ведут себя в Swift.
Шаблон проекта не сильно отличается от проекта, созданного с Objective-C в качестве языка программирования. Наиболее важные различия связаны с AppDelegate
и ViewController
.
Если вы работали с Objective-C в прошлом, то вы заметите, что шаблон проекта содержит меньше файлов. В нашем проекте нет файлов заголовков ( .h ) или файлов реализации ( .m ). В Swift у классов нет отдельного заголовочного файла, в котором объявлен интерфейс. Вместо этого класс объявляется и реализуется в одном файле .swift .
В настоящее время наш проект содержит два файла Swift: один для класса AppDelegate , AppDelegate.swift , а другой для класса ViewController , ViewController.swift . Проект также включает две раскадровки: Main.storyboard и LaunchScreen.storyboard . Мы будем работать с основной раскадровкой чуть позже в этом уроке. В настоящее время он содержит только сцену для класса ViewController
.
Есть несколько других файлов и папок, включенных в проект, но сейчас мы их проигнорируем. Они не играют важной роли в рамках данного урока.
3. Наследование
Первое, что мы затронем в этом уроке, — это наследование, общая парадигма объектно-ориентированного программирования. В Swift только классы могут наследовать от другого класса. Другими словами, структуры и перечисления не поддерживают наследование. Это одно из ключевых отличий между классами и структурами.
Откройте ViewController.swift, чтобы увидеть наследование в действии. Интерфейс и реализация класса ViewController
довольно ViewController
, что позволяет нам сосредоточиться на основных ViewController
.
UIKit
В верхней части ViewController.swift вы должны увидеть оператор импорта для инфраструктуры UIKit. Помните, что инфраструктура UIKit предоставляет инфраструктуру для создания функционального приложения iOS. Оператор импорта в верхней части делает эту инфраструктуру доступной для нас в ViewController.swift .
1
|
import UIKit
|
Superclass
Ниже инструкции import мы определяем новый класс с именем ViewController . Двоеточие после имени класса не соответствует типу, как мы видели ранее в этой серии. Вместо этого класс после двоеточия является суперклассом класса ViewController
. Другими словами, следующий фрагмент может быть прочитан, поскольку мы определяем класс с именем ViewController
который наследуется от UIViewController
.
1
2
3
|
class ViewController: UIViewController {
}
|
Это также объясняет наличие оператора import в верхней части ViewController.swift, поскольку класс UIViewController
определен в платформе UIKit.
Переопределение
Класс ViewController
настоящее время включает в себя два метода, но обратите внимание, что каждый метод имеет префикс с ключевым словом override
. Это указывает на то, что методы определены в суперклассе класса — или выше по дереву наследования — и переопределяются классом ViewController
.
01
02
03
04
05
06
07
08
09
10
11
|
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
|
Конструкция override
не существует в Objective-C. В Objective-C вы реализуете переопределенный метод в подклассе без явного указания, что он переопределяет метод выше дерева наследования. Objective-C исполняет все остальное.
Хотя то же самое верно и в Swift, ключевые слова override
повышают безопасность переопределения методов. Поскольку мы viewDidLoad()
префикс viewDidLoad()
к ключевому слову override
, Swift ожидает этот метод в суперклассе класса или выше в дереве наследования. Проще говоря, если вы переопределите метод, который не существует в дереве наследования, компилятор выдаст ошибку. Вы можете проверить это, неправильно viewDidLoad()
метод viewDidLoad()
как показано ниже.
4. Пользовательский интерфейс
Объявление торговых точек
Давайте добавим табличное представление к контроллеру представления, чтобы отобразить список задач. Прежде чем мы это сделаем, нам нужно создать точку для представления таблицы. Розетка — это не что иное, как свойство, которое видно и может быть установлено в Интерфейсном Разработчике. Чтобы объявить выход в классе ViewController
, мы ViewController
свойство, переменную, к @IBOutlet
.
01
02
03
04
05
06
07
08
09
10
11
12
13
|
class ViewController: UIViewController {
@IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
|
Обратите внимание, что розетка является неявно развернутой опцией. Что? Позвольте мне начать с того, что розетка всегда должна быть опционального типа. Причина проста. Каждое свойство класса ViewController
должно иметь значение после инициализации. Розетка, однако, подключается к пользовательскому интерфейсу только во время выполнения после ViewController
экземпляра ViewController
, следовательно, необязательного типа.
Подожди минуту. Выход tableView
объявлен как неявно развернутый необязательный, а не необязательный. Нет проблем. Мы можем объявить выход tableView
как необязательный, заменив восклицательный знак в приведенном выше фрагменте знаком вопроса. Это будет хорошо скомпилировано. Однако это также будет означать, что нам нужно будет явно развернуть свойство каждый раз, когда мы хотим получить доступ к значению, хранящемуся в необязательном элементе. Это быстро станет громоздким и многословным.
Вместо этого мы объявляем выход tableView
как неявно развернутый необязательный параметр, что означает, что нам не нужно явно развертывать необязательный параметр, если нам нужен доступ к его значению. Короче говоря, неявно развернутый необязательный необязательный параметр является необязательным, но мы можем получить доступ к значению, хранящемуся в необязательном элементе, как обычная переменная Важно помнить, что ваше приложение будет аварийно завершать работу, если вы попытаетесь получить доступ к его значению, если значение не было установлено. Это гоча. Однако, если розетка правильно подключена, мы можем быть уверены, что розетка настроена при первой попытке доступа к ней.
Подключение розеток
С объявленной розеткой пришло время подключить ее в Интерфейсном Разработчике. Откройте Main.storyboard и выберите контроллер представления. Выберите « Встроить»> «Контроллер навигации» в меню « Редактор» . Это устанавливает контроллер представления в качестве корневого контроллера представления контроллера навигации. Не волнуйтесь об этом сейчас.
Перетащите экземпляр UITableView
из библиотеки объектов в представление контроллера представления и добавьте необходимые ограничения макета. Выбрав табличное представление, откройте инспектор соединений и установите источник данных табличного представления и delegate
выходы контроллеру представления.
Пока инспектор подключений открыт, выберите контроллер представления и подключите выход tableView
к только что добавленному представлению таблицы. Это соединяет выход ViewController
класса ViewController
с представлением таблицы.
5. Протоколы
Прежде чем мы сможем создать и запустить приложение, нам необходимо реализовать протоколы UITableViewDataSource
и UITableViewDelegate
в классе ViewController
. Это включает в себя несколько вещей.
Шаг 1: Соответствие протоколам
Нам нужно сообщить компилятору, что класс ViewController
соответствует протоколам UITableViewDataSource
и UITableViewDelegate
. Синтаксис выглядит так же, как в Objective-C.
1
2
3
|
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
…
}
|
Протоколы, которым соответствует класс, перечислены после суперкласса, UIViewController
в вышеприведенном примере. Если у класса нет суперкласса, что не редкость в Swift, то протоколы перечисляются сразу после имени класса и двоеточия.
Шаг 2: Реализация протокола UITableViewDataSource
Поскольку протокол UITableViewDelegate
не определяет требуемые методы, сейчас мы только собираемся реализовать протокол UITableViewDataSource
. Прежде чем мы это сделаем, давайте создадим свойство переменной для хранения элементов списка дел, которые мы собираемся перечислить в табличном представлении.
1
2
3
4
5
6
7
|
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
@IBOutlet var tableView: UITableView!
var items: [String] = []
…
}
|
Мы объявляем items
свойства переменной типа [String]
и устанавливаем пустой массив []
в качестве начального значения. Это должно выглядеть знакомым к настоящему времени. Далее, давайте реализуем два обязательных метода протокола UITableViewDataSource
.
Первый необходимый метод, numberOfRows(inSection:)
, прост. Мы просто возвращаем количество элементов, хранящихся в свойстве items
.
1
2
3
|
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
|
Второй обязательный метод, cellForRow(at:)
, нуждается в объяснении. Используя синтаксис нижнего индекса, мы запрашиваем items
для элемента, который соответствует текущей строке.
01
02
03
04
05
06
07
08
09
10
11
12
|
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Fetch Item
let item = items[indexPath.row]
// Dequeue Cell
let cell = tableView.dequeueReusableCell(withIdentifier: «TableViewCell», for: indexPath)
// Configure Cell
cell.textLabel?.text = item
return cell
}
|
Затем мы запрашиваем в табличном представлении ячейку с идентификатором "TableViewCell"
, передавая путь индекса для текущей строки. Обратите внимание, что мы храним ячейку в константе, cell
. Нет необходимости объявлять cell
как переменную.
В следующей строке кода мы настраиваем ячейку табличного представления, устанавливая текст текстовой метки со значением item
. Обратите внимание, что в Swift свойство textLabel
объекта UITableViewCell
объявлено как необязательный тип, отсюда и знак вопроса. Эта строка кода может быть прочитана, если установить свойство text
текстовой метки в item
если textLabel
не равно nil
. Другими словами, свойство text
текстовой метки устанавливается, только если textLabel
не равно nil
. Это очень удобная конструкция безопасности в Swift, известная как дополнительная цепочка .
Шаг 3: Повторное использование ячейки
Перед созданием приложения нужно разобраться с двумя вещами. Во-первых, нам нужно сообщить табличному представлению, что ему нужно использовать класс UITableViewCell
для создания новых ячеек табличного представления. Мы делаем это, вызывая registerClass(_:forCellReuseIdentifier:)
, передавая класс UITableViewCell
и идентификатор повторного использования, который мы использовали ранее, "TableViewCell"
. Обновите метод viewDidLoad()
как показано ниже.
1
2
3
4
5
6
|
override func viewDidLoad() {
super.viewDidLoad()
// Register Class for Cell Reuse
tableView.register(UITableViewCell.self, forCellReuseIdentifier: «TableViewCell»)
}
|
Шаг 4: Добавление предметов
В настоящее время у нас нет элементов для отображения в виде таблицы. Это легко решить, items
свойство items
несколькими делами. Есть несколько способов сделать это. Я решил заполнить свойство items
в viewDidLoad()
как показано ниже.
1
2
3
4
5
6
7
8
9
|
override func viewDidLoad() {
super.viewDidLoad()
// Populate Items
items = [«Buy Milk», «Finish Tutorial», «Play Minecraft»]
// Register Class for Cell Reuse
tableView.register(UITableViewCell.self, forCellReuseIdentifier: «TableViewCell»)
}
|
6. Построить и запустить
Пришло время принять нашу заявку на спин. Выберите Run из меню продукта Xcode или нажмите Command-R . Если вы последовали, вы должны получить следующий результат.
Обратите внимание, что я добавил заголовок To Do вверху панели навигации. Вы можете сделать то же самое, установив свойство ViewController
экземпляра ViewController
в viewDidLoad()
.
01
02
03
04
05
06
07
08
09
10
11
12
|
override func viewDidLoad() {
super.viewDidLoad()
// Set Title
title = «To Do»
// Populate Items
items = [«Buy Milk», «Finish Tutorial», «Play Minecraft»]
// Register Class for Cell Reuse
tableView.register(UITableViewCell.self, forCellReuseIdentifier: «TableViewCell»)
}
|
Вывод
Хотя мы только что создали простое приложение, вы узнали немало новых вещей. Мы исследовали методы наследования и переопределения. Мы также рассмотрели протоколы и способы принятия протокола UITableViewDataSource
в классе ViewController
. Однако самое важное, что вы узнали, это то, как взаимодействовать с API Objective-C.
Важно понимать, что API iOS SDK написаны на Objective-C. Swift был разработан, чтобы быть совместимым с этими API. Основываясь на прошлых ошибках, Apple поняла, что Swift необходимо иметь возможность подключаться к iOS SDK без необходимости переписывать каждый API в Swift.
Комбинирование Objective-C и Swift возможно, но есть некоторые предостережения, которые мы рассмотрим более подробно по мере продвижения вперед. Из-за неустанного внимания Свифта к безопасности нам нужно время от времени преодолевать некоторые препятствия. Однако ни одно из этих препятствий не слишком велико, как мы узнаем на следующем уроке, в котором мы продолжим работу над нашим приложением.
А пока ознакомьтесь с некоторыми другими нашими курсами и учебными пособиями по разработке iOS на языке Swift!