Статьи

iOS с нуля с Swift: первые шаги с UIKit

UIKit — это фреймворк, который вы будете использовать чаще всего при разработке приложений для iOS. Он определяет основные компоненты приложения iOS, от ярлыков и кнопок до табличных представлений и контроллеров навигации. В этой статье мы не только начнем исследование фреймворка UIKit, но также рассмотрим внутреннюю часть проекта iOS и основные строительные блоки приложений iOS.

В то время как платформа Foundation определяет классы, протоколы и функции для разработки под iOS и OS X, платформа UIKit предназначена исключительно для разработки под iOS. Это эквивалент Application Kit или платформы AppKit для разработки под OS X.

Как и Foundation, UIKit определяет классы, протоколы, функции, типы данных и константы. Он также добавляет дополнительные функциональные возможности к различным NSValue классам, таким как NSObject , NSString и NSValue благодаря использованию категорий Objective-C.

Категории Objective C — удобный способ добавить дополнительные методы к существующим классам без необходимости создавать подклассы. Они похожи на расширения Swift. Прочтите этот урок от Аарона Крабтри, если вы хотите узнать больше о категориях Objective-C.

Вместо того, чтобы исследовать ключевые классы UIKit, как мы это делали для платформы Foundation, мы собираемся создать новый проект для iOS и исследовать классы, с которыми мы сталкиваемся. При таком подходе быстро станет ясно, в каком контексте используется класс, как каждый класс вписывается в более широкую схему приложения iOS и какую роль он играет.

Запустите Xcode и создайте новый проект, выбрав New> Project … в меню File . В разделе iOS слева выберите категорию приложений . Из списка шаблонов проектов выберите шаблон приложения Single View .

Шаблон приложения Single View содержит основные строительные блоки приложения для iOS, что делает его хорошим местом для начала нашего путешествия.

Выбор шаблона проекта

Назовите проект FirstSteps и введите название и идентификатор организации. Установите язык на Swift и установите устройства на iPhone . Оставьте флажки внизу непроверенными.

Конфигурирование проекта

Сообщите Xcode, где вы хотите сохранить новый проект, и убедитесь, что проект находится под контролем версий, установив флажок « Создать репозиторий Git на моем Mac» . Пересмотрите эту статью для получения дополнительной информации о контроле версий и его преимуществах.

Сохранение проекта

С тех пор, как мы в последний раз создавали проект iOS с нуля, мы узнали немало нового, поэтому неплохо было бы изучить различные файлы и папки нашего нового проекта, чтобы узнать, не звонят ли они в колокол.

В Навигаторе проекта вы должны увидеть две папки в корне проекта:

  • Товары
  • папка с именем вашего проекта, FirstSteps в этом примере

Давайте посмотрим на содержимое каждой из этих папок.

Структура проекта

Папка « Продукты » в настоящее время содержит один элемент. Он носит название проекта и имеет расширение .app . Папка « Продукты » содержит приложение или приложения, созданные проектом после компиляции исходного кода.

Вы заметили, что FirstSteps .app выделен красным? Всякий раз, когда Xcode не может найти файл, он выделяет файл красным цветом. Не беспокойся, хотя. Поскольку проект еще не скомпилирован, Xcode еще не создал продукт.

Большая часть вашего времени проводится в папке проекта, которая в настоящее время содержит шесть файлов. Первые два файла, AppDelegate.swift и ViewController .swift , являются исходными файлами. Эти файлы содержат исходный код приложения.

Main.storyboard содержит пользовательский интерфейс приложения. Мы уже работали с раскадровками ранее в этой серии. Обратите внимание, что есть вторая раскадровка, LaunchScreen.storyboard . Эта раскадровка используется операционной системой при запуске приложения. Вместо того чтобы показывать пустое представление, операционная система использует эту раскадровку для динамического создания образа запуска, который отображается пользователю при загрузке приложения.

Info.plist , обычно называемый файлом проекта «info-dot-plist», представляет собой список свойств, который содержит различные параметры конфигурации. Большинство этих параметров также можно изменить, выбрав проект в Навигаторе проектов , выбрав цель в списке « Цели» и открыв вкладки « Общие» , « Возможности» и « Информация» .

Assets.xcassets — это специальный тип папки для хранения ресурсов вашего проекта, таких как изображения.

Теперь, когда мы знаем, для чего предназначены различные файлы и папки в нашем проекте, пришло время изучить различные компоненты приложения для iOS. Продолжая наше путешествие, мы столкнемся с несколькими классами, которые принадлежат UIKit. Каждый класс, принадлежащий платформе UIKit, начинается с префикса класса UI . Помните, что базовые классы имеют префикс NS .

Прежде чем мы начнем исследовать инфраструктуру UIKit, я хочу поговорить о шаблоне Model-View-Controller (MVC). Шаблон MVC является одним из самых распространенных шаблонов в объектно-ориентированном программировании. Он способствует повторному использованию кода и имеет тесные связи с концепцией разделения обязанностей или интересов. Одной из основных целей шаблона MVC является отделение бизнес-логики приложения от уровня представления.

Cocoa Touch охватывает шаблон MVC, что означает, что важно понимать, что это такое и как оно работает. Другими словами, благодаря пониманию шаблона MVC будет гораздо легче понять, как различные компоненты приложения iOS работают вместе.

В шаблоне MVC уровень модели контролирует бизнес-логику приложения. Например, взаимодействие с базой данных является обязанностью модельного уровня. Уровень представления представляет данные, которыми управляет уровень модели, пользователю. Уровень представления управляет пользовательским интерфейсом и пользовательским вводом.

Контроллер — это клей между слоем модели и слоем вида. В то время как уровень модели и уровень представления не знают о существовании друг друга, контроллер знает об обоих.

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

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

Когда приложение запускается, создается синглтон этого класса. Вы помните, что такое одноэлементный объект ? Это означает, что в течение времени жизни приложения создается только один экземпляр UIApplication класса UIApplication .

Экземпляр UIApplication является точкой входа для взаимодействия с пользователем и отправляет события соответствующим целевым объектам. Точный смысл этого станет ясен через несколько минут, когда мы посмотрим на контроллеры представления.

В приложениях iOS с экземпляром UIApplication связан объект делегата. Всякий раз, когда вы создаете проект iOS, используя один из предоставленных шаблонов, XCode создаст класс делегата приложения для вас. Посмотрите на имена исходных файлов в папке проекта в Project Navigator . Первый файл называется AppDelegate.swift .

Экземпляр этого класса является делегатом синглтона UIApplication . Прежде чем более детально взглянуть на класс AppDelegate , нам нужно понять, что такое шаблон делегата.

Оле Бегеманн написал отличную статью о последовательности запуска типичного приложения для iOS. В своей статье Оле рассказывает о различных задействованных компонентах и ​​их роли во время запуска приложения. Я настоятельно рекомендую прочитать эту статью, если вы хотите лучше понять роль класса UIApplication .

Шаблон делегата широко используется в Cocoa и Cocoa Touch. В будущей статье этой серии, в которой мы рассмотрим все входы и выходы табличных представлений, вы обнаружите, что табличные представления в значительной степени зависят от шаблона делегата (и источника данных).

Как и шаблон MVC, делегирование является распространенным в объектно-ориентированном программировании. Шаблон делегата в Cocoa Touch, однако, немного отличается, поскольку он использует протокол делегата для определения поведения объекта делегата.

Давайте прыгнем вперед и посмотрим на таблицы. Если вы провели какое-то время с iPhone или iPad, то класс UITableView должен быть вам знаком. Он представляет упорядоченный список данных пользователю, и он делает эту работу очень хорошо.

Как табличное представление знает, что делать, когда постукивают по строке? Это поведение включено в класс UITableView ? Конечно, нет, потому что это поведение варьируется от приложения к приложению. Если бы мы включили это поведение в класс UITableView , его нельзя было бы использовать повторно.

Табличное представление передает эту ответственность на объект делегата. Иными словами, он делегирует эту задачу другому объекту, объекту делегата . Потратьте немного времени, чтобы проверить ссылку на класс класса UITableView . У этого есть два свойства, названные dataSource и delegate . delegate уведомляется табличным представлением, когда пользователь касается строки. Ответственность за это событие касания несет объект-делегат.

Источник данных табличного представления аналогичен. Основное отличие состоит в том, что табличное представление запрашивает объект источника данных о данных, которые ему необходимо отобразить.

Объекты делегата и источника данных, такие как объекты delegate табличного представления и dataSource , почти всегда являются пользовательскими классами. В большинстве случаев разработчик должен создать и реализовать эти классы, потому что их реализация специфична для каждого приложения.

Теперь, когда мы знаем, что такое делегирование, пришло время изучить созданный для нас класс AppDelegate Xcode. При запуске приложения приложение создает экземпляр класса AppDelegate . Этот экземпляр AppDelegate затем устанавливается как делегат экземпляра UIApplication операционной системы, созданной для вашего приложения. Вы никогда явно не создаете экземпляр объекта делегата приложения.

Откройте AppDelegate.swift, чтобы проверить реализацию класса AppDelegate . Игнорируя комментарии вверху, первая строка импортирует UIKit-фреймворк, чтобы мы могли работать с его классами и протоколами.

1
import UIKit

На следующей строке мы видим что-то, что мы еще не рассмотрели, атрибут. Атрибуты в Swift начинаются с символа @ и могут рассматриваться как инструкции для компилятора. @UIApplicationMain сообщает компилятору, что AppDelegate — это класс, который должен использоваться в качестве делегата приложения. Это все, что вам нужно знать об этом атрибуте на данный момент.

1
@UIApplicationMain

Следующая строка должна выглядеть знакомо. Это начало объявления класса AppDelegate . Он указывает имя класса и родительский класс класса, UIResponder .

Он также сообщает компилятору, что AppDelegate соответствует протоколу UIApplicationDelegate . Это неудивительно, так как мы уже знаем, что AppDelegate служит делегатом приложения.

1
2
3
class AppDelegate: UIResponder, UIApplicationDelegate {
    …
}

Следующая строка является объявлением свойства для свойства window . Обратите внимание, что свойство является переменной типа UIWindow? , Класс UIWindow является подклассом UIView , базового класса для представлений на iOS.

1
var window: UIWindow?

Наиболее интересной частью интерфейса класса AppDelegate является протокол UIApplicationDelegate . Взгляните на ссылку протокола UIApplicationDelegate для полного списка методов, которые определяет протокол.

Протокол определяет десятки методов, и я рекомендую вам изучить некоторые из них, чтобы получить представление о возможностях протокола. На данный момент наиболее интересным для нас является метод application(_:didFinishLaunchingWithOptions:) .

Когда объект UIApplication завершил подготовку приложения к запуску, он уведомляет своего делегата, объект AppDelegate , о том, что приложение собирается запустить.

Почему он уведомляет делегата приложения об этом событии? Экземпляр UIApplication уведомляет своего делегата об этом событии, чтобы у него была возможность подготовиться к запуску приложения. Реализация application(_:didFinishLaunchingWithOptions:) довольно коротка, как вы можете видеть ниже.

1
2
3
4
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    // Override point for customization after application launch.
    return true
}

Он предоставляет ссылку на экземпляр UIApplication и словарь опций. Вы можете пока игнорировать словарь опций. Чтобы убедиться, что приложение запускается операционной системой, мы возвращаем true .

Проект Xcode содержит еще один интересный файл, Main.storyboard . Раскадровка определяет, как выглядит пользовательский интерфейс вашего приложения. По умолчанию раскадровка называется Main.storyboard . Выберите раскадровку, чтобы открыть ее.

Главный раскадровка проекта

В настоящее время раскадровка содержит один вид в центральной рабочей области. Справа от Project Navigator вы можете увидеть список элементов, которые являются объектами, которые вы видите в представлении. Верхний элемент View Scene Scene содержит один дочерний элемент View Controller .

Объект View Controller также имеет несколько дочерних элементов, но есть один, который представляет для нас особый интерес, объект с именем View . Помните обсуждение о шаблоне MVC. Теперь вы можете увидеть шаблон MVC в действии. В данный момент модель отсутствует, но у нас есть представление, объект View и контроллер, объект View Controller .

Когда приложение запускается, раскадровка используется для создания пользовательского интерфейса приложения. Контроллер представления автоматически создается, как и представление контроллера представления. Объект View в раскадровке управляется контроллером представления.

Подожди минуту. Где я могу найти класс контроллера представления в раскадровке? Как я могу изменить его поведение, чтобы создать уникальное приложение? Выберите объект View Controller слева и откройте Identity Inspector справа.

Класс Контроллера Представления

Инспектор личных данных расскажет вам все, что вам нужно знать. Вверху, в разделе Custom Class , вы можете увидеть имя класса контроллера представления, ViewController . Вы заметили, что файл, о котором мы еще не говорили, носит то же имя? Мы рассмотрим этот файл через несколько минут.

Контроллер представления создан для нас, потому что это начальный контроллер представления раскадровки. Это указано в раскадровке стрелкой, указывающей на сцену View Controller .

Стрелка указывает на начальный вид контроллера раскадровки

Если вы откроете ViewController.swift , вы заметите, что класс ViewController является подклассом UIViewController . Как и AppDelegate , класс UIViewController является подклассом UIResponder . Контроллеры представления или их подклассы попадают в категорию контроллеров шаблона MVC. Как следует из названия, они управляют представлением, экземпляром класса UIView , который принадлежит категории представления шаблона MVC.

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

Контроллер представления в раскадровке имеет ссылку на представление. Вы можете убедиться в этом, выбрав контроллер представления в раскадровке и открыв инспектор соединений справа.

Контроллер вида имеет ссылку на свой вид

В Инспекторе соединений вы должны увидеть раздел « Розетки» . Термин «аутлет» — это модное слово для свойства, которое вы можете установить в раскадровке. Наведите указатель мыши на розетку с именем view и посмотрите, как подсвечивается вид в рабочей области. Это связь между контроллером представления и представлением.

Даже если ваше приложение может иметь только один экземпляр UIWindow , оно может иметь много представлений. Класс UIView является важным компонентом инфраструктуры UIKit, поскольку многие классы наследуют его — прямо или косвенно.

Пересмотрите Main.storyboard , выбрав его, и посмотрите на библиотеку объектов в нижней части инспектора .

Просмотр библиотеки объектов

Просмотрите библиотеку объектов и перетащите метку и кнопку в представление в рабочей области. Неважно, где вы размещаете их в представлении, пока они находятся в представлении контроллера представления.

Добавление метки и кнопки на раскадровку

Обратите внимание, что два новых объекта были добавлены в раздел « Объекты » слева. И метка ( UILabel ), и кнопка ( UIButton ) наследуются от UIView . Вы заметили, что объекты Label и Button имеют небольшой отступ по сравнению с объектом View ? Это указывает на то, что объекты Label и Button являются подпредставлениями объекта View . Представление может иметь одно или несколько подпредставлений, которыми оно управляет.

Как я упоминал ранее, класс UIView является важным компонентом UIKit. Представление управляет прямоугольной областью или рамкой на экране. Он управляет содержимым области, подпредставлениями и любым взаимодействием с содержимым представления. Класс UIView является подклассом UIResponder . Вы узнаете намного больше о взглядах в течение этой серии.

Давайте рассмотрим пример, иллюстрирующий связь между раскадровкой, представлением, которое она содержит, и контроллером представления. Эти три компонента важны, и я хочу убедиться, что вы понимаете, как они работают вместе.

Несколько мгновений назад вы добавили метку и кнопку в представление контроллера представления. Как контроллер представления узнает об этих объектах? На данный момент они не отображаются в Инспекторе соединений , но мы можем изменить это, сообщив о них контроллеру представления. Откройте ViewController.swift и добавьте свойство для метки и одно для кнопки.

01
02
03
04
05
06
07
08
09
10
import UIKit
 
class ViewController: UIViewController {
 
    @IBOutlet var myLabel: UILabel!
    @IBOutlet var myButton: UIButton!
 
    …
 
}

При добавлении атрибута @IBOutlet к объявлению свойства свойства появляются в Инспекторе соединений в раскадровке, и это то, что нам нужно.

Вернитесь к раскадровке, выберите объект View Controller в View Controller Scene и откройте Инспектор соединений справа. Новые свойства теперь перечислены в списке торговых точек . Однако контроллер представления еще не установил связь между новыми свойствами и объектами в раскадровке.

Это легко исправить. Перетащите myLabel от пустого круга слева от выхода myLabel к метке в рабочей области. Это создаст соединение. Контроллер представления теперь знает о метке. Повторите этот шаг для кнопки.

Подключение розеток в раскадровке

Хотя мы можем изменить текст метки в раскадровке, давайте сделаем это в контроллере представления, чтобы проиллюстрировать, что контроллер представления имеет доступ к метке и кнопке в раскадровке.

Откройте ViewController.swift и найдите метод viewDidLoad() . Измените метод viewDidLoad() чтобы отразить реализацию ниже. Комментарии были опущены для ясности.

1
2
3
4
5
override func viewDidLoad() {
    super.viewDidLoad()
     
    myLabel.text = «This is an instance of a UILabel.»
}

Мы можем отправлять сообщения в свойство label, используя свойство myLabel контроллера представления. Мы можем изменить текст метки, установив свойство text метки, используя строковый литерал.

1
myLabel.text = «This is an instance of a UILabel.»

Метод viewDidLoad() автоматически вызывается, когда контроллер представления загружает свое представление. Ключевое слово override указывает, что мы переопределяем метод, определенный классом выше в дереве наследования. Класс UIViewController , родительский класс класса ViewController , реализует этот метод.

Также обратите внимание, что мы вызываем viewDidLoad() для super . Что super ? Так же, как self ссылается на текущий экземпляр, super ссылается на родительский класс, UIViewController . Другими словами, мы вызываем метод viewDidLoad() в суперклассе. Это часто важно при переопределении метода в подклассе, потому что суперкласс может выполнять важные задачи в своей собственной реализации, и мы не хотим ничего ломать.

Запустите приложение в симуляторе, нажав кнопку « Выполнить» в левом верхнем углу. Обратите внимание, что текст метки был обновлен. Не беспокойтесь о положении метки и кнопки. Это то, что мы исправим в следующем уроке.

В этой статье мы исследовали много нового. Я хочу закончить эту часть разговора о действиях. Как и розетки, действия — это не что иное, как методы, к которым вы можете получить доступ в раскадровке.

Посмотрим, как это работает. Откройте ViewController.swift и добавьте следующий метод ниже метода viewDidLoad() .

01
02
03
04
05
06
07
08
09
10
11
12
@IBAction func changeColor(sender: UIButton) {
    print(sender.classForCoder)
    print(sender.superclass)
     
    let r = CGFloat(arc4random() % 255)
    let g = CGFloat(arc4random() % 255)
    let b = CGFloat(arc4random() % 255)
     
    let color = UIColor(red: (r/255.0), green: (g/255.0), blue: (b/255.0), alpha: 1.0)
     
    view.backgroundColor = color
}

Не @IBAction атрибутом @IBAction . Этот атрибут указывает, что метод является действием и, следовательно, доступен в раскадровке. Если мы подробнее рассмотрим действие changeColor(_:) , то увидим, что оно принимает один аргумент типа UIButton .

Как следует из имени аргумента, аргумент метода или действия — это объект, который отправил сообщение в контроллер представления. Я объясню это более подробно всего лишь немного.

Еще раз зайдите в раскадровку, выберите объект View Controller в представлении View Controller Scene и откройте Инспектор соединений . В Инспекторе соединений , « Полученные действия» появился новый раздел, и только что добавленное действие перечислено в этом разделе.

Подключение действия в раскадровке

Перетащите из пустого круга слева от действия на кнопку в рабочей области. Должно появиться всплывающее меню со списком параметров. Список содержит все возможные события, на которые может ответить кнопка. То, что нас интересует, это Touch Up Inside . Это событие срабатывает, когда пользователь касается кнопки и поднимает палец в пределах кнопки.

Выбор подходящего события касания

Запустите приложение еще раз и нажмите кнопку. Вид контроллера представления должен менять цвет каждый раз, когда вы нажимаете кнопку. Мы добавили два оператора печати к changeColor(_:) . Посмотрим, как будет выглядеть вывод, когда вы нажмете кнопку.

1
2
UIButton
Optional(UIControl)

Первая строка отображает класс отправителя, экземпляр UIButton . Это доказывает, что именно кнопка вызывает этот метод на контроллере представления. Он отправляет сообщение changeColor(_:) в контроллер представления.

Вторая строка отображает суперкласс отправителя. Помните, что не у каждого класса есть суперкласс. Вот почему мы вернемся по желанию. Выходные данные говорят нам, что родительским классом UIButton является UIControl .

Сам метод довольно прост. Мы генерируем три случайных целых числа от 0 до 255 , передаем эти значения в init(red:green:blue:alpha:) для генерации случайного цвета и обновляем цвет фона представления контроллера представления случайно сгенерированным цветом. Обратите внимание, что view ссылается на представление, которым управляет контроллер представления.

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

Если у вас есть какие-либо вопросы или комментарии, вы можете оставить их в комментариях ниже или обратиться ко мне в Twitter .