Хитрости разработчика под iOS. Splash Screen

Вступление


Приветствую, хабравчане!

В ходе разработки приложений под iOS у меня накопились некоторые хитрости, которыми я хотел бы поделиться с Вами.

Сразу предупрежу, что я стараюсь шагать в ногу со временем, поэтому примеры будут под iOS >=5.0 и использовать Storyboard и ARC, но ничего не мешает их портировать на 4.*.

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

Заинтересовавшимся — прошу под кат (Пост содержит скриншоты областей Interface Builder-а, поэтому предупреждаю о траффике).



Splash screen


По умолчанию, iOS предоставляет механизм отображения картинки загрузки приложения в виде указания png-файлов под определённые разрешения, этим сейчас никого не удивишь. Но iOS славится своими transition-ами, анимациями и прочим, это смотрится прикольно и благодаря API Core Animation делается совсем не сложно.

Мне всегда нравился стартовый экран у Skype.app, когда логотип приложения после загрузки уходил вверх, а на экране появлялись элементы ввода. Красиво и легко.

Используя Storyboard, задача становится тривиальной:

Допустим, у нас есть некоторый экран, на который мы попадаем при старте приложения:


А наш Default.png выглядит примерно так:


Если сейчас запустить приложение, то сначала мы увидим Default.png, а потом сразу же попадём на стартовый экран. Выглядит не очень, согласитесь? Чтобы исправить это, мы создадим ещё один экран, начальное состояние которого будет идентично по виду нашему Default.png и будет содержать outlet UIImageView. сделаем его стартовым и укажем базовый класс, например, SplashScreenViewController:


View очень простой, фон + UIImageView с логотипом по центру. Он будет связан с SplashScreenViewController посредством аутлета с именем imageView.

Теперь создадим Segue от только что созданного экрана к стартовому экрану.

Для тех, кто только изучает Storyboard, или просто забыл, напоминаю, что чтобы создать Segue не по событию, а просто как связь, вы должны начать тянуть его от StatusBar-а исходного ViewController-а и вызывать его вручную (как — покажу дальше в коде):


Параметры:
  • identifier: splash
  • Style: Custom
  • Segue Class: SplashSegue (мы создадим этот класс чуть позднее)


Код для этого примера будет выглядеть примерно так:

@interface SplashSegue : UIStoryboardSegue
@end

@interface SplashScreenViewController : UIViewController

@property IBOutlet UIImageView *imageView;

@end

@implementation SplashScreenViewController

- ( void ) viewDidAppear:(BOOL)animated
{
	[super viewDidAppear:animated];

	// Анимируем стандартными средствами Cocoa
	[UIView animateWithDuration:1.0 delay:0.2 options:0
					 animations:^
					 {
						 // Сделаем простую анимацию смещения логотипа вверх (в моём примере
						 //  логотип встаёт на позицию, которая совпадает с онной в стартовом экране,
						 //   чтобы переход не был заметен
						 CGRect frame = self.imageView.frame;
						 frame.origin.y = 15.0;
						 self.imageView.frame = frame;
					 }
					 completion:^( BOOL completed )
					 {
						 // По окончанию анимации выполним наш переход к стартовому экрану
						 [self performSegueWithIdentifier:@"splash" sender:self];
					 }];
}

@end


@implementation SplashSegue

- ( void ) perform
{
	// Для перехода к стартовому экрану будем использовать стандартный presentModalViewController.
	// Обратите внимание на параметр animated, я намеренно установил его в NO, т.к. анимацию перехода
	// мы выполнили заранее. 
	[[self sourceViewController] presentModalViewController:[self destinationViewController] animated:NO];
}

@end


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

Данная простая по виду задачка анимации перехода от экрана загрузки к стартовому учит нас:
  • Программно создавать переходы (Segue) между экранами в Storyboard-е
  • Реализовывать собственную логику Segue
  • Не бояться пользоваться встроенными средствами Cocoa и выполнять то, что оценивается человекоднями, за человекочасы:)


Я сам данной платформой занимаюсь относительно недавно и порой мне не хватает таких вот уроков, и я уверен, что найдутся те, кому это так же будет полезно, так что не судите строго, если для вас он оказался бесполезным;)

UPD:
Добавил видеодемонстрацию эффекта в эмуляторе
Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 13

    +5
    Главное не увлекаться, и не забывать iOS Human Interface Guidelines:
    Start Instantly

    Display a launch image that closely resembles the first screen of the app. This practice decreases the perceived launch time of your app.

    Avoid displaying an About window or a splash screen. In general, try to avoid providing any type of startup experience that prevents people from using your app immediately.
    Ладно если приложению действительно нужно при запуске обработать что-то «тяжёлое», но если простенький калькулятор при открытии показывает 5-секундную заставку…
      +1
      Абсолютно с Вами согласен и ни в коем случае не подталкиваю использовать данную механику в каждом приложении, даже наоборот. Просто часто с «верхов» приходят «хотелки», которые приходится реализовывать, и лучше когда это делается логично, нежели чем костылями.

      Поэтому:

      Автор топика не несёт ответственности за нарушение iOS HIG, а так же любых других сводов разумных правил. Ни одного приложения при написании статьи не пострадало и не получило reject:)
        0
        С другой стороны, раздражает, когда аппликуха показывает «картинку похожую на первый экран», на которой нельзя ничего ткнуть потому что сама аппликуха еще прогружается.
        0
        Читал как-то давно уже про хак со сплэш-скрином, когда в Info.plist делается указание не на реальный файл, а на несуществующий файл в /Documents приложения, а приложение во время работы делает и сохраняет по этой ссылке скриншоты себя. В итоге, при первом запуске — черный экран, а при всех дальнейших — экран последнего состояния, то есть так, как работают системные приложения iOS — почта, калькулятор… Только вот для реализации этого всего надо еще озаботиться и реализовать процедуру восстановления последнего состояния приложения после рестарта.
        0
        Мне было лень создавать отдельный view controller, поэтому прямо в делегате приложения, в
        - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

        я сделал такую штуку:

            UIImage *backImg;
            
            if ([UIScreen mainScreen].scale > 1.0 && self.window.bounds.size.height > 480)
            {
                backImg = [UIImage imageNamed:@"Default-568h"];
            } else
            {
                backImg = [UIImage imageNamed:@"Default"];
            }
            
            UIImageView *background = [[UIImageView alloc] initWithImage:backImg];
            [self.window addSubview:background];


        и туда же добавил
        UIActivityIndicatorView


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

          Просто я не люблю «засорять» AppDelegate, а так же очень люблю Storyboard и стараюсь по максимуму использовать его возможности, а так же расширять их. Я визуалист и стараюсь по-максимуму использовать доступные средства визуализации, чтобы видеть то, что я делаю, и появления Storyboard я ждал очень давно:)
          +2
          А не было ли у кого опыта работы с отображением видео «вместо» сплеша? Просто в свое время хотел отдать игру паблишеру он прислал видео и попросил проигрывать в начале, но я тогда забил и так до конца и не разобрался как проигрывать видео, никак не мог при начале воспроизведения скрыть кнопки плеера.
          PS По теме могу только сказать, что лучше поддерживать 4.3, еще очень актуально для любого уважающего себя приложения.
            0
            Думаю Вам поможет вот эта ссылка и конкретно:
            mMoviePlayer.controlStyle = MPMovieControlStyleNone;
            


            А про 4.3 — у Apple политика «хочешь получить новую версию продукта на всех устройствах — добавь решающие функции, недоступные на предыдущих платформах», я понимаю, что это в какой то степени плохо, но я стараюсь прислушиваться заветов монастыря, в который пришёл. В какой то степени ARC и Storyboard — это инструментарий любого уважающего себя программиста:)
            –1
            ARC — неотимизированный код и во многих случаих им пользуються те кто не знает как устроена memory management, писать с ARC вам никто не мещает и под 4.3 насчет стори-бордов не могу сказать, так как точно не знаю. В реальных проектах надо поддерживать 4.3 так как очень много клиентов не обновляют прошивку — ознакомтесь со статистикой. И политика Apple тут совсем не при чем. И вообщем-то мне совсем непонятно к чемы вы написали эту фразу «хочешь получить новую версию продукта на всех устройствах — добавь решающие функции, недоступные на предыдущих платформах».
              0
              ARC на оптимизированность кода напрямую никак не влияет, на самом деле. Зато я встречал проекты, где повсюду было [[[SomeClass alloc] init] autorelease], и я думаю Вы осведомлены, какой результат даёт такой код.

              Статистику я неплохо знаю, про 10-15% 4.3 слышал, но в собственных проектах я могу себе позволить их игнорировать, ибо это ускоряет разработку приложений раза в 2.

              А по поводу политики Apple — попробуйте Xcode 4.2 поставить на 10.6. Ну и тому подобное:)
                0
                Вызывающе неверная информация.
                ARC местами производит даже более оптимальный код, чем ручное управление памятью.

                Пример 1: в ручном управлении памятью Вы явно посылаете сообщения retain/release объектам, ARC же вставляет не посылку сообщений, а прямой вызов objc_release/objc_retain. Это как минимум быстрее чем посылка сообщений (т.к. это прямой вызов, без поиска метода, соответствующего селектору).

                Пример 2: в ручном управлении памятью прежде чем вернуть объект как результат метода обычно делается autorelease. В вызывающем коде обычно сразу же после возврата объекта делается ему retain. ARC за счет хитрого рантайм-анализа стека вызовов и наличия в функциях вверх по стеку дальнейшего вызова retain, он 1) не вызывает autorelease 2) выставляет флаг, превращающий retain в caller'е в nop. Т.е. вот это вот передергивание retain -> autoreleae -> retain исключается.

                Пример 3: реализация autorelease pools под ARC.

                Подробности: www.mikeash.com/pyblog/friday-qa-2011-09-30-automatic-reference-counting.html
                +1
                KysokZla,
                >ARC — неотимизированный код и во многих случаих им пользуються те кто не знает как устроена memory management
                Не хотелось бы вас расстраивать, но вы, кажется, не понимаете суть ARC. Это не сборщик мусора. И если следовать Cocoa naming conventions код не будет отличаться от кода с ручным управлением памятью.
                Storyboards только с iOS 5.

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

                Самое читаемое