Программирование для Mac OS X Cocoa - работа с ресурсами

Одним из огромных плюсов операционной системы Mac OS X (на самом деле благополучно унаследованном из NextStep) является мощная система разработки пользовательского интерфейса.

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

Эта статья открывает цикл статей, посвященных программированию для лучшей в мире операционной системы (IMHO) - Mac OS X.

Основным инструментом для разработки приложений под Mac OS X является XCode - полноценная интегрированная среда разработки, поставляемая вместе с самой операционной системой.

Кроме XCode важным инструментом для разработки приложений является Interface Builder - среда для создания пользовательского интерфейса (и работы с ресурсами приложения вообще).

Для начала работы запустите Interface Builder щелкнув по его иконке в доке (см. рис. 1).

Start Interface Builder

Рис 1. Запуск Interface Builder.

После этого запустится Interface Builder и предложит выбрать что именно будет создаваться. Выберите в предлагаемом списке пункт Cocoa/Application и щелкните по кнопке New.

select type of project

Рис 2. Выбор типа приложения.

В результате Interface Builder создаст новый проект, пока еще не имеющий имени (вместо имени используется "Untitled"). При этом на экране будут отображены следующие окна - пустое окно для создаваемого приложения (озаглавленное "Untitled"), меню (окно с заголовком "Untitled - MainMenu"), палитра с компонентами (окно с заголовком "Cocoa-Containers") и окно самого проекта, также озаглавленное "Untitled" (см. рис 3).

Основной концепцией Interface Builder-а является то, что графические (визуальные) вещи надо делать визуально. Поэтому при работе большинство вещей делается именно визуально - нужные объекты перетаскиваются из палитры и кладутся куда их надо положить, размеры, положение и т.п. атрибуты также изменяются непосредственно при помощи мыши.

Рис 3. Начало работы с Interface Builder.

Чтобы убрать лишние окна с экрана (не относящиеся к Interface Builder-у) можно воспользоваться командой меню Interface Builder/Hide Others.

Interface Builder служит для работы с ресурсами приложения. Все имеющиеся ресурсы являются объектами и хранятся в так называемом nib-файле. Хотя на самом деле это не файл, а каталог (имеющий расширение nib).

В главном окне проекта представлены основные категории объектов - Instances (внешние объекты на которые можно ссылаться), Classes (информация о классах), Images (используемые изображения), Sounds (используемые звуки), Nib.

Свойства каждого объекта можно просматривать и изменять при помощи так называемого инспектора. Для вызова инспектора для текущего объекта используйте команду меню Tools/Show Inspector (см. рис 4. ).

Inspector panel

Рис 4. Вызов окна инспектора.

Окно инспектора всегда одно и оно отображает свойства текущего (выделенного в данный момент) объекта. При выделении нового объекта инспектор автоматически переключается на отображение свойств нового объекта.

Открыв окно инспектора сделайте активным пустое окно, озаглавленное "Untitled". При этом заголовок окна инспектора сразу же изменится на "NSWindow Inspector" - все окна обычно принадлежат классу NSWindow.

Изменим заголовок окна набрав "No Code Demo" в поле "Window Title". Также снимем выделение с пункта "Zoom (and resize)" - это запретит изменять размер окна во время выполнения программы (см. рис 5. ).

Changing window attributes

Рис 5. Изменение свойств окна.

Теперь начнем помещать компоненты интерфейса на наше окно. Для этого в палитре компонент выберем палитру стандартных управляющих элементов (выбрав в верхней части палитры вторую иконку - см. рис 6).

palette of standart view

Рис 6. Палитра стандартных управляющих элементов.

После этого возьмем горизонтальный слайдер и перетащим его на наше окно (именно перетащим, т.е. удерживая во время перетаскивания нажатой клавишу мыши).

dragging slider from panel to window

Рис 7. Перетаскиваем слайдер в окно.

Положив слайдер в окно и сделав его активным объектом мы перевели инспектор на отображение свойств слайдера (заголовок окна инспектора стал "NSSlider Inspector"). Зададим теперь в окне инспектора диапазон изменения значений для слайдера (от -100 до 100), текущее значение (50). Также зададим количество отметок (5) и перетаскивая голубые точки разместим слайдер как показано на рис. 8.

setting slider attributes

Рис 8. Настройка слайдера.

Выберем теперь на палитре компонент третью группу и перетащим поле для ввода текста (расположенное в палитре сразу же под компонентой "System Font Text") на окно прямо под слайдер.

Рис 9. Перетаскиваем поле для ввода текста в окно.

Сделав поле ввода активным, выровняем его так, чтобы его ширина совпадала бы с шириной слайдера (также при помощи перетаскивания голубых точек вокруг объекта). Обратите внимание, что инспектор теперь показывает свойства для поля ввода (объекта класса NSTextField).

aligning text field

Рис 10. Настройка поля ввода.

Теперь настроим свойства поля ввода текста как показано на рисунке 10.

Далее перетащим из палитры кнопку и поместим ее под полем ввода как показано на рис. 11.

dragging button into window
Рис 11. Перетаскиваем кнопку в окно.

Выровняем кнопку и поменяем ее текст (либо в окне инспектора, либо просто введем текст после двойного щелчка мышью по самой кнопке) на "Close". Также выберем для нее так называемый key equivalent - клавишу, нажатие которой будет эквивалентно нажатию данной кнопки. В качестве такой клавиши выберем из выпадающего списка справа Escape (см. рис. 12).

setting button attributes

Рис 12. Настройка атрибутов кнопки.

Далее изменим размер окна, как показано на рисунке 13.

change window size

Рис 13. Изменение размеров окна.

Обратите внимание по синии линии на рис. 13. При размещении элементов на в окне, изменении их размеров и/или положения (равно как и изменении размеров окна) появляющиеся пунктирные синии линии служат для обозначения "правильного" положение (размера). Правильность понимается в соответствии с принятыми в Mac OS X принципами построения удобного пользовательского интерфейса.

А вот дальше начинается самое интересное - удерживая нажатой клавишу Ctrl кликнем по слайдеру и удерживая нажатой клавишу мыши протянем связь к полю ввода (при перемещении курсора мыши за ним будет идти линии связи от слайдера). При входе курсора мыши в поле ввода поле будет показано выделение поля ввода. Отпустите клавишу мыши (рис 14).

drag connection from slider to text field

Рис 14. Установления связи от слайдера к полю для ввода текста.

Обратите внимание, что теперь мы видим, что слайдер и поле ввода соединены между собой. Более того, в окне инспектора (слайдера, т.к. заголовок NSSlider Inspector) открыта новая закладка Connections.

slider connections

Рис 15. Раздел Connections для слайдера, отображающий возможные сообщения в класса NSTextField.

Под этим разделом отображаются связи текущего объекта с другими объектами. При загрузке nib-файла в память происходит его разархивация и установление соответствующих связей между объектами приложения.

Подобные связи бывают двух видов - outlet и Target/Action.

Связь типа outlet - это просто ссылка на другой объект (в общем случае неизвестного класса, хотя можно и явно задать тип класса для объекта, на который указывает outlet).

Связь типа Target/Action определяет объект и сообщение, которые нужно послать ему при наступлении определенного события. Для слайдера таким событием является изменение отображаемого им значения (при перетаскивании пользователем ползунка).

Выберем в разделе Connections закладку Target/Action и в списке возможных сообщений - сообщение takeFloatValueFrom:. После этого щелкнем по кнопке Connect для установления данной связи.

Теперь каждый раз, когда отображаемое слайдером значение будет изменено, то полю ввода будет посылаться сообщение takeFloatValueFrom: с указателем на слайдер в качестве аргумента.

Что же делает это сообщение ? Оно использует переданный параметр для получения от него вещественного значения (путем посылки ему сообщения floatValue) и устанавливает текущее вещественное значение компоненты в данное (при помощи посылки самому себе сообщения setFloatValue:).

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

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

Для этого протянем связь от поля ввода к слайдеру и также выберем сообщение takeFloatValueFrom: (см. рис. 16).

Рис 16. Установка связи от поля ввода к слайдеру.

Теперь и редактирование текста в поле ввода также будет приводить к изменению положения ползунка слайдера (обратите внимание, что поле редактирования может фактически работать в двух разных режимах, в одном из которых сообщение будет посылаться только при нажатии клавиши Enter).

Установим еще одну связь - чтобы по нажатию кнопки "Close" выполнение программы прерывалось. Для этого протянем связь от кнопки "Close" к объекту File's Owner , расположенному в окне проекта Untitled в секции Instances (см. рис 17).

Рис 17. Установка связи для кнопки "Close".

Как уже отмечалось Interface Builder предназначен для редактирования ресурсов, помещаемых в каталог с расширением nib. На самом деле каждый nib-файл представляет собой сериализованные объекты и связи между ними.

Каждый такой nib файл должен быть загружен приложением (что автоматически приводит с созданию всех задаваемых в этом nib-е объектов и их связей). При его загрузке необходимо указать объект, который будет являться владельцем данного nib-а. Он и обозначается как File's Owner (т.е. это фактически ссылка на внешний для nib-а объект).

Если приложение содержит всего один nib (или это главный nib), то в качестве загружающего его объекта выступает NSApp - объект класса NSApplication, обеспечивающий доступ к основным функциям связанным с управлением данным приложением.

К числу таких функция относиться и окончание работы приложения, соответствующее сообщение называется terminate:.

Поэтому протянув связь от кнопки "Connect" к объекту File's Owner, выберем в списке Target/Action сообщение terminate: и установим эту связь щелчком по кнопке "Connect".

Сохраним проект при помощи Command-S (см. рис 18) выбрав имя No-Code-Demo.

saving project

Рис 18. Сохранение проекта.

Теперь можно попробовать как работает наш интерфейс. Для этого используйте команду меню File/Test Interface или Command-R. После этого все окна Interface Builder-а исчезнут, кроме главного окна, на котором мы разместили наши компоненты.

Можно попробовать подвигать ползунок слайдера (наблюдая как при этом меняется значение в поле ввода) и изменить значение в поле ввода (наблюдая как при этом меняется положение ползунка слайдера).

Рис 19. Окно в действии

Давайте теперь зададим формат, в котором мы хотим получать значения в поле ввода. Для этого в палитре компонент откроем закладку текстовых компонент и выберем форматтер для чисел (NSNumberFormatter)(см. рис. 20)

Рис 20. Выбираем форматтер из палитры компонент.

После этого перетащим форматтер на поле ввода.

Рис 21. Перетаскиваем форматтер на поле ввода.

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

Рис 22. Свойства форматтера.

Настроив свойства форматтера перейдем к еще одному виду настроек - настройке поведения элементов при изменении размера окна. Для этого откроем в окне инспектора закладку Size (см. рис. 23).

Рис 23. Закладка Size

Наиболее интересной частью здесь является нижняя часть, озаглавленная Autoresizing.

Именно в этой части можно задать что должно происходить с компонентой при изменении размера. При изменении размера окна может изменяться как размер самой компоненты, так и ее положение в окне.

Попробуйте щелкнуть мышью по одной из линий в этой области. При этом соответствующая часть превращается из прямой линии в пружину (и наоборот при повторном нажатии). Пружина обозначает что соответствующая величина будет изменяться при изменении размеров окна.

Вы можете разрешить или запретить изменение следующих величин - высоты и ширины самой компоненты (линии внутри маленького квадрата), расстояние от компоненты до левой, правой, верхней и нижней границы окна. Каждое такое расстояние представлено или прямой линией или пружиной. Если в одном направлении (например по горизонтали) имеется сразу несколько пружин, то это значит, что сразу несколько размеров/расстояний будут изменяться.

Так на следующем рисунке представлена следующая схема изменения размеров - расстояния в левой и правой границ окна всегда остается неизменным, зато изменяется ширина компоненты.

Точно также неизменным остается расстояние до верхней границы окна и высота компоненты. Такие настройки задают вполне осмысленное поведение для компоненты - ее ширина подстраивается под ширину окна, но при этом вертикальное положение компоненты остается неизменным.

Рис 24. Задание автоматического изменения размера для поля ввода.

А вот для кнопки "Close" гораздо правильнее будет задать другой закон, при котором данная кнопка окажется как-бы приклеенной к правому нижнему углу окна (т.е. не будет изменять своего положения относительного этого угла).

Рис 25. Задание автоматического изменения размера для кнопки.

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

Нашим следующим шагом будет настройка меню, изображенного в окне с заголовком "No-Code-Demo - MainMenu".

Откроем пункт меню озаглавленный New Application и сделаем двойной щелчок мышью по пункту меню About NewApplication. После этого текст этого пункта выделится и мы сможем его редактировать.

Рис 26. Редактирование меню приложения.

Заменим его на About No Code Demo.

Рис 27. Измененный пункт About.

Аналогичным образом заменим последний пункт меню на Quit No Code Demo и уберем пункт Preferences... (для этого выделим этот пункт и нажмем Control-X).

Рис 28. Исправленное главное меню.

Обратите внимание, что сам пункт меню NewApplication тоже следует переименовать в No Code Demo.

Теперь при тестировании интерфейса мы будем получать правильное меню (хотя по-прежнему никакого приложения у нас нес - есть только файл ресурсов).

Рис 29. Меню в действии.

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

Рис 30. Раздел звуков.

Теперь выделим звук Funk и перетащим его на кнопку "Close".

Рис 31. Перетаскиваем звук на кнопку.

Обратите внимание на свойства кнопки в инспекторе - в поле "Sound:" появилось название перетащенного туда звука.

Рис 32. Свойства кнопки после перетаскивания на нее звука.

Если теперь попробовать интерфейс (при помощи Command-R), то при нажатии на кнопку будет издаваться звук.

Таким образом мы фактически построили простейшее приложение ни написав ни одной строчки кода - просто путем комбинирования уже готовых объектов.

Самое поразительное, что вся эта функциональность существовала в NextStep еще в самом начале 90-х годов, т.е. задолго до Visual Basic, Delphi, Windows 95/NT и т.п.

По этой ссылке можно скачать получившийся nib.

Следующая статья будет посвящена написании простейшего калькулятора, в ней будет как разработка интерфейса, так и написание кода на Objecticve-C.

Valid HTML 4.01 Transitional

Напиши мне
Используются технологии uCoz