Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

(AppStore *) Timera: зодчество приложения и особенности разработки. Часть 2

Anna | 2.07.2014 | нет комментариев

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

В этой части наш iOS разработчик – heximal – расскажет о том, как реализовывалась гео-позиционно-картографическая функциональность. Так как у него read only то данный пост вновь публикую я (будет изумительно, если кто-нибудь даст ему инвайт). С его слов все записано правильно. 

— «Первоначально timera проектировалась, как средство создания временнЫх туннелей. Согласно многим научным теориям пространство и время имеют непосредственную связь, следственно местоположение для таймеры является дюже значимым аспектом. Первая реализация таймеры подразумевала поиск ветхих фотографий экстраординарно на карте: пользователь открывал в приложении экран с картой, находил ветхие фотографии вокруг себя, выбирал понравившуюся, и запускал процесс таймераграфии. Ради этого был разработан веб-сервис, возвращающий все ветхие фотографии в регионе, тот, что определялся видимой областью на экране карты (minlat,maxlat,minlng,maxlng). На этапе тестирования стало ясно, что данный процесс нужно оптимизировать, от того что число пинов в определенной области может достичь такого числа, что будет непонятное месиво, и в результате предпочесть какой-то объект на карте станет дюже трудно.

image

Устранять эту задачу было решено с поддержкой кластеризации. Кластеры – это пины на карте, связанные с группой, нежели с каким-то определенным объектом. Визуально – это иконка с числом, отражающим число сгруппированых объектов.
Тут следует упомянуть, что в качестве картографического обслуживания мы избралиGoogle Maps. Отчего мы выбрали их нативным Apple картам? Скорее каждого, это решение ближайшем грядущем будет пересмотрено. Легко на момент проектирования еще было свежы воспоминания о фиаско Apple-карт, а так же мой индивидуальный навык применения обоих фреймворков.
Возвращаемся к кластеризации. Первоначально рассматривалась вероятность реализации кластеров локально. Выяснилось, что Google Maps имеет вероятность делать это дословно в одну строчку кода, впрочем это качество было доступно только на Андроиде. Мы теснее начали локальную реализацию кластеров на iOS, как в коллективное сознание взбрела здоровая мысль: улучшить веб-сервис, таким образом, Дабы он возвращал кластеры. Серверное решение имеет огромное преобладание в плане оптимизации: во-первых, уменьшение числа передаваемых объектов (читай, уменьшение трафика), и во-вторых снижения стоимости сохранения данных в Core Data. Модель Core Data так же пришлось модифицировать – добавилась новая сущность MapCluster, тот, что владеет такими признаками, как latitude, longitude, zoom, count, objectId
где
latitude, longitude – координаты кластера
zoom – ярус зума, которую выставляет пользователь
count – число объектов, привязанных к кластеру.
objectId – кластер может быть привязан к определенному объекту, таким образом, его необходимо отображать в виде подлинного кликабельного пина.

Дальше дело техники: если пользователь меняет местоположение карты либо ярус зума, то вначале делается запрос в локальном хранилище для выбранной области, и кластеры из полученной коллекции наносятся на карту, а также следом отправляется запрос на сервер с теми же параметрами. Если со связью все отлично, и сервер возвращает результат, локальные кластеры из базы удаляются, и на их место заливаются новые – так происходит обновление.
Уверен, что много каждого увлекательного о реализации веб-обслуживания могли бы расказать наши сервер-сайд разработчики. Могу лишь сказать, что ради этого так же создавались новые сущности БД для агрегации объектов в кластеры, которые заполняются задачей, запускаемой по расписанию.

Еще пару увлекательных моментов хотелось бы рассказать об iOS реализации кластеров. Для отрисовки кластеров пришлось создавать маленький класс, тот, что возвращает изображение в виде кружка с числом, чай способу setIcon класса GMSMarker из GoogleMaps.framework необходим именно UIImage, в виде которого он отобразит соответствуюший пин.
В результате сделанный класс представляет собой преемника UIView, тот, что содержит вложенные элементы, формирующие изображение кластера, а UIImage из этого каждого получается дальнейшим способом:

-(UIImage *) renderedClusterImage { UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, [UIScreen mainScreen].scale); [self.layer renderInContext:UIGraphicsGetCurrentContext()]; UIImage *capturedImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return capturedImage; }

Также хотелось бы рассказать об одной особенности GoogleMaps фреймворка, с которой мне пришлось довольно повозиться. Речь идет о методе создания кастомного представления InfoWindow (окошко с изложением, которое возникает, когда пользователь тыкает по пину).

Для отображения кастомного окна информации гугл-карты вызывают способ делегата

- (UIView *)mapView:(GMSMapView *)mapView markerInfoWindow:(GMSMarker *)marker;

как видно, данный способ должен воротить объект UIView. На этой вьюхе дозволено располагать различные компоненты (UILabel, UIImageView etc), и все это в результате будет отображено рядом с выбранным пином. Как бы все ясно и не вызывает сомнений. Впрочем в нашем случае появилась надобность перерисовки окна в связи с тем, что картинка превью на момент открытия InfoWindow может не быть загружена с сервера. В таком случае запускается процесс загрузки изображения, по заключению которого необходимо перерисовать InfoWindow. И здесь появился нюанс. Я думал, довольно будет сберечь указатель на UIView, тот, что мы возвращаем в способе делегата, а потом через свойства поменять изображение вложенному UIImageView. Оказалось, GoogleMaps растеризует (переводит в UIImage) отданный ему UIView, допустимо из соображений оптимизации, следственно все попытки перерисовать его замышленным методом оказались напрасны.

В итоге пришлось изобретать хак. Заключался он в дальнейшем: при тапе на пин показывается пустое InfoWindow, если данных еще нет, запускается процесс загрузки, и дальше происходит следующее:

- (UIView *)mapView:(GMSMapView *)mapView markerInfoWindow:(GMSMarker *)marker { TMMapImagePreview *view = [[TMMapImagePreview alloc] initWithFrame:CGRectMake(0, 0, mapView.frame.size.width * 0.7, 64)]; id image = marker.userData; NSData *imgData = (((MapCluster *)image).image).imageThumbnailData; if (imgData) view.imgView.image = [UIImage imageWithData:imgData]; else { NSString * url = (((MapCluster *)image).image).imageThumbnailURL; if (url) { [[ImageCache sharedInstance] downloadDataAtURL:[NSURL URLWithString:url] completionHandler:^(NSData *data) { (((MapCluster *)image).image).imageThumbnailData = data; [marker setSnippet:@""]; [mapView_ setSelectedMarker:marker]; }]; } } return view; }

тут TMMapImagePreview – это класс-преемник UIView, в нем формируется лэйаут InfoWindow. Каждая магия принудительной перерисовки заключена в compeltion-блоке способа downloadDataAtURL синглтона ImageCache, тот, что как не сложно додуматься, занимается скачиванием и кэшированием графического контента.

Будет здорово вы скачаете приложение, проверите его и дадите взвешенную критику и комментарии. Тем больше, мы выпустили обновление с момента написания первой части поста. Необходим фидбек. Спасибо!

 

Источник: programmingmaster.ru

 

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB