Использование Storyboard

    Вступление


    В недавно вышедшем iOS 5 появился удобный механизм разработки интерфейса программы — Storyboard. Этот механизм позволяет заметно уменьшить количество кода связанного с переходами между экранами, показами Popover'a и даже с настрокой ячеек в таблице.

    Задача: По нажатию на кнопку показать следующий экран поместив его в текущий NavigationController.
    Решение без Storyboard:

    DDetailViewController* nextController = [DDetailViewController new];
    [self.navigationController pushViewController:nextController animated:YES];
    [nextController release];
    

    Решение с использованием Storyboard:


    Вот и все! Никакого кода!

    Как это работает


    В проекте создается файл .storyboard и весь интерфейс загружается из него. Внутри этого файла описана сцена.


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


    Эта стрелка описывает отношения между контроллерами, в данном случае rootViewController NavigationController'a соответствует FirstViewController. Эту связь можно задать так же как задаются связи аутлетов и экшенов (к примеру, можно провести линию от одного контроллера к другому с зажатым Ctrl).


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

    На первый взгляд все кажется очень тривиальным, но сразу встает вопрос, что же делать если надо передать данные при переходе из одного контроллера в другой?

    Перехват показа экрана


    Каждый переход между контроллерами описывается классом UIStoryboardSegue, в его интерфейсе всего три свойства: identifier, sourceViewController, destinationViewController. Именно этот клас и будет передан нам в метод
    - (void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender

    Этот метод будет вызван у контроллера из которого был начат переход, в нашем случае это будет FirstViewController. Определим его следующим образом:
    - (void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender
    {
        if ([[segue identifier] isEqualToString:@"showDetail"]) 
    	{
            [[segue destinationViewController] setText:@"SecondViewController"];
        }
    	else
    	{
    		[super prepareForSegue:segue sender:sender];
    	}
    }

    Значение identifier было задано в настройках перехода.

    Storyboard на iPad


    В айфоне одновременно может выполняться лишь одна сцена, то на айпаде есть исключения в виде поповеров. Для показа поповера используется класс UIStoryboardPopoverSegue, наследник UIStoryboardSegue. В этом класе добавлено одно свойство — popoverController, которое всегда возвращает поповер, в котором показывается текущая сцена.

    Микс кода


    Не во всех ситуациях переходы можно описать в Storyboard. В этом случае можно миксовать Storyboard со старыми, привычными методами отображения и перехода между контроллерами. Тут тоже есть небольшие упрощения:
    • У класса UIViewController появились новые методы и свойства для работы со Storyboard, одно из них — storyboard, которое возвращает текущий Storyboard сцены.
    • Если переход уже есть в Storyboard и ему задан идентификатор, то переход можно вызвать используя следующий метод принадлежащий UIViewController:
      - (void)performSegueWithIdentifier:(NSString*)identifier sender:(id)sender
      

    • Если перехода не описан в Storyboard, но описан контроллер, то можно вызвать его используя метод принадлежащий UIStoryboard:
      - (id)instantiateViewControllerWithIdentifier:(NSString*)identifier
      

    • В остальных случаях можно использовать старые методы.


    Приятная мелочь


    Как и обещал в самом начале статьи, теперь возможно настраивать UITableViewCell прямо в storyboard! Можно как выбирать из готовых типов ячеек так и делать полностью свои!

    Сомневаюсь в юзабилити такого интерфейса, но раньше у меня ушло бы на него куда больше времени. Если не определять методы UITableViewDataSource в контроллере, то табличка будет именно так же выглядеть в коде. Если же нам надо наполнять таблицу динамически, то используем старый код, за исключением того, что метод таблицы
    - (id)dequeueReusableCellWithIdentifier:(NSString *)identifier
    теперь всегда возвращает ячейку. Если такой ячейки нету в очереди, то она будет создана.

    Как добавить Storyboard в проект


    • Если создаете новый проект, то тут все просто — ставим галочку Use Storyboard.
    • Если надо добавить новую часть интерфейса в существующий проект и захотелось попробовать Storyboard, то делаем следующее: добавляем новый файл в проект, в виззарде выбираем в секции iOS->User Interface тип файла Storyboard. Дальше используем класс UIStoryboard для создания сцены.
    • Если хотим перевести весь интерфейс в существующем проекте на Storyboard, то делаем как в пукте выше, переносим туда весь интерфейс и обязательно в настройках проекта выбираем интересующий нас таргет и на вкладке Summary удаляем все в поле Main Interface и вписываем название сцены в Main Storyboard:


    Заключение


    Конечно, сейчас еще рано начинать интегрировать Storyboard в свои проекты, так как нужна поддержка более ранних версий, но технология выглядит заманчиво. Кроме того, если не хватает стандартных типов переходов, всего можно написать своего наследника UIStoryboardSegue.

    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 30

      +7
      Последнее несколько дней тоже со сторибордами заморачиваюсь.

      Вы бы хоть конкретные примеры привели, что ли.

      Нашел несколько толковый статей о Storyboard-ах на английском — Beginning Storyboards in iOS 5 (Part 1 и Part 2), Simple iOS 5 UI Design Tutorial Using Storyboard in XCode 4 и Introducing Interface Builder Storyboarding.
        0
        Какие примеры интересуют? По сути, для уже писавшего под айфон до iOS 5 вопросов быть не должно, так как просто выкидываем часть кода и рисуем связь визуально.
          0
          А я вот, считайте, не писавший.

          Или примеры перевода проектов, основанных на xib-ах на сториборды, или просто создание простенького приложения с нуля.
            –2
            XCode -> File -> New -> New Project… -> Master-Detail Application -> checkmark
        0
        Интересная технология — спасибо за описание. Сам еще, правда, не разбирался. А в чем магическая сущность файла .storyboard? Почему нельзя было сделать тоже самое на основе .xib файлов? Файл .storyboard — это xmlина или может бандл?.. Представляю как теперь будет прикольно мерджиться когда весь проект в одном файле, если это так :)
          0
          Внутри обычный XML, но очень даже человекочитаемый, думаю, что резолвить кофликты можно будет без проблем. По поводу много .xib или один .storyboard, то тут не так все просто — зависит в первую очередь от задач. Довольно много программ можно сделать чисто используя переходы описанные в сториборде. С другой стороны — если это будут десятки экранов, то даже не знаю на сколько удобно будет в этом разбираться.
            0
            Можно иметь несколько сторибордов в проекте.
              0
              А можно один сторибоард связывать с другим визуально, без кода? Как, например, лейзи подгрузка UIViewController с другого ниба?
                0
                Я не нашел способа это сделать.
                  0
                  Создать UIViewController внутри Storyboard, указать для него ваш класс, внутри которого initWithNib — ваш ниб. Или вы что-то другое имели ввиду?
                  • UFO just landed and posted this here
                      0
                      Правильно понимаете.
              +2
              От себя хочу добавить ссылку на документ Apple где описано — как перевести приложение с использования .xib-файлов на использование Storyboard: ссылка. Всё довольно просто, но, по идее, приложение после этого только на iOS 5 работает (поправьте, если неправ).

              Правда в своём проекте поймал неприятный баг — когда добавляешь локализацию к .storyboard-файлу — при билде никаких после этого внесённых изменений не появляется =( и такой баг только в конкретном проекте, в новом свежесозданном всё ок.
                +1
                А эти идентификаторы контроллеров так и таскать по коду строками? Это же ужас какой-то…
                  0
                  Строка в Cocoa больше, чем строка! :)
                    +3
                    А, ну да, точно, строка с собакой!
                    0
                    Согласен, что не очень красиво, но можно, к примеру, описать эти строки как константы в отдельном файле.
                      0
                      Константы константами, но ведь получается двойная работа, одну определять в Storyboard строителе, другую где-то в файле… Могли бы придумать что-нибудь удобнее.
                      0
                      Я так пишу —
                      NeededViewController* nVC = [[self storyboard] instantiateViewControllerWithIdentifier:[NeededViewController description]];
                      А вообще сториборд не люблю — когда там количество контроллеров переваливает за пару десятков, начинает лагать, не дружит с мержем систем версионирования, формошлепство.
                      0
                      Спасибо за интересную статью, однако присоединюсь к некоторым комментирующим на тему, что можно было бы и более конкретные примеры кода привести, ибо не все читающие такие кабаны в разработке под iOS, чтобы понять детали реализации механизма Storyboard из одной только этой статьи.
                        +1
                        Спасибо, в следующий раз постараюсь писать подробней.
                          0
                          Думаю, публика будет признательна! :)
                        0
                        Что, интересно, такого делает метод [super prepareForSegue:segue sender:sender], что вы его вызываете только в определенном случае?
                          0
                          В документации ничего не сказано про реализацию этого метода в родительском классе, но так же нету и слова о необходимости вызова реализации предка. Во всех примерах эппл, реализация предка тоже не вызывается. Из этого я сделал вывод, что ее можно не вызывать. Но так как я описывал общий случай, то класс из примера мог быть наследником другого класса с переопределенным методом. Чтоб небыло пересечений я его вызываю лишь в случае, если этот переход не хочу обработать сам.
                          0
                          Сомнительное удовольствие обрабатывать prepareForSegue, куда удобнее сделать конструктор initWithParams у контроллера, а внутри при необходимости вызвать initWithNib (контроллер-то знает как его xib называется) и обработать входные параметры. Не говоря уже о том, что на данный момент ограничиваться поддержкой iOS 5+ — не вариант ни разу.
                            +1
                            Статья как раз о технологии доступной только с iOS 5. Не писать ее лишь потому, что сейчас главный недостаток требование последней прошивки — глупо. Указаные Вами методы, в контексте сториборда, не применимы, так как за инициализацию контроллеров ответственен сам сторибоард.
                              0
                              Я и не предлагал использовать вариант с кастомным конструктором в контексте сториборда =)
                              Напишу чуть более подробно. К статье претензий нет, претензии к самой идее/реализации storyboard'а. Чтобы сэкономить 3 строчи кода из начала статьи мне нужно городить огород:
                              — добавлять в публичный интерфейc sender'а (который передается в prepareForSegue) публичный метод/property, который ему ни разу не нужен
                              — хранить параметры неопределенное время (потому что неизвестно когда вызовут prepareForSegue), вместо того, чтобы отдать в конструктор авторелизнутый объект
                              — отказываться от кастомных конструкторов
                              — использовать никак не синхронизированные между кодом и storyboard'ом идентификаторы, что многократно увеличивает вероятность ошибок
                              — терять возможность быстро по ctrl-F найти вызывающий код
                              — обязывать приложение работать на iOS 5+
                              — наверняка будет еще пачка неочевидных моментов
                              Поэтому главный вопрос — нафига все это счастье нужно? =)

                            +1
                            Когда на WWDC представили Prototype Cells и Static Cells я зааплодировал вместе со все залом, хотя смотрел дома )
                              0
                              В свете требования iOS 5 для Storyboard — а какой процент устройств сейчас на 5-й версии, может где-то можно подсмотреть статистику, хотя бы приблизительно?
                                0
                                Видел где-то про 40%, хотя вопрос актуален.

                              Only users with full accounts can post comments. Log in, please.