Pull to refresh

+(AppStore *) Timera: архитектура приложения и особенности разработки

Reading time7 min
Views6.9K
Настала пора поведать общественности о нашем приложении timera. C сегодняшнего дня его можно скачать в appstore.
Об архитектуре timera расскажет heximal (Павел), наш ios разработчик, у него сейчас read only, поэтому его пост публикую я.

— > В этом посте я напишу о моем скромном участии в интересном и перспективном проекте с весьма оригинальным названием Timera (от слов time и era). По удачному стечению обстоятельств я пребывал в активной фазе поисков новых горизонтов развития в тот самый момент, когда руководство стартапа искало кандидата на вакансию iOS-разработчика. Суть проекта довольно проста – ее визуальное представление и описание архитектуры можно видеть на главной иллюстрации к посту, под катом.



image

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

Прежде всего, хотелось бы пару слов сказать о глобальной архитектуре. Центральным звеном является сервер, выполняющий функции back-end, front-end, data storage, web-api. Подробно описывать серверную начинку не стану, поскольку никакого касательства к ней не имею. Cкажу лишь, что production-версия работает на облачном хостинге от Microsoft, а для реализации server-side использовался классический на сегодняшний момент стэк: IIS+MSSQL+ASP.NET

Front-end системы (веб-сайт www.timera.com) предоставляет пользователю возможность просматривать фотографии других пользователей, а так же оснащен функционалом социальной сети: присутствует возможность ставить лайки, оставлять комментарии, есть функция Поделиться в других социальных сетях. Так же у пользователя веб-сайта есть возможность загрузить старое фото. Что это значит, и для чего это нужно? Об этом далее.

Помимо веб-сайта неотъемлемую часть архитектуры составляет мобильное приложение. На сегодняшний день оно имплементировано для двух наиболее популярных платформ – это конечно же Android и iOS. Функционал приложения практически полностью воспроизводит функционал веб-сайта, за исключением единственной особенности, которой у веб-сайта быть не может. Речь идет о таймераграфии.

Термин «таймераграфия» используется для обозначения жанра исторического коллажа. Он не новый. Ведь эти работы давно гуляют по интернету и многие из них стали очень вирусными. Стоит только вспомнить проект Сергея Ларенкова «Связь времен» или Шона Клавера с его слайд-шоу о землетрясении в Сан-Франциско в 1906 году. Собственно, название приложения придумал Девид Вебб, основатель проекта, просто сократив термин «таймераграфия». Сейчас самое время немного пояснить саму механику таймераграфии и то, как этот процесс реализован в мобильном приложении Timera.
Предположим, у нас есть старая фотография Эйфелевой башни. Мне лично нравится фотография 1889 года, на которой башня построена лишь до первого уровня. Чтобы создать timera-снимок, нужно найти ракурс, с которого была создана старая фотография, сделать снимок, «перегнать» на компьютер, и в каком-нибудь графическом редакторе произвести совмещение, используя различные фильтры и градиенты. Задачка, скажем, не каждому по силам. Но зато она по силам каждому пользователю приложения Timera. Весь процесс максимально упрощен и даже может показаться весьма забавным (по крайней мере лично я его таковым и считаю).

Пользователь открывает экран с картой, на которой синими пинами отображаются старые фотографии доступные в том или ином месте. Естественно, приложение дает возможность автоматического местоположения пользователя и отображения объектов вокруг. После того, как пользователь определился с тем, что он будет таймераграфировать, запускается экран с камерой. На этом экране присутствует одновременно живое изображение с камеры и поверх нее полупрозрачное изображение старой фотографии (степень прозрачности можно менять). Пользователь может двигать старую фотографию по экрану, менять ее размер с помощью возможностей touch-screen технологии, таким образом производить первичное прицеливание. После того, как снимок сделан, открывается экран более точного совмещения.

image   image

Самое интересное начинается на третьем экране, где производится настройка time-tunnel (тайм-туннеля). Здесь пользователю доступен тулбар с различными инструментами, такими как:
— слайдеры прозрачности и резкости границы тайм-тоннеля (transparency and softness)
— поворот тайм-тоннеля вокруг оси
— функция, которая получила название swap (поменять местами старую и новую фотографии, то есть старая уходит на задний план, а новая на передний)
— изменение формы тайм-тоннеля: эллиптический или прямоугольный
— обрезка (crop)

Во всех режимах доступно изменение размера и положения тайм-туннеля с помощью экранных жестов.

Казалось бы негусто? Возможно, но даже с этим набором инструментов создаются очень неплохие композиции. И плюс ко всему, планируется расширять данный функционал, идей по этому поводу хватает.
После редактирования возникает получившееся изображение можно загрузить на сервер и/или расшарить в соцсети. При загрузке на сервер можно установить флаг приватности, если нет желания делать свое произведение достоянием общественности.
Здесь хотелось бы рассказать немного нюансов о сложностях, возникших при создании основного use-case (таймераграфировнии).

Во-первых, неприятной неожиданностью стало наличие двух значений типа UIDeviceOrientation. В прошлом мне доводилось работать с iOS-акселерометром и концепцией ориентации устройства, и тогда количество возможных ориентаций устройства равнялось четырем: UIDeviceOrientationPortrait, UIDeviceOrientationPortraitUpsideDown, UIDeviceOrientationLandscapeLeft, UIDeviceOrientationLandscapeRight. На экране фотографирования ориентация устройства весьма критична, поскольку overlay-изображение необходимо ориентировать соответствующим образом, чтоб старая фотка в итоге не получилась вверх ногами, или лежащей на боку. Как выяснилось, существует еще два положения устройства: UIDeviceOrientationFaceUp и UIDeviceOrientationFaceDown. Немало человекочасов ушло, чтобы осознать этот факт и распознать в нем причину неадекватного поведения программы.

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

[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(volumeChanged:)
name:@"AVSystemController_SystemVolumeDidChangeNotification"
object:nil];


Как выяснилось позже, связано это с тем, что у нас используется overlayView, на которой пользователь может менять прозрачность старой фотографии, и вообще видеть ее. Такое же точно поведение было обнаружено в других популярных фото-приложениях. В то же время приложения, которые используют стандартные контролы для фотографирования не страдают от отсутствия реакции на нажатие кнопок громкости.
Наибольшее время разработки ушло на реализацию экрана с эффектами тайм-туннеля. Алгоритм тайм-туннеля основан на методе вычитания градиентного трафарета из исходного изображения: чем темнее пиксель изображения на маске, тем меньше значение прозрачности одноименного пикселя маскируемого изображения. Вся магия реализована при помощи стандартного фреймворка CoreGraphics и функции CGImageCreateWithMask, которой передаются на вход два вышеупомянутых изображения в виде CGImageRef объектов.

Формирование таймераграфии происходит в три этапа. Условимся использовать для изображения на заднем плане обозначение «Изображение А», а для изображения на переднем плане «Изображение Б».

1. Отрисовывается прямоугольная маска с градиентными краями и применяться к Изображению Б. Это было сделано, чтобы временной туннель нигде резко не обрывался. Следует упомянуть, что пользователь имеет возможность повернуть «Изображение Б», например, если горизонт завален. Таким образом, маска формируется исходя из следующих параметров: размер «Изображения Б», угол поворота «Изображения Б», величина размытости границы (регулируется слайдером). Полученная маска вычитается из «Изображения Б», в итоге получается «Изображение Б» с размытыми краями.

image

Изрядно пришлось повозиться с разворотом градиента, который осуществлялся при помощи все того же CoreGraphics через преобразования матрицы координат графического контекста:

CGFloat angleInRadians = angle * (M_PI / 180);

CGContextRotateCTM(bmContext, angleInRadians);


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

image

Я даже задал вопрос на stackoverflow, после безуспешных поисков подсказки. Однако, не получив ответа, нашел решение эмпирическим путем, и оставил ответ на собственный вопрос (http://stackoverflow.com/questions/19219855/uiimageview-get-transformed-uiimage)

Другой неожиданностью стало отсутствие готового способа отрисовать прямоугольный градиент для маски (такой, как на иллюстрации). Для эллиптического решение в CoreGraphics есть, а вот именно для прямоугольного, со скругленными краями, пришлось изобретать (http://stackoverflow.com/questions/18918382/coregraphics-rectangle-gradient – еще один само ответ).

2. Отрисовывается градиентная маска и применяется к «Изображению Б», полученному в результате обработки на первом этапе. В отличие от первого этапа пользователь может задавать вид маски: прямоугольный или эллиптический плюс те же самые размеры маски, угол поворота, степень размытости границы. Размеры маски и положение пользователь меняет пальцами на экране. Таким образом формируется изображение тайм-туннеля.

image

3. На последнем этапе полученное изображение тайм-туннеля отрисовывается в нужных координатах на «Изображении А»

image

Должен заметить, что CoreGraphics далеко не самая производительная вещь на свете, и особенно это заметно на старых девайсах, начиная с iPhone 4 и ниже. В связи с этим был принят ряд мер по оптимизации функционала таймераграфии. Но об этом и многом другом во второй части.

Надеюсь, я вас заинтересовал. Качайте приложение, оценивайте и пишите фидбек.
Если понравилось, не откажусь от инвайта. Буду отвечать на комментарии и публиковать со своего аккаунта.
Жду вопросы и комментарии.
Tags:
Hubs:
Total votes 21: ↑17 and ↓4+13
Comments19

Articles

Information

Website
timera.com
Registered
Founded
Employees
2–10 employees