UIKit — это фреймворк, который вы чаще всего будете использовать при разработке приложений для iOS. Он определяет основные компоненты приложения iOS, от ярлыков и кнопок до табличных представлений и контроллеров навигации. В этой статье мы не только начнем исследование фреймворка UIKit, но также рассмотрим внутреннюю часть проекта iOS и основные строительные блоки приложений iOS.
Что такое фреймворк UIKit?
В то время как платформа Foundation определяет классы, протоколы и функции для разработки под iOS и OS X, платформа UIKit предназначена исключительно для разработки под iOS. Это эквивалент Application Kit или платформы AppKit для разработки под OS X.
Как и Foundation, UIKit определяет классы, протоколы, функции, типы данных и константы. Он также добавляет дополнительные функциональные возможности к различным NSValue
классам, таким как NSObject
, NSString
и NSValue
благодаря использованию категорий Objective-C.
Категории Objective C — удобный способ добавить дополнительные методы к существующим классам без необходимости создавать подклассы. Прочтите этот урок от Аарона Крабтри, если вы хотите узнать больше о категориях Objective-C.
Вместо того, чтобы исследовать ключевые классы UIKit, как мы это делали для платформы Foundation, мы создадим и пройдемся по новому проекту iOS и изучим классы, с которыми мы сталкиваемся. При таком подходе быстро станет ясно, в каком контексте используется класс и как каждый класс вписывается в более широкую схему приложения iOS и какую роль он играет.
Новое начало
Запустите Xcode и создайте новый проект, выбрав New> Project … в меню File . В разделе iOS слева выберите категорию приложений . Из списка шаблонов проектов выберите шаблон приложения Single View .
Шаблон приложения Single View содержит основные строительные блоки приложения для iOS и поэтому является хорошим местом для начала нашего путешествия.
Назовите проект UIKit и введите название организации и идентификатор компании. Введите префикс класса, как я объяснял ранее в этой серии , и установите « Устройства» на iPhone.
Сообщите Xcode о том, где вы хотите сохранить новый проект, и убедитесь, что проект находится под контролем версий, установив флажок « Создать репозиторий git на моем Mac» . Пересмотрите эту статью для получения дополнительной информации о контроле версий и его преимуществах.
Файлы и папки
С тех пор, как мы в последний раз создавали проект iOS с нуля, мы узнали немало нового, поэтому неплохо было бы изучить различные файлы и папки нашего нового проекта, чтобы узнать, не звонят ли они в колокол.
В Навигаторе проекта вы должны увидеть три папки в корне проекта;
- Товары
- Каркасы
- папка с именем вашего проекта, UIKit в этом примере
- папка, имя которой заканчивается тестами , в этом примере UIKitTests
Давайте посмотрим на содержимое каждой из этих папок.
Товары
Папка « Продукты » в настоящее время содержит два элемента. Первый элемент носит название нашего проекта и имеет расширение .app . Имя второго элемента оканчивается на Тесты и имеет расширение .xctest , Я не буду описывать юнит-тесты в этой серии, поэтому вы можете пока игнорировать второй пункт.
Папка «Продукты» содержит приложение или приложения, созданные проектом после компиляции исходного кода.
Вы заметили, что UIKit.app выделен красным цветом? Всякий раз, когда Xcode не может найти файл, он выделяет файл красным цветом. Поскольку проект еще не скомпилирован, Xcode еще не создал продукт.
Каркасы
Папка Frameworks содержит рамки, с которыми связан ваш проект. В предыдущей статье наш проект был связан только с фундаментом Foundation. Этот проект, однако, основан на шаблоне приложения для iOS и поэтому связан с четырьмя платформами: Foundation, UIKit, Core Graphics и XCTest.
Возможно, вы помните инфраструктуру Core Graphics из ранее этой серии . Каркас содержит интерфейсы для API-интерфейса (Quartz) 2D-рисования. Фреймворк XCTest связан с модульным тестированием, о котором я не буду рассказывать в этой серии.
В нашем проекте есть еще одно место, которое определяет, с какими фреймворками связан проект. Выберите ваш проект в Навигаторе проектов , выберите единственный элемент в разделе « Цели » слева и откройте вкладку « Фазы сборки » вверху.
Открыв ящик Link Binary with Libraries , вы получаете тот же список фреймворков, который мы видели в папке Frameworks. Связать наш проект с другой системной платформой iOS так же просто, как нажать кнопку «плюс» внизу списка и выбрать платформу из списка.
Папка проекта
Большая часть вашего времени проводится в папке проекта, которая в настоящее время содержит шесть файлов и одну папку с именем « Вспомогательные файлы» . Давайте начнем с просмотра содержимого папки «Вспомогательные файлы».
- <PROJECT> -Info.plist: этот файл, обычно называемый файлом проекта «info-dot-plist», представляет собой список свойств, который содержит различные параметры конфигурации. Большинство этих параметров также можно изменить, выбрав проект в Навигаторе проектов , выбрав цель в списке « Цели» и открыв вкладки « Общие» , « Возможности» и « Информация» .
- InfoPlist.strings: Если Info.plist содержит значения, которые необходимо локализовать, вы можете сделать это, указав их в InfoPlist.strings . Локализация приложений — это то, что мы не рассмотрим в этой серии.
- main.m: Этот файл уже должен быть знаком. Однако при создании приложений для iOS вы редко изменяете содержимое main.m. Он содержит
main
функцию приложения, с которой начинается вся магия. Даже если мы не будем изменять main.m , это важно для вашего iOS-приложения. - <PROJECT> -Prefix.pch: это предварительно скомпилированный заголовочный файл проекта, с которым мы уже сталкивались ранее в этой серии . Как следует из названия, файлы заголовков, перечисленные в предварительно скомпилированном файле заголовков, предварительно скомпилированы Xcode, что сокращает время, необходимое для компиляции вашего проекта. Фреймворки Foundation и UIKit меняются не очень часто, поэтому нет необходимости их компилировать каждый раз, когда вы компилируете свой проект. В дополнение к предварительной компиляции заголовочных файлов, перечисленных в предварительно скомпилированном заголовочном файле, каждый исходный файл в вашем проекте имеет префикс перечисленных заголовочных файлов.
Компоненты приложения
Теперь, когда мы знаем, для чего предназначены различные файлы и папки в нашем проекте, пришло время изучить различные компоненты приложения для iOS. Продолжая наше путешествие, мы столкнемся с несколькими классами, которые принадлежат UIKit. Каждый класс, принадлежащий платформе UIKit, начинается с префикса класса UI
. Помните, что базовые классы имеют префикс NS
.
Модель Модель-Вид-Контроллер
Прежде чем мы начнем исследовать инфраструктуру UIKit, я хочу поговорить о шаблоне Model-View-Controller (MVC). Шаблон MVC является одним из самых распространенных шаблонов в объектно-ориентированном программировании. Он способствует повторному использованию кода и имеет тесные связи с концепцией разделения обязанностей или интересов. Одной из основных целей шаблона MVC является отделение бизнес-логики приложения от уровня представления.
Cocoa Touch охватывает шаблон MVC, и поэтому важно понимать, что это такое и как оно работает. Другими словами, благодаря пониманию шаблона MVC будет гораздо легче понять, как различные компоненты приложения iOS работают вместе.
В паттерне MVC модель контролирует бизнес-логику приложения. Например, взаимодействие с базой данных является обязанностью модели. Представление представляет данные, которыми управляет модель, пользователю. Представление управляет пользовательским интерфейсом и пользовательским вводом.
Контроллер — это клей между моделью и видом. Хотя модель и представление не знают о существовании друг друга, контроллер знает как модель, так и представление.
Поскольку контроллер знает как о модели, так и о представлении, он часто является наименее повторно используемым компонентом приложения. Чем меньше объект знает о своей среде и объектах, с которыми он взаимодействует, тем выше его повторное использование в будущих проектах.
UIApplication
Несмотря на то, что класс UIApplication
является ключевым компонентом каждого приложения iOS, вы не будете взаимодействовать с ним очень часто, и вы редко, если вообще когда-либо будете чувствовать необходимость подкласса UIApplication
.
Когда приложение запускается, создается синглтон этого класса. Вы помните, что такое одноэлементный объект ? Это означает, что в течение времени жизни приложения создается только один экземпляр UIApplication
класса UIApplication
.
Экземпляр UIApplication
является точкой входа для взаимодействия с пользователем и отправляет события соответствующим целевым объектам. Точный смысл этого станет ясен через несколько минут, когда мы посмотрим на контроллеры представления.
В большинстве приложений iOS экземпляр UIApplication
имеет связанный с ним объект делегата. Всякий раз, когда вы создаете проект iOS, используя один из предоставленных шаблонов, XCode создаст класс делегата приложения для вас. Посмотрите на имена исходных файлов в папке проекта в Project Navigator. Первые два файла называются TSPAppDelegate .
Экземпляр этого класса является делегатом синглтона UIApplication
. Прежде чем более детально взглянуть на класс TSPAppDelegate
, нам нужно понять, что такое шаблон делегата.
Оле Бегеманн написал отличную статью о последовательности запуска типичного приложения для iOS. В этой статье он рассказывает о различных задействованных компонентах и их роли во время запуска приложения. Я настоятельно рекомендую прочитать эту статью, если вы хотите лучше понять роль класса UIApplication
а также таинственную функцию main()
в main.m.
Шаблон делегата
Шаблон делегата широко используется в Cocoa и Cocoa Touch. В следующей статье этой серии, в которой мы рассмотрим все входы и выходы табличного представления, вы обнаружите, что табличные представления в значительной степени зависят от шаблона делегата (и источника данных).
Как и шаблон MVC, делегирование является распространенным в объектно-ориентированном программировании. Шаблон делегата в Cocoa Touch, однако, немного отличается, поскольку он использует протокол делегата для определения поведения объекта делегата.
Давайте прыгнем вперед и посмотрим на таблицы. Если вы провели какое-то время с iPhone или iPad, то класс UITableView
должен быть вам знаком. Он представляет упорядоченный список данных пользователю, и он делает эту работу очень хорошо.
Как табличное представление знает, что делать, когда постукивают по строке? Это поведение включено в класс UITableView
? Конечно, нет, потому что это поведение варьируется от приложения к приложению. Если бы мы включили это поведение в класс UITableView
, его нельзя было бы использовать повторно.
Табличное представление передает эту ответственность на объект делегата. Иными словами, он делегирует эту задачу другому объекту, объекту делегата . Потратьте немного времени, чтобы проверить ссылку на класс класса UITableView
. У этого есть два свойства, названные dataSource
и delegate
. delegate
уведомляется табличным представлением всякий раз, когда пользователь касается строки. Ответственность за реагирование на нажатие пользователя возлагается на объект делегата.
Источник данных табличного представления схож с точки зрения поведения. Основное отличие состоит в том, что табличное представление запрашивает объект источника данных о данных, которые ему необходимо отобразить.
Объекты делегата и источника данных, такие как объекты delegate
табличного представления и объекты источника данных, почти всегда являются настраиваемыми классами, классами, которые вы создаете, поскольку их реализация зависит от конкретного приложения.
Прежде чем мы взглянем на класс TSPAppDelegate
, важно понять, что объект делегата соответствует протоколу делегата. В предыдущей статье мы уже узнали о протоколах в Objective-C и о том, как они определяют поведение, определяя список методов, которые должны быть внедрены его пользователями.
Делегат приложения
Теперь, когда мы знаем, что такое делегирование, пришло время изучить класс TSPAppDelegate
который Xcode создал для нас. При запуске приложения приложение создает экземпляр класса TSPAppDelegate
. Обычно вам никогда не требуется явно создавать экземпляр объекта делегата приложения.
Откройте файл TSPAppDelegate
класса TSPAppDelegate ( TSPAppDelegate.h ), чтобы проверить его содержимое. Если мы проигнорируем комментарии вверху, первая строка импортирует заголовочные файлы инфраструктуры UIKit, чтобы мы могли работать с ее классами и протоколами.
1
|
#import <UIKit/UIKit.h>
|
Следующая строка должна быть знакомой. Это начало интерфейса класса TSPAppDelegate
как обозначено директивой @interface
. Он определяет имя класса и суперкласс класса, UIResponder
.
Между угловыми скобками указаны протоколы, которым соответствует класс. Класс TSPAppDelegate
соответствует одному протоколу, протоколу UIApplicationDelegate
.
1
|
@interface TSPAppDelegate : UIResponder <UIApplicationDelegate>
|
Следующая строка — это объявление свойства, которое должно выглядеть знакомо. Свойство window
является экземпляром UIWindow
, который является подклассом UIView
. Слова в скобках, сильные и неатомарные , являются необязательными атрибутами свойства, которые определяют семантику хранения и поведение свойства. Вы можете игнорировать их сейчас.
1
|
@property (strong, nonatomic) UIWindow *window;
|
Наиболее интересной частью интерфейса класса TSPAppDelegate
является протокол UIApplicationDelegate
. Взгляните на ссылку протокола UIApplicationDelegate
для полного списка методов, которые определяет протокол.
Протокол определяет десятки методов, и я рекомендую вам изучить некоторые из них, чтобы получить представление о возможностях протокола. На данный момент наиболее интересным для нас является application:didFinishLaunchingWithOptions:
Когда экземпляр UIApplication
завершит подготовку приложения к запуску, он уведомит делегата, наш экземпляр TSPAppDelegate
, о том, что приложение завершит запуск, но пока этого не произошло.
Почему он уведомляет делегата приложения об этом событии? Экземпляр UIApplication
уведомляет своего делегата об этом событии, чтобы у него была возможность подготовиться к запуску приложения. TSPAppDelegate
к файлу реализации TSPAppDelegate
чтобы понять, что я имею в виду. Первый метод в файле реализации — это application:didFinishLaunchingWithOptions:
и он должен более или менее выглядеть так, как application:didFinishLaunchingWithOptions:
ниже.
1
2
3
|
— (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
return YES;
}
|
Метод application:didFinishLaunchingWithOptions:
предоставляет ссылку на экземпляр UIApplication
и словарь с параметрами, которые на данный момент нам не интересны. Реализация метода довольно проста. Все, что он делает — возвращает YES
чтобы сообщить экземпляру UIApplication
что приложение готово к запуску.
Раскадровка
Проект Xcode содержит еще один интересный файл, Main.storyboard . Раскадровка определяет, как будет выглядеть пользовательский интерфейс нашего приложения. По умолчанию раскадровка называется Main.storyboard . Выберите раскадровку, чтобы открыть ее.
В настоящее время раскадровка содержит один вид в центральной рабочей области. Справа от Project Navigator вы можете увидеть список элементов, которые являются объектами, которые вы видите в представлении. Верхний элемент называется View Controller Scene , который содержит один дочерний элемент, помеченный View Controller .
Объект View Controller также имеет несколько дочерних элементов, но есть один, который представляет для нас особый интерес, объект с именем View . Помните наше обсуждение паттерна MVC. Здесь вы можете увидеть шаблон MVC в действии. В данный момент модель отсутствует, но у нас есть представление, объект View и контроллер, объект View Controller .
Когда наше приложение запускается, раскадровка используется для создания пользовательского интерфейса приложения. Контроллер представления автоматически создается, как и представление контроллера представления. Объект View в раскадровке управляется контроллером представления.
Подожди минуту. Где я могу найти класс контроллера представления в раскадровке? Как я могу изменить его поведение, чтобы создать уникальное приложение? Выберите объект View Controller слева в раскадровке и откройте Identity Inspector справа.
Инспектор личных данных расскажет вам все, что вам нужно знать. Вверху, в разделе Custom Class , вы можете увидеть имя класса контроллера представления, TSPViewController
. Вы заметили, что два файла, о которых мы еще не говорили, имеют одно и то же имя? Мы рассмотрим эти файлы через несколько минут.
Контроллер представления создан для нас, потому что это начальный контроллер представления раскадровки. Это указано в раскадровке стрелкой, указывающей на раскадровку.
UIViewController
Если вы откроете TSPViewController.h , вы заметите, что класс TSPViewController
является подклассом UIViewController
. Как и TSPAppDelegate
, класс UIViewController
является подклассом UIResponder
. Контроллеры представления или их подклассы попадают в категорию контроллеров шаблона MVC. Как следует из названия, они управляют представлением, экземпляром класса UIView
, который попадает в категорию представлений шаблона MVC.
Контроллер представления управляет видом и его подвидом, как мы увидим позже. Для этого контроллеру представления необходимо знать о представлении. Другими словами, он должен иметь ссылку на представление.
Контроллер представления в раскадровке имеет ссылку на представление. Вы можете убедиться в этом, выбрав контроллер представления в раскадровке и открыв инспектор соединений справа.
В Инспекторе соединений вы должны увидеть раздел « Розетки» . Термин «аутлет» — это модное слово для свойства, которое вы можете задать в раскадровке. Наведите указатель мыши на розетку с именем view и посмотрите, как подсвечивается вид в рабочей области. Это связь между контроллером представления и представлением.
UIView
Даже если ваше приложение может иметь только один экземпляр UIWindow
, оно может иметь много представлений. Класс UIView
является важным компонентом инфраструктуры UIKit, потому что многие классы наследуют его — прямо или косвенно.
Пересмотрите Main.storyboard , выбрав его, и посмотрите на библиотеку объектов в нижней части инспектора .
Просмотрите библиотеку объектов и перетащите метку и кнопку в представление в рабочей области. Неважно, где вы размещаете их в представлении, пока они находятся в представлении контроллера представления.
Вы заметите, что два новых объекта были добавлены в раздел Объекты слева. И метка ( UILabel
), и кнопка ( UIButton
) наследуются от UIView
. Вы заметили, что объекты Label и Button имеют небольшой отступ по сравнению с объектом View ? Это указывает на то, что объекты Label и Button являются подпредставлениями объекта View . Представление может иметь одно или несколько подпредставлений, которыми оно управляет.
Как я упоминал ранее, класс UIView
является важным компонентом UIKit. Представление управляет прямоугольной областью или рамкой на экране. Он управляет содержимым области, подпредставлениями и любым взаимодействием с содержимым представления. Класс UIView
является подклассом UIResponder
. Вы узнаете намного больше о взглядах в течение этой серии.
Магазины
Давайте рассмотрим пример, иллюстрирующий связь между раскадровкой, представлением, которое она содержит, и контроллером представления. Эти три компонента важны, и я хочу убедиться, что вы понимаете, как они работают вместе.
Несколько минут назад вы добавили метку и кнопку в представление контроллера представления. Как контроллер представления узнает об этих объектах? На данный момент они не отображаются в Инспекторе соединений , но мы можем изменить это, сообщив о них контроллеру представления.
Откройте файл заголовка контроллера представления ( TPS ViewController.h ) и добавьте свойство для метки и для кнопки.
1
2
|
@property IBOutlet UILabel *myLabel;
@property IBOutlet UIButton *myButton;
|
Добавив ключевое слово IBOutlet
в объявление свойства, свойства появятся в инспекторе соединений в раскадровке, и это то, что нам нужно.
Вернитесь к раскадровке, выберите объект View Controller в View Controller Scene и откройте Инспектор соединений справа. Новые свойства теперь перечислены в списке торговых точек . Однако контроллер представления еще не установил связь между новыми свойствами и объектами в раскадровке.
Это легко исправить. Перетащите myLabel
от пустого круга слева от выхода myLabel
к метке в рабочей области. Это создаст это крайне важное соединение, чтобы контроллер представления знал о метке. Сделайте то же самое для кнопки.
Хотя мы можем изменить текст метки в раскадровке, давайте сделаем это в контроллере представления, чтобы проиллюстрировать, что контроллер представления имеет доступ к метке и кнопке в раскадровке.
Откройте файл реализации контроллера представления ( TPSViewController.m ) и viewDidLoad
метод viewDidLoad
. Измените метод viewDidLoad
чтобы отразить реализацию ниже. Комментарии были опущены для ясности.
1
2
3
4
5
|
— (void)viewDidLoad {
[super viewDidLoad];
[self.myLabel setText:@»This is an instance of UILabel.»];
}
|
Мы можем отправлять сообщения в свойство label, запрашивая у view-контроллера self
свойство myLabel
. Отправляя свойству myLabel
сообщение setText:
и передавая строковый литерал, мы обновляем текст метки.
Обратите внимание, что setText:
является аксессором или сеттером. Несмотря на то, что можно использовать точечную нотацию Objective-C (см. Ниже), я использовал более традиционный синтаксис, поскольку он лучше показывает вам, что на самом деле происходит.
1
|
self.myLabel.text = @»This is an instance of UILabel»;
|
Запустите приложение в iOS Simulator, нажав кнопку « Выполнить» в левом верхнем углу, и обратите внимание, что текст метки действительно обновлен.
действия
В этой статье мы исследовали много нового. Я хочу закончить эту часть разговора о действиях. Как и в розетке, действия — это не что иное, как методы, которые вы можете увидеть в раскадровке.
Посмотрим, как это работает. Откройте файл заголовка контроллера представления ( TPSViewController.h ) и добавьте следующее объявление метода где-нибудь в блоке интерфейса контроллера представления.
1
|
— (IBAction)changeColor:(id)sender;
|
Не смущайтесь ключевым словом IBAction
. IBAction
идентичен void
, что означает, что метод не возвращает значения. Если мы более внимательно посмотрим на объявление метода, то увидим, что он принимает один аргумент типа id
, ссылку на объект Objective-C.
Как следует из имени аргумента, аргумент метода или действия — это объект, который отправил сообщение в контроллер представления. Я объясню это более подробно всего лишь немного.
Еще раз зайдите в раскадровку, выберите объект View Controller в представлении View Controller Scene и откройте Инспектор соединений . В Инспекторе подключений появился новый раздел под названием « Полученные действия», и только что добавленное действие перечислено в этом разделе.
Перетащите из пустого круга слева от действия на кнопку в рабочей области. Должно появиться небольшое окно со списком параметров. Список содержит все возможные события, на которые может ответить кнопка. То, что нас интересует, это Touch Up Inside . Это событие срабатывает, когда пользователь касается кнопки и поднимает палец, находясь внутри кнопки.
Создайте и запустите приложение еще раз и нажмите кнопку. Приложение рухнуло и у вас? Как это случилось? Когда вы нажали кнопку, он отправил сообщение changeColor:
в контроллер представления. Хотя контроллер представления объявляет метод changeColor:
он еще не реализует этот метод.
Всякий раз, когда сообщение отправляется объекту, который не реализует соответствующий метод, возникает исключение, и приложение вылетает, если исключение не перехвачено. Вы можете проверить это, запустив приложение еще раз, нажав кнопку и проверив вывод в окне консоли.
Чтобы исправить это, нам нужно реализовать метод changeColor:
. Откройте файл реализации контроллера представления ( TPSViewController.m ) и добавьте следующую реализацию метода где-нибудь в блоке реализации.
01
02
03
04
05
06
07
08
09
10
11
12
|
— (IBAction)changeColor:(id)sender {
NSLog(@»Sender Class > %@», [sender class]);
NSLog(@»Sender Superclass > %@», [sender superclass]);
int r = arc4random() % 255;
int g = arc4random() % 255;
int b = arc4random() % 255;
UIColor *color = [UIColor colorWithRed:(r/255.0) green:(g/255.0) blue:(b/255.0) alpha:1.0];
[self.view setBackgroundColor:color];
}
|
Реализация метода changeColor:
идентична той, которую мы использовали ранее в этой серии . Однако я добавил два дополнительных вызова NSLog
к его реализации, чтобы показать вам, что отправитель сообщения — это действительно кнопка, которую мы добавили в раскадровку.
Сам метод довольно прост. Мы генерируем три случайных целых числа от 0
до 255
, передаем эти значения в colorWithRed:green:blue:alpha:
для генерации случайного цвета и обновляем цвет фона представления контроллера представления случайным образом сгенерированным цветом.
Обратите внимание, что self.view
ссылается на представление, которым управляет контроллер представления и которое мы видели ранее в раскадровке.
Создайте и запустите приложение еще раз, нажмите кнопку, и не забудьте проверить вывод в окне консоли Xcode. Вы заметите, что отправитель является экземпляром UIButton
а его суперкласс — UIControl
.
Вывод
В этой статье мы рассмотрели несколько классов UIKit-фреймворка и подробно рассмотрели различные компоненты приложения для iOS. Мы будем исследовать и работать со структурой UIKit более подробно в оставшейся части этой серии.
Если вы не до конца понимаете различные концепции и модели, то я уверен, что вы поймете, как сериал прогрессирует. Не стесняйтесь оставлять комментарии, если у вас есть вопросы по поводу этой статьи.