Красивые фоновые текстуры в iOS

Друзья.
Сегодня речь пойдёт о создании красивых бэкграундов в iOs приложениях с точки зрения программиста, расчёте их разрешений, специфики разных Apple устройств и обходе подводных камней. Многое из этого для большинства iOs разработчиков покажется очевидным, но я буду рад, если для некоторых это станет инструкцией при непосредственной работе.

Итак, мы должны ответить на следующие вопросы:
  1. Какие разрешения выбирать для картинок.
  2. Как именовать картинки.
  3. Как это использовать.


Какие разрешения выбирать для картинок


Не важно, для iPad, iPhone, iPod или универсальное приложение мы пишем, будут поддерживаться обе ориентации устройства или только одна, в любом случае, нам бы хотелось, чтобы задумка дизайнера с фоном была правильно реализована точно как на картинке-прототипе. Для этого нам, в первую очередь, необходимо избежать масштабирования изображения, к тому же это также благоприятно повлияет на performance, что тоже неплохо. Поэтому нам нужно рассчитать точные размеры рабочей области окна (размер бэкграундной картинки).

Исходные данные
Разрешения iOs устройств:
  • iPhone 320x480
  • iPhone (Retina) 640x960
  • iPhone 5 (Retina) 640x1136
  • iPad 768x1024
  • iPad (Retina) 1536x2048


Высоты элементов:

  • Status Bar 20pt (20px без Retina и 40px с Retina)
  • Navigation Bar 44pt (44px без Retina и 88px с Retina)
  • Tab Bar 49pt (49px без Retina и 98px с Retina)


Теперь просто вычитаем из высоты экрана количество точек, что «съедают» элементы управления и получаем размеры картинок для бэкграунда, учитываем обе ориентации, если необходимо.

Например, если у нас присутствуют status bar и navigation bar, но нет tab bar, мы получаем:

Портретная ориентация:

  • iPhone 320x416
  • iPhone (Retina) 640x832
  • iPhone 5 (Retina) 640x1008
  • iPad 768x960
  • iPad (Retina) 1536x1920

Альбомная ориентация:
  • iPhone 480x256
  • iPhone (Retina) 960x512
  • iPhone 5 (Retina) 1136x512
  • iPad 1024x704
  • iPad (Retina) 2048x1408


Как именовать картинки


Согласно документации Apple среда сама выберет картинку нужного разрешения (для Retina или без) для нужного устройства, если в ресурсах присутствуют файлы с соответствующими суффиксами. Для Retina экранов это "@2x", для iPhone — "~iphone", для iPad — "~ipad". Внимание! Суффиксы чувствительны к регистру.

Кроме того, нам нужно учесть так же в названиях обе ориентации (предлагаю это сделать без суффикса для портретной и с помощью суффикса «l» для альбомной) и увеличенный размер картинки для iPhone 5 (многие это делают с помощью суффикса "-568h"). Эти суффиксы не обрабатываются системой автоматически и нам придётся делать это вручную.

Таким образом мы получаем список имён файлов, которые надо добавить в ресурсы:

  • iPhone background~iphone.png
  • iPhone (Retina) background@2x~iphone.png
  • iPhone 5 (Retina) background-568h@2x~iphone.png
  • iPad background~ipad.png
  • iPad (Retina) background@2x~ipad.png

Альбомная ориентация:
  • iPhone backgroundl~iphone.png
  • iPhone (Retina) backgroundl@2x~iphone.png
  • iPhone 5 (Retina) backgroundl-568h@2x~iphone.png
  • iPad backgroundl~ipad.png
  • iPad (Retina) backgroundl@2x~ipad.png


Как это использовать


Итак, картинки нужных разрешений созданы, теперь давайте заставим их работать так, как нужно.
Для начала сделаем функцию loadBgImage, которая будет выбирать нужную текстуру. Напоминаю, что нам вручную нужно подставлять только 2 суффикса к названию.

- (UIImage *) loadBgImageWithLandscapeOrientation: (BOOL) isLandscape
{
    static BOOL isIphone5 = [UIScreen mainScreen].bounds.size.height == 568;
    NSString * imageName = @"background";
    if (isLandscape)
    {
        imageName = [imageName stringByAppendingString: @"l"];
    }
    if (isIphone5)
    {
        imageName = [imageName stringByAppendingString: @"-568h"];
    }
    return [UIImage imageNamed: imageName];
}


А теперь добавим в наш view controller следующую функцию:

-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    [super willAnimateRotationToInterfaceOrientation: toInterfaceOrientation duration: duration];
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:duration];
    self.tableView.backgroundView =  [[UIImageView alloc] initWithImage: [self loadBgImageWithLandscapeOrientation: UIInterfaceOrientationIsLandscape(toInterfaceOrientation)]];
    [UIView commitAnimations];
}


Добавим аналогичный код первоначальной загрузки картинки в viewDidLoad и всё!

Обратите внимание, что мы меняем бэкграунд при смене ориентации, используя Core Animation, поэтому всё должно быть не только быстро, но и красиво. Кроме того, чтобы это работало ещё быстрее, советую растеризовать объекты, находящиеся на этом фоне, установив у них свойство view.layer.shouldRasterize в YES и указав в свойстве view.layer.rasterizationScale необходимый коэффициент маштабирования [UIScreen mainScreen].scale.

Всё, мы справились с заданием, которое ставили себе в самом начале.

Спасибо за внимание.
Поделиться публикацией

Похожие публикации

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

    0
    "-568h" же вроде?
      0
      Спасибо большое. Исправил опечатку
      0
      Как-то даже не для новичков статья, а для тех, кто вообще ни разу не писал ни на чём. Для новичков в программировании.
        +1
        Статья под девизом «Чтоб долго не гуглить». Примерно в такой форме я объяснял дизайнеру, почему нужно столько текстур рисовать
          0
          Хм, ну тогда надо и во вступлении про дизайнеров написать — действительно, для общения с некоторыми было бы полезно.
        0
        Кое-что из опыта для тех, кто работает со списками. Помните, что если в таблице ставите бэкграундом «цвет» из картинки, то движок будет применять эту картинку к каждой ячейке, а не к таблице целиком. Это нужно учитывать, особенно, если ячейки разновысотные. Не все текстуры применимы в таких условиях.
          +2
          [UIImage imageNamed: imageName]

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

          NSString *fileName = [[NSBundle mainBundle] pathForResource:@«foo» ofType:@«png»];
          UIImage *image = [UIImage imageWithContentsOfFile:fileName];

          Кроме того

          [UIView beginAnimations:nil context:NULL];

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

            Так уже ж с четвертой оси оптимизировали — все выгружается после использования.
              +1
              В каком смысле «оптимизировали»? То что он кеширующий — и есть оптимизация, он работает как запланировано. И его назначение это загрузка небольших изображений, которые будут использоваться повторно. И в этом смысле он работает так же как раньше, что подтверждается документацией:

              If you have an image file that will only be displayed once and wish to ensure that it does not get added to the system’s cache, you should instead create your image using imageWithContentsOfFile:. This will keep your single-use image out of the system image cache, potentially improving the memory use characteristics of your app.

              Возвращаясь к теме статьи: если у вас на каждом экране есть бэкграунд и всегда используется одна и та же картинка, то можно использовать и его. Но если бэкграундов много или он не на всех экранах, то зря забьёте память.
                0
                Оптимизировали в том плане, что как раз таки выгружаются все не используемые картинки в момент прихода memory warnings, давным-давно, картинки загруженные с imageNamed не выгружались никогда.
                  0
                  В приложении с большим количеством картинок На 5 или 6 оси сталкивался с этой проблемой, на iPad заканчивалась память при использовании этого метода. При переходе на imageWithContentsOfFile проблема пропала.

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

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