24 октября 2013 в 16:15

Blur в iOS7

С седьмой версией iOS многие элементы UI стали выглядеть привлекательнее (см. Session 226, Implementing Engaging UI on iOS).

Например, теперь поп-оверы, большинство баров и алерты имеют размытый фон.



Что делать, если хочется добавить блюр в свой контрол, подскажет эта статья.


Блюр бывает разный


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

Реализовать блюр несложно. Общая схема такая: сделать снимок фона за view, (например, при помощи renderInContext), размыть его и установить полученное изображение в качестве фона view. В случае динамического блюра все вышеперечисленные действия выполняются по таймеру. Как пример такого подхода рекомендую глянуть FXBlurView.

Главным плюсом 7-ки в плане блюра является высокая скорость работы. Например, можно смотреть видео и при этом панель с регулировкой громкости будет блюриться в реальном времени без каких-либо лагов.

Итак, способы реализовать быстрый блюр в iOS 7.

Кража лейера


Идея довольно проста. Раз у UIToolbar есть прозрачность, то можно взять и использовать его лейер в качестве фона.

UIToolbar *toolbar = [[UIToolbar alloc] initWithFrame:[self bounds]]];
[toolbar setBarTintColor:[UIColor greenColor]];
[self.layer insertSublayer:[self.toolbar layer] atIndex:0];

Подход хорош, работает стабильно и очень прост.
Уже есть готовая реализация под названием iOS-blur (доступна через CocoaPods).

Использование drawViewHierarchyInRect:afterScreenUpdates


Если стоит задача наворотить хитрый блюр, то придется самим брать фон и размывать его.
К счастью, в iOS 7 появился чрезвычайно быстрый способ создания снимков view drawViewHierarchyInRect:afterScreenUpdates.


На картинке из презентации видно, что скорость получения снимка view возросла в 15 раз!

Использовать довольно просто (готовая реализация):

-(UIImage *)blurredSnapshot
{
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, self.window.screen.scale);
    [self drawViewHierarchyInRect:self.frame afterScreenUpdates:NO];
    UIImage *snapshotImage = UIGraphicsGetImageFromCurrentImageContext();
    UIImage *blurredSnapshotImage = [snapshotImage applyLightEffect];
    UIGraphicsEndImageContext();
    return blurredSnapshotImage;
}

В данном примере для блюра испосльзуется категория UIImage+ImageEffects взятая из WWDC и использующая мощь Accelerate Framework. Конечно можно блюрить и чем нибудь другим. Например CIFilter или его приватной реализацией как сделано в JMIBlurView. Главная мысль в том, что с 7-ки можно персестать использовать renderInContext, который отрабатывает почти секунду и добиться real-time блюра без исользования шейдров.

Как пример реализации динамического блюра рекомендую глянуть 7blur.

Заключение


Блюр на iOS7 работает очень быстро, и его несложно реализовать. Так что смело используйте его в своих приложениях, не боясь потерять производительность.
Автор: @Fanruten
e-Legion Ltd.
рейтинг 84,85
Лидер мобильной разработки в России
Похожие публикации

Комментарии (25)

  • 0
    По мойму, очень хорошо а лаконично всё описано. Спасибо большое за обзор разных способов!
  • +1
    Как раз вовремя. Правда я уже реализовал real time blur, но все равно спасибо, я утвердился в мысли, что все правильно сделал. Кстати есть еще одна реализация динамического блюра, основанная на небольшом реверс-инжиниринге uitoolbar и использовании CAFilter. Правда это закрытый API, не факт, что Apple пропустит.
    • +1
      А почему-бы не использовать для этого CIFilter (CIGaussianBlur), он немного медленнее, но зато встроенный родной и стандартный.
      А вот кстати хорошая статья на сравнение разных подходов.
      А тут наглядная демонстрация встроенных фильтров.
      • 0
        Они медленные. Очень медленные.И CIFilter применяется к картинке, а CAFilter применяется к UIView.
        • 0
          CAFilter нельзя использовать, картинку из UIView получить — тривиальная задача, она всегда доступная через бэкинг стор у Layer этого вью.
          Они не такие и медленные, долго создается контекст, если держать один контекст во время операции динамического размытия, то всё будет ок.
      • 0
        Особой разницы, чем блюрить нет.
        Можно CIFilter, но UIImage+ImageEffects из WWDC работающий с Accelerated Framework гораздо лучше. Проблема в том, что renderInContext, который приходилось использовать до 7-ки работает очень медленно и real time blur с ним фиг сделаешь.
        • 0
          Ну так и CIGaussianBlur появился только в семерке вроде.
        • 0
          Тошнотворно он работает. Ужасное размытие, если сравнивать с Ps.
          • 0
            Беру свои слова обратно — забыл, что работаю в 5 битном режиме 8-|
        • 0
          В частности поэтому и (UIView *)snapshotViewAfterScreenUpdates:(BOOL)afterUpdates
  • +3
    Я бы с удовольствием почитал, как реализовать такой же эффект в вэбе.
    (результаты моих экспериментов совсем не так красивы)
  • 0
    А на каких устройствах вы тестировали? Хотелось бы узнать скорость работы начиная с iPhone 4S. На 4-ке тоже было бы интересно узнать, но думаю и так ясно, что на нем будет гораздо медленнее работать.
  • +2
    Или можно сделать в ворде =)
  • +1
    А мне в iOS7 блур не понравился, т.к. иногда кажется, что экран испорчен/грязный, а оказывается это что-то на заднем плане.
  • +1
    Кстати, странно что Apple не включили в новые API возможность без каких-то ухищрений блюрить вьюхи так же как UIToolbar, было же очевидно что все ломанутся использовать эту возможность. Тем более что их же приложения вовсю эту возможность используют, например AppStore на iOS. И что мы в итоге получаем? Кучу приложений с растянутым UIToolbar и кражу лейера, несерьезно.
    • +2
      Насколько я заметил, то, что автор топика привел как «готовую реализацию» на самом деле просто выпилено из кода примеров WWDC. Статья тоже по сути просто вольный пересказ небольшого кусочка одной из сессий WWDC.
      • 0
        Готовых реализаций полно на gitlab и нет смысла изобретать велосипед.
        Задача поста рассказать о там, как без лишних телодвижений реализовать блюр.
        Из WWDC, пожалуй только applyLightEffect.
    • 0
      Насколько помню, текущая реализация слишком низкоуровневая (буквально на уровне драйверов видеокарты). Не включили по соображениям безопасности и целостности API.
  • 0
    Нативный блюр в iOS делается с помощью закрытых методов API, которые работают на очень низком уровне прямо с графическим буфером. Только это способно дать такую скорость отрисовки.
    Снэпшоты подходят для статики, но динамику ими уже не нарисовать — будут тормоза (например, не выйдет сэмулировать блюр в навигейшн баре и списке прокрутки).
    • 0
      А куда более низкоуровневей? бэкинг-стор у CALAyer — это ни что иное как память видеокарты, ты веделяешь себе кусок и все операции происходят не в CPU, а в GPU с помощью конвеера.
      Другой вопрос, что тут для эффектов испольузется блжндинг двух буферов, и для этого нужно иметь доступ к обоим бУферам. Поэтому мне кажется проблема состоит в том, что:
      — с соображений безопасности
      — не унифицировали API, поэтому не могут еще гарантировать обратой совместимости
      — вообще решили, что не нужно это разработчикам.

      Или всё вместе.
  • 0
    Вместо renderInContext новый метод советуют в приложении-примере.
  • 0
    А кто-нибудь пробовал библиотеку iFlou вот отсюда? iFlou — The Blur Effect you need!
    Blur эффект для iOS в данном случае использует технику, при которой используется заранее подготовленная картинка

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

Самое читаемое Разработка