Содержание статьи
В прошлый раз ты познакомился с созданием простых консольных приложений для Mac OS X с помощью языка Objective-C и фреймворка Cocoa. Но, конечно, гораздо лучше иметь в своих приложениях полноценный графический интерфейс. В этот раз мы поговорим о создании GUI-приложений с помощью все тех же Cocoa и Objective-C.
XCode
Если ты читал предыдущую статью или уже пробовал кодить под макось, то наверняка познакомился с XCode, стандартной средой разработки под Mac OS X от Apple. Эта среда содержит все, что тебе может потребоваться для кодинга: редактор, компилятор, отладчик и т.д. Для создания графического интерфейса там есть специальная тулза под названием Interface Builder.
Да, именно так – до XCode 4 Interface Builder существовал в виде отдельного приложения, что было довольно-таки неудобно, поэтому в четвертой версии XCode Apple интегрировала Intefrace Builder в свою IDE, и теперь ковать железо можно, не отходя от кассы. К сожалению, XCode 4 на данный момент (на момент сдачи статьи – конец сентября – прим. ред.) находится в фазе тестирования, и в свободном доступе ее нет, поэтому все сказанное ниже относится к XCode и Interface Builder третьей версии. Давай для начала попробуем создать простое GUI-приложение под макось с помощью всего этого хозяйства.
Сначала был проект
Каждый программный продукт в современном мире начинается с создания проекта для той среды, в которой он будет дальше развиваться.
Проект – это, как ты знаешь, собрание разнообразных элементов, которые используются для построения приложения: исходных файлов, файлов с описанием пользовательского интерфейса, звуков, изображений, ссылок на используемые фреймворки и библиотеки и т.д., и т.п.
Самый распространенный тип программного продукта, который создается с помощью XCode – это, конечно, приложения. Но приложения – не единственное, что ты можешь создать с помощью этой IDE. Кроме него, можно собрать, например, command-line тулзу, фреймворк, плагины, библиотеки и расширения ядра – kext'ы, а также приложения для iOS.
Попробуем проследить создание приложения в XCode от начала и до конца. Первое, что нам потребуется – это, как ты уже догадался, создать проект.
Для этого запусти XCode (если в твоей системе XCode не установлена, ты можешь безвозмездно скачать ее с сайта Apple).
Для того, чтобы создать новый проект, идем в главное меню File -> New Project. Как видишь, здесь таится множество разных типов проектов. Нам потребуется Cocoa Application – оконное приложение, использующее фреймворк Cocoa.
После того, как ты выберешь шаблон для проекта и место его расположения, XCode создаст файлы проекта и даже напишет за тебя пару строчек кода.
Вот они:
Точка входа нашего приложения
#import <Cocoa/Cocoa.h>
int main(int argc, char * argv[])
{
return NSAppliction(argc, (const char**) argv);
}
Нам здесь интересен вызов функции NSApplication. После передачи управления этой функции приложение входит в цикл обработки сообщений от системы. Такое «пассивное» поведение тулзы (ожидание оповещения от системы) – стандарт для современных многозадачных систем.
Если ты попробуешь собрать и запустить этот проект (Build -> Build and Go), то увидишь пустое окошко. Откуда оно взялось? Ведь никакого кода для его создания мы не писали? Посмотри на файлы нашего проекта.
Среди них в смарт-группе NIB Files (все файлы приложения разделены на так называемые смарт-группы. NIB Files – это файлы того самого Interface Builder’a) есть MainMenu.xib. Открываем его двойным кликом и попадаем в Interface Builder, где видим то самое окошко и еще главное меню нашего приложения, которое при запуске приложения в Mac OS X находится вверху экрана.
ОК. Теперь поработаем немного в IB и накидаем на эту форму контролов.
Строим интерфейс
Давай разберемся, как изменить внешний вид главного окна нашей программы. Идем в Tools -> Library (заметь, что теперь главное меню изменилось, так как мы находимся не в XCode, а в Interface Builder’е) и видим всевозможные контролы, которые ты можешь разместить на своей форме простым dran-and-drop'ом. Я кину на окно три текстовых поля, пару статических текстовых полей и одну кнопку. Сделаем простой калькулятор, который при нажатии на кнопку складывает значения из двух текстовых полей и выводит результат в третьем. Надо же с чего-то начинать :).
Для настройки контролов используется «инспектор объектов» (Tools -> Inspector). В его окошке отображаются свойства выделенного контрола, и их можно редактировать. Так, с помощью инспектора я задал тайтл кнопки и окна.
Ну, хорошо. Интерфейс у нас есть, и если мы соберем и запустим проект, то увидим на экране форму такой, какой мы ее создали с помощью Interface Builder’а. Ну а как же логика работы нашего приложения? Как создать обработчики для событий, приходящих от контролов, и изменять внешний вид нашего приложения в коде? А вот тут-то и начинается самое интересное... Нам нужно интегрировать наш интерфейс и приложение.
Для этого создадим класс-контроллер окна. Этот класс будет содержать обработчики сообщений от контролов формы и изменять их вид. Идем опять в XCode (не забыл, что мы все это время работали в Interface Builder’е?) и там создаем класс с именем AppConroller (File -> New File -> Cocoa -> Objective-C class). XCode создаст для нас два файла AppController.h и AppController.m с интерфейсом и реализацией класса-контроллера соответственно. Есть одна проблемка – Interface Builder о нашем классе AppController ничего не знает, а мы ведь должны будем привязать объекты интерфейса к полям и методам AppController’а. Перетаскиваем AppController.h на главное окно InterfaceBuilder. Теперь порядок. Но чего-то все-таки не хватает. Ага! Класс есть, а объекта этого класса нет ни одного. Идем в Interface Builder -> Tools -> Library -> Object и перетаскиваем объект с палитры на главное окно билдера. С помощью инспектора сообщаем билдеру, что это – объект класса AppController.
Теперь вся необходимая информация есть в MainMenu.xib, и при старте нашего приложения среда выполнения, загрузив этот файл, содержащий описание интерфейса, создаст объект нашего класса и настроит его нужным образом.
Наконец-то кодинг
После того, как с формошлепством покончено, настало время написать немного кода, который будет отвечать за поведение нашего приложения.
Код класса AppController
// Интерфейс класса
// Включаем Cocoa.h
#import <Cocoa/Cocoa.h>
@interface AppController : NSObject
{
// Ссылки на три текстовых поля.
// Значение будет присвоено
// средой выполнения.
IBOutlet NSTextField * FirstNumber;
IBOutlet NSTextField * SecondNumber;
IBOutlet NSTextField * Result;
}
// Обработчик клика на единственной кнопке
// нашего приложения
- (IBAction) buttonClick: (id) sender;
@end
// Реализация класса
#import "AppController.h"
@implementation AppController
// Обработчик клика очень прост
- (IBAction) buttonClick: (id) sender
{
// Берем значения из двух текстовых полей и
// присваиваем их сумму третьему
[Result setIntValue:
[FirstNumber intValue] +
[SecondNumber intValue]];
}
@end
Об идентификаторах IBOutlet и IBAction, которые присутствуют в коде нашего класса, нужно сказать отдельно. Это, собственно, Interface Builder Outlet (ссылка на элемент интерфейса) и Interface Builder Action – обработчик события, которое генерируется каким-то объектом GUI. Привязывать их к реальным объектам пользовательского интерфейса в коде не нужно, сделаем это, используя великий и могучий Interface Builder. Для этого:
- открываем контекстное меню для объекта AppController в InterfaceBuilder’е;
- дропаем созданные нами аутлеты на соответстующие контролы, а IBAction – на кнопку.
Ну вот, наконец-то наше мегаприложение готово к работе. Жмем на «Build and Go» и видим следующее.
Кстати, важный момент: методы и функции Cocoa не бросают исключения, поэтому не жди, что это приложение упадет, если ты вместо целого числа введешь строчку букв. Обработка некорректных значений в этом случае – задача программиста (но об этом в другой раз).
Кварцевая графика
Итак, стандартные компоненты на форму мы с тобой кидать научились, и даже научились обрабатывать события от них и изменять их состояние из кода. Ну, а что если нужного контрола в стандартных библиотеках нет? Если хочется нарисовать на форме что-то необычное? В самом деле, это же графическое приложение :). Конечно же в Mac OS X сделать это возможно, причем, с помощью самого Cocoa.
Для отрисовки графики в Mac OS X используется векторный двежок Quarz. Для того, чтобы отрендерить собственную 2D-сцену, нам потребуется создать свой класс-вид, наследник NSView. Нам нужно будет переопределить метод drawRect в этом классе.
Для того, чтобы отрендерить собственную 2D-сцену нам потребуется создать свой класс-вид
XCode создаст для нас два файла – MyView.h и MyView.m с шаблоном интерфейса и реализации нашего класса соответственно. Дропаем MyView.h из списка файлов проекта на главное окно Interface Builder. Теперь выбираем из палитры контролов Library CustomView, перетаскиваем его на нашу форму и в окошке инспектора задаем для этого контрола MyView в качестве базового класса. Устанавливаем высоту вида равной ширине (это нам пригодится для профилактики искажений в данном конкретном примере). Теперь нужно позаботиться о внешнем виде нашего контрола. Нарисуем что-нибудь красивое, например, монаду Инь-Янь. Все линии (path) будем создавать с помощью кривых Безье. В этом нам поможет Cocoa-класс NSBezierPath.
Реализация класса MyView
// Импортируем заголовок
// Его за нас создала XCode
// Ничего там менять не будем в этот раз.
#import "MyView.h"
@implementation MyView
// Этот код тоже создала XCode
// И здесь мы тоже ничего менять не будем :)
- (id)initWithFrame:(NSRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
}
return self;
}
// А вот в методе отрисовки
// мы и напишем код нашей
// 2D-сцены
- (void)drawRect:(NSRect)rect
{
// Установим серый цвет для кисти
[[NSColor grayColor] set];
// Заполним всю доступную нам область
NSRectFill( rect );
// Впишем в нашу прямоугольную область окружность
NSBezierPath * circle =
[NSBezierPath bezierPathWithOvalInRect: rect];
// Задаем толщину линии
[circle setLineWidth: 2.0];
// Заливать будем белым
[[NSColor whiteColor] set];
[circle fill];
// А теперь черный для границы
[[NSColor blackColor] set];
// Отрисуем границу
[circle stroke];
// Эти значения нам очень пригодятся; чтобы не
// cчитать их каждый раз, сделаем это здесь
float center_x = rect.size.width / 2.0;
float center_y = rect.size.height / 2.0;
NSPoint center = {center_x, center_y};
NSPoint center_up = {center_x, center_y * 0.5};
NSPoint center_dn = {center_x, center_y * 1.5};
float radius =
center_x < center_y ? center_x : center_y;
// Темная часть монады
NSBezierPath * black_side =
[NSBezierPath bezierPath];
// Большая дуга
[black_side appendBezierPathWithArcWithCenter:
center
radius: radius
startAngle: 90
endAngle: 270
clockwise: YES];
// Верхняя малая дуга
[black_side appendBezierPathWithArcWithCenter:
center_up
radius: radius / 2
startAngle: 270
endAngle: 90
clockwise: NO];
// Нижняя малая дуга
[black_side appendBezierPathWithArcWithCenter:
center_dn
radius: radius / 2
startAngle: 270
endAngle: 90
clockwise: YES];
// Заливаем черным
[[NSColor blackColor] set];
[black_side fill];
// Малый черный круг
[[NSBezierPath bezierPathWithOvalInRect:
NSMakeRect(center_x - radius / 6.0,
center_y - radius * (0.5 + 1/6.0),
radius / 3.0, radius/3.0)] fill];
// Малый белый круг
[[NSColor whiteColor] set];
[[NSBezierPath bezierPathWithOvalInRect:
NSMakeRect(center_x - radius / 6.0,
center_y + radius * (0.5 - 1/6.0),
radius / 3.0, radius/3.0)] fill];
}
@end
Заключение
Теперь ты знаешь, как создать GUI’шное Cocoa-приложение. Процесс разработки под iOS для всяческих iPhone’ов и iPad’ов не сильно отличается от создания оконных приложений для макоси, который описан здесь, поэтому дерзай. Удачи!