Знакомство с Interface Builder. Связи между объектами.

    Кросспост из блога "Программирование на Python и
    Objective-C под Mac OS и для iPhone / iPod Touch
    "
    Посвящено комментариям #1, #2 и #3 (оу, чёрт, hellraiser09 не читай последний)


    Процесс создания любого приложения можно условно разделить на три этапа: создание интерфейса, непосредственное написание кода и отладка. В первой части своих статей я хочу познакомить вас с Interface Builder (далее просто IB) — средством для визуального создания и тестирования интерфейсов, входящей в состав SDK разработчика под Mac OS, на примере разработки интерфейса для iPhone. Способ создания интерфейса программ для Mac OS X сильно не отличается от приведенных ниже принципов, поэтому данное руководство можно использовать для разработки интерфейсов для «большой» Mac OS с некоторыми различиями, о которых я упомяну, когда придет время.



    Создание интерфейса с помощью Interface Builder обычно включает в себя:

    • создание nib файла, который представляет собой не просто хранилище интерфейса программы, но и хранит связи между объектами, а также инициализирует многие из них без дополнительного кода
    • создание нового окна приложения (а также меню для настольной Mac OS)
    • добавление элементов интерфейса в окно приложения с помощью встроенной библиотеки и настройка их атрибутов
    • разработчик также может при помощи IB создавать новые классы, методы и атрибуты, а затем создавать экземпляр класса на основе этого описания и все это без единой строчки кода!
    • в любой момент при помощи сочетания клавиш command+R можно протестировать интерфейс программы


    Предлагаю пройтись по этим пунктам и дать пояснение особо сложным местам. Заранее предупрежу, что я не буду вдаваться в подробности и давать расшифровку банальным присвоениям свойств элементам интерфейса, разобраться в которых не состави труда любому начинающему программисту, а заострю внимание именно на проблемных и сложных для понимания концепциях, с которыми столкнулся во время своего ознакомления с программой и, уверен, что такие же вопросы могли возникнуть и у вас, если вы уже пробовали разобраться в доступных на сайте developer.apple.com примерах приложений для iPhone.

    Состав окон в Interface Builder



    Предлагаю начать наше знакомство с IB с открытия интерфейса программы из проекта, с которым мы знакомились в одном из прошлых постов. Тем кому не довелось прочесть мои предыдущие статьи, просто создайте новый проект с именем «Empty» для iPhone на основе шаблона «Window-Based Application», и откройте прилагающийся к нему xib-файл интерфейса (xib и nib одно и то же). Затем вызовите сочетанием клавиш Shift+Command+I окно инспектора свойств, на которое я буду ссылаться в процессе повествования.



    Окно Library (библиотеки компонентов и ресурсов) разделено на две части: библиотека компонентов Objects (строка навигации, тектовое поле, поля ввода, кнопки и пр.) и ресурсы Media (изображения, звуки и пр.).

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

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



    File's Owner и First Responder являются так называемыми прокси-объектами — это «хранилища» для объектов, на использование которых ссылаются в интерфейсе программы, но они не включены в nib-файл. Прокси-объекты при загрузке nib-файла в теле программы не инициализируются, вместо этого они замещаются реальными объектами в запущенном приложении. Такие замены происходят автоматически, но вы можете вручную указать замещаемые объекты при загрузке файла интерфейса в программу. Проще говоря, прокси-объекты — это всего лишь ссылки.

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

    Объект First Responder в процессе работы приложения постоянно меняется. Cocoa использует несколько факторов, чтобы определить какой из объектов должен играть роль First Responder в данный момент: какое окно сейчас активно, какой view получил фокус и пр. Типичные действия за которые отвечает First Responder в программе: работа с буфером обмена, манипуляции с текстом, операции уровня документа (undo, redo, save), определенные пользователем действия и пр. Сейчас его назначение не совсем ясно, но по мере его использования в своих примерах я расскажу подробности его применения и все встанет на свои места.

    EmptyAppDelegate — класс, которому делегировал полномочия UIApplication, т.е. File's Owner в нашем случае (подробнее о данной процедуре можно прочесть в посте "Изучая пустоту").

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

    Переключите просмотр окна nib-файла в режим списка — такой вид облегчит нам дальнейшее конфигурирование интерфейса приложения.



    Создание связей между объектами



    Наверно, одна из первых сложностей с которой сталкивается разработчик в IB — это связи между объектами. Новая концепция настройки методов и свойств, основанная на специфике языка Objective-C общаться при помощи отправки сообщений, вызывает недопонимание, хотя смысл работы достаточно прост. В процессе создания связей между объектами используются два термина: Outlet и Action.

    Outlet представляет собой обычную переменную объекта, которая ссылается на объект в интерфейсе приложения.

    Наш интерфейс уже содержит несколько примеров Outlets. В окне nib-файла выберите объект File's Owner и переключитесь на закладку Connections в окне инспектора свойств. Для объекта File's Owner определена переменная delegate, которая ссылается на объект делегата класса UIApplication, в нашем случае Empty App Delegate — главный класс нашего приложения.



    Выберите последний и посмотрите его свойства. Он в свою очередь также имеет одну переменную — window, которая содержит ссылку на объект UIWindow или, проще говоря, на главное окно нашего приложения.



    Подраздел Referencing Outlets представляет собой список ссылающиеся на наш объект переменных других объектов.

    В коде приложения такие переменные классов отличаются от обычных спецификатором типа — IBOutlet. Откройте EmptyAppDelegate.h файл в XCode.

    #import <UIKit/UIKit.h>
    
    @class EmptyViewController;
    
    @interface EmptyAppDelegate : NSObject <UIApplicationDelegate> {
        IBOutlet UIWindow *window;
    }
    
    @property (nonatomic, retain) UIWindow *window;
    
    @end
    
    


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

    Связи типа Action в Interface Builder необходимы для передачи сообщений от одного объекта к другому. Всякий раз когда пользователь взаимодействует с интерфейсом программы: нажимает кнопки, перемещает слайдер, вводит текст в поле ввода генерируются сообщения, обработку которых можно реализовать в своем коде.

    В окне Library зайдите на вкладку Objects и перетащите элемент UIView на нашу форму. Его размер автоматически изменится, чтобы охватить все свободное пространство в окне. Это нормально.



    Здесь хочу сделать небольшое отступление. Как вы помните, я уже говорил в одном из моих прошлых постов, что объект типа UIWindow является дочерним от UIView. В нашей форме уже присутствует объект типа UIWindow, который определяет главное окно приложения, и я мог бы реализовать все приведенные ниже примеры взяв его в качестве основы для размещения элементов интерфейса. Но с точки зрения программирования приложения для iPhone правильнее использовать объект UIView как область размещения интерфейса программы. Обычно в приложении для iPhone используется даже не одно, а множество Views, которые меняются в процессе работы приложения для представления различных данных. Вспомните, например, программу Настройки на вашем iPhone / iPod Touch. Смена окон при выборе раздела настроек (почта, музыка, основные) реализована как раз за счет многочисленных View в приложении, а за процесс смены View отвечает объект класса UIViewController, но о нем я расскажу в следующей статье. Кроме того, я хотел бы рассказать вам об одном из преимуществ Interface Builder, демонстрация которого без реализации нового класса в IB невозможна.

    Вернемся к нашей форме. Выделите объект View на нашей форме, кликнув по пустому месту в окне, и перейдите на закладку Identity инспектора свойств. В разделе Class Outlets добавьте новую переменную с именем label и типом UILabel, а в разделе Class Identity дайте название нашему новому классу reView.



    Перетащите элемент Label из Library в центр нашей формы. Измените его размеры, чтобы элемент занимал всю ширину окна, и сделайте выравнивание текста по центру.



    Теперь мы должны установить связь между этой переменной и объектом Label на нашей форме. Есть три пути:

    1. С нажатой клавишей Ctrl нажмите на объект View в окне приложения и не отпуская клавиши мыши проведите появившуюся линию до объекта Label на форме.



      После этого отпустите кнопку и выберите из выпадающего списка возможных вариантов переменных, объявленных в классе UIView, переменную label.



    2. Выделите объект Label на нашей форме и перейдите на закладку Connections в окне инспектора свойств. Нажмите левой кнопкой мыши на кружок напротив New Referencing Outlets и проведите появившуюся линию до объекта View на форме. После отпускания клавиши мыши выберите из появившегося списка переменных label.



    3. Нажмите правой кнопкой мыши (или Ctrl+клик) по View в окне nib-файла. В появившемся окне связей класса нажмите на кружок напротив переменной label и соедините его линией с объектом Label на форме.





    Перетяните из библиотеки элементов Round Rect Button на нашу форму и назовите кнопку «Напомнить».



    После чего выделите объект View, перейдите на закладку Identity и в разделе Class actions добавьте новый элемент под названием touchIt. А затем любым из вышеописанных методов свяжите его кнопкой на форме. В выпадающем списке выберите метод Touch Up Inside.



    Теперь при нажатии на кнопку будет отправлено сообщение в объект View. А точнее, в момент окончания нажатия на кнопку (Touch Up Inside вызывается в момент отпускания кнопки), будет вызван метод touchIt класса reView.

    При создании связей типа Action и Outlet разрешены соединения типа один-ко-многим. То есть мы могли добавить на нашу форму еще несколько элементов и связать их с нажатием на кнопку. В этом случае вызов Touch Up Inside приводил к реакции сразу нескольких элементов. Или в случае многих связей с элементом Label на форме мы могли бы изменять его значение из разных классов в процессе работы программы.

    Заметьте также, что от типа связи (Outlet или Action) зависит направление перетягивания линии связи: от кнопки к view — action, когда нажатие на кнопку вызывает метод во view; от view к label — outlet, когда изменение переменной внутри реализации класса вызывает изменения в объекте на форме.

    Напоследок выделите объект Label в окне приложения и очистите его значение.

    Как вы видите, процесс создания actions в нашем приложении не сильно отличает от создания outlets. Давайте теперь взглянем на код, отрабатывающий действие.

    Interface Builder предоставляет ряд удобств в плане создания новых классов, которые позволят меньше уделять внимания коду и сокращают время разработки. Как вы успели заметить, мы создали новый класс производный от UIView в Interface Builder, но у нас отсутствуют файлы, его реализующие. Для этого у IB есть мощнейшая функция, облегчающая нам работу. Выделите объект View в окне nib-файла, а затем войдите в меню File и вызовите команду Write Class Files.



    Оставьте имя класса по умолчанию — reView и нажмите сохранить. В новом окне поставьте галочку, которая добавит файлы нового класса в наш проект в XCode.



    Переключитесь в XCode и убедитесь, что чудо произошло. Файлы reView.h и reView.m добавлены в наш проект. Перетяните их из корня проекта в папку Classes для субординации.



    Откройте файл reView.h

    #import <UIKit/UIKit.h>
    #import <Foundation/Foundation.h>
    
    @interface reView : /* Specify a superclass (eg: NSObject or NSView) */ {
        IBOutlet UILabel *label;
    }
    - (IBAction)touchIt;
    @end
    
    


    Здорово, не правда ли? Все что нам остается, так это описать свойство label и указать родительский класс:

    #import <UIKit/UIKit.h>
    #import <Foundation/Foundation.h>
    
    @interface reView : UIView {
        IBOutlet UILabel *label;
    }
    
    @property (nonatomic, retain) UILabel *label;
    
    - (IBAction)touchIt;
    @end
    
    


    Переключитесь в реализацию класса опишите реализацию метода touchIt:

    #import "reView.h"
    
    @implementation reView
    
    @synthesize label;
    
    - (IBAction)touchIt {
        label.text = @"Позвони родителям!";
        label.textColor = [UIColor redColor];
    }
    @end
    
    


    Позволю себе здесь сделать еще одно отступление. Когда я первый раз столкнулся с программой на Objective-C, я не мог понять зачем ставиться какой-то идентификатор @ перед всем строковыми выражениями. Когда я нашел ответ, то понял, что не могу не поделиться им с вами. Директива @"" скрывает под собой создание нового объекта класса NSConstantString, чтобы программист каждый раз при работе со строками не тратил время на его объявление.

    Для тех кто читал мой пост "Введение в Objective-C" дам более глубокое объяснение работы данной директивы. В документации нигде не дано описания этого класса, но известно, что не нужно заботиться об освобождении объектов-строк данного класса после использования. Я так полагаю, что инициализация происходит в следующем виде:

    [[[NSConstantString alloc] initWithSomething:"Наша строка"] autorelease]
    
    


    Я думаю, вы уже догадались как работает этот код. Инициализуется новый объект класса NSConstantString и сразу же делается отложенное освобождение объекта из памяти вызовом метода autorelease. Как вы помните, объект в этом случае будет освобожден позже: после окончания работы участка кода, где был вызван этот метод.

    Объект NSConstantString может быть передан в качестве параметра в функцию или как значение в переменную, где будет произведено retain (в общем случае) его значения, что приведет к увеличению количества ссылок на объект на 1. А значит к моменту вызова release в AutoReleasePool количество ссылок на объект будет равно 2, и мы сохраним объект до окончания работы с ним.

    В реализации метода touchIt мы использовали еще один новый для нас класс UIColor. Он возвращает объект, описывающий указанный при инициализации цвет. Объект UIColor инициализируется с помощью метода класса redColor, поэтому не требует вызова alloc метода, а также освобождения объекта после использования.

    Сохраните все изменения и запустите наше приложение.



    Посреди нашей формы красуется нопка «Напомнить», нажатие на которую приводит появлению красной надписи «Позвони родителям!».

    Interface Builder работает в тесном контакте с XCode. Поэтому когда вы объявляете новую переменную или метод со спецификатором типа IBOutlet или IBAction в заголовочном файле, они сразу же становятся доступны в окне связей IB. Чтобы посмотреть как это происходит, измените объявление класса reView, в соответствии с примером:

    #import <UIKit/UIKit.h>
    #import <Foundation/Foundation.h>
    
    @interface reView : UIView {
        IBOutlet UILabel *label;
        IBOutlet UITextField *textField;
    }
    
    @property (nonatomic, retain) UILabel *label;
    @property (nonatomic, retain) UITextField *textField;
    
    - (IBAction)touchIt;
    @end
    
    


    Переключитесь в окно IB и вызовите окно связей объекта reView. Теперь мы может создать новую связь между нашей переменной textField и объектом на форме. Но учтите, что это должен быть обязательно элемент типа UITextField. После создания связи вы можете поиграться со свойствами элемента в коде программы и поглядеть как это будет выглядеть в форме после запуска приложения. Советую для лучшего понимания потренироваться с как можно большим количеством элементов интерфейса, а в более сложные примеры мы углубимся в следующих статьях.

    Данный обзор был подготовлен на Mac mini G4 с установленной Mac OS X 10.5.4 и iPhone SDK (build 9M2199a). Как мне удалось запустить iPhone SDK на платформе PowerPC я расскажу в следующем посте уже совсем скоро.

    Скачать архив проекта (14Кб)


    Эпилог



    Под конец хочу сказать благодарственные слова:
    1. создателю данного блога — hellraiser09, который стал первопроходцем в данной теме на Хабрахабр. Жаль, что он забросил развивать свой блог, но я постараюсь заменить его насколько меня хватит.
    2. хочу сказать огромное спасибо людям сделавших блоговый движок Byteflow за их работу и помощь в настройке блога


    Давно руки чесались напечатать несколько пожеланий блоггерам. Я понимаю, что Хабр — прекрасное место для того, чтобы пропиарить свой блог (собственно, я сам сейчас этим и занимаюсь). Но, пожалуйста, прежде чем заниматься раскруткой уделите внимание его наполнению. Попробуйте в течении месяца писать в блог. Если вам надоест — не тратьте время хабрачитателей. Сколько раз видя слово «кросспост» я нажимал на ссылку и, перейдя на сайт автора, разочарованно уходил с него буквально сразу. Знаете почему? Потому что там пусто или тупо нечего читать!

    Если вы все-таки решили сделать рекламу на Хабре, то наполните блог интересными статьями, чтобы первым посетителям было что почитать, кроме поста «Знакомство» и «Я буду писать в этом блоге о биопсии мозга». Если вы хотите, чтобы вас было интересно читать, то не старайтесь писать часто, но пишите содержательно, чтобы даже ваши старые посты было полезно перелистать заново. Доставляйте уникальную информацию и не пишите банальных вещей, глупостей и коротких постов, не несущих смысла.

    Всем спасибо за внимание.
    Поделиться публикацией
    Похожие публикации
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 24
    • 0
      Очень полезно, спасибо
      • 0
        А есть-ли некий правильный софт на тему Microsoft Visio, но под Mac и с такой же философией?
      • 0
        Отлично! А то на русском языке описаний вообще кот наплакал. Тем более таких подробных и с хорошими объяснениями. Спасибо большое! :)
        • 0
          Очень полезная статья. Лично я давно хотел научиться кодить под мак. Плюсанул бы автора, но жаль, что не могу =(. Must Read =)
          • +1
            Это только у меня картинки не грузятся?
            • 0
              не только у вас
              • 0
                Не только. :-)
                • 0
                  Сайт упал у хостера :( Загрузил картинки на Хабр.
                • 0
                  Сегодняшние статьи меня очень радуют.
                  • 0
                    Жаль, что он забросил развивать свой блог, но я постараюсь заменить его насколько меня хватит.

                    да никуда я не пропал. просто сексия, потом море, теперь вот валом работы(кстати разработка под iPhone). Немного разгребусь и напишу еще. Материал подсобрал, о чем писать есть.

                    За статью спасибо. Статью про IB надо было еще в самом начале написать, но как-то не получалось. Теперь вы избавили меня от этого
                    • 0
                      Как только подниму рейтинг, тоже начну писать в блог по разработке в OS X
                      • 0
                        поднял рейтинг как смог, жду интересных статей
                        • 0
                          Аналогично поспособствовал
                      • 0
                        Отличная статья, спасибо.
                        • 0
                          Когда же я доберусь до изучения обджект Си :(
                          • +1
                            не пролог - эпилог :(
                            • 0
                              спасибо за статью! очень доступно описано!
                              • 0
                                Здрасти, меня интересует разработка именно интерфейсов.
                                Посоветуйте пожалуйста литературу(желательно на русском) по interface builder. Мануалы, сайты и прочую информацию, может быть программы-аналоги.

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