Свежие впечатления о BlackBerry 10 NDK

image

Последние две недели я снова копался в BlackBerry 10 NDK, так как один из моих клиентов попросил помочь ему. Я предложил адаптировать свой курс «введение в Qt» под платформу BlackBerry, а также порекомендовал следовать советам из моей серии учебных роликов про BB10 и Cascades, опубликованных в начале этого года на YouTube. Теперь мне хочется поделиться с вами моими свежими впечатлениями о BlackBerry 10 NDK. Кстати, я уже писал о моих первых экспериментах с BB10 NDK этой весной.

Внимание. Это вольный перевод заметки Йэнса Веллера. Перевод сделан для составления общей картины о текущем состоянии мира [BB10 + Qt]. Приятного чтения.

Приложения и C++

Перед тем как начать, небольшое введение о приложениях и C++. Люди, переходящие с других языков, вроде Java или .NET, часто не понимают необходимости написания приложений именно на C++. Особенно переходящие с языков, принудительно завязанных на ООП и Сборщик Мусора — им непросто понять все концепты, используемые в C++. По-моему, на самом деле, есть много причин использовать C++ для разработки приложений, особенно в связке с таким мощным фреймворком Qt. Одна из причин — производительность, так как C++ действительно ближе к железу, ваше приложение будет кушать минимум батареи. Не говоря уже о том, что имеется потолок роста производительности устройств в будущем, об этом упоминал Герб Саттер в своей заметке «бесплатные пайки закончились» (есть перевод на хабре от webmasconприм. переводчика). Qt Framework теперь доступен и для Android и для iOS, так что C++ и Qt/QML стали действительно мощной комбинацией для создания мобильных приложений.

NDK и Cascades

Поэтому, когда вы разрабатываете приложения под BlackBerry 10, вам необходимо начинать именно с BlackBerry NDK. У меня не было достаточно времени поиграть со свежевышедшей 1.2 версией, но IDE на первый взгляд стала заметно лучше и стабильнее. Но 1.2 пока не получила «золотой» статус (фиксация API — прим. переводчика), поэтому я рекомендую писать под 1.1, разве что вам что-нибудь нужно из 1.2. BlackBerry NDK поставляется вместе с Cascades Framework, API которого вы будете использовать при разработки приложения под BB10. Cascades сделан поверх Qt и использует QML, в то время как у Qt5 есть QtQuick 2, потому что у BlackBerry своя реализация QML, работающая в UI-потоке. Так что QML, созданный под QtQuick1 или 2 не будет работать под Cascades. А ещё Qt5 не полностью поддерживается Cascades — текущая версия основана на Qt 4.8.

С 2010 года я интересуюсь мобильной разработкой на Qt и QML, раньше был занят MeeGo, а сейчас я с BlackBerry. QML в BB10 немного отличается, в нём используются специальные элементы, например: Container, Pages и Controls, в то время как в QtQuick1/2 предлагает совсем базовые элементы вроде Item или Rectangle. Так что для QML и его API у BlackBerry имеется свой маленький мирок. Тем временем Qt5-приложения можно собрать и запустить на BB10, правда это не будет сопровождаться той степенью интеграции, которую предлагает Cascades.

QML и C++

Судя по документации и обсуждениям, основной подход сводится к использованию QML для большинства задач и мостов в C++ только при необходимости. Например, при написании моделей в C++ и дкларирования методов класса с помощью Q_PROPERTY для доступа их QML. Затем бóльшая часть работы происходит на стороне QML. У QML немного способов верификации и проверок на ошибки, например console.assert, но так как QML траслируется в JavaScript и не обладает сторогой типизацией, следовательно, не может проверять соответствие типов. Опечатка в имени переменной приведёт к тому, что QML может не заметить этой ошибки, а посчитать эту переменную объявленной новой. Простой пример:

Container {     layout: DockLayout {     }     Label{         text:ListItemData.titel     } } 

Этот простой элемент для отображения в контейнере ListView, через ListItemData мы получаем доступ к данным для отображения. Я тут внёс небольшую ошибку, на самом деле элемент называется title, а titel — это по-немецки. Тем более, немец это не сразу заметит. А QML не заметит тем более. Вы можете вставить любое слово здесь, QML не проверяет такого рода ошибки не во время компиляции, ни во время исполнения. Ничего не будет отображено, возможно вам повезёт обнаружить предупреждение в консоли. Кстати, если правильно настроить IDE, то сообщение точно должно появляться, даже при отладке на устройстве.

Как с этим бороться? За Cascades скрывается C++ фреймворк, построенный на Qt, так что хотя бы в C++ у нас есть шанс обнаружить это и запротоколировать ошибку. К сожалению, это невозможно обнаружить во время компиляции, но я работаю в этом направлении. Так что Q_ASSERT делает проверку во время работы приложени. Для всех элементов из Cascades, задействованных в QML, имеется класс в C++, инстанциирующийся динамически для каждого элемента во время исполнения приложения. Cascades API позволяет искать объекты этих классов и предоставляет способ контроллировать некоторые вещи. Для ListView также имеется класс, поставляющий элементы для ListView из C++: ListItemProvider. У этого классе есть свой интерфейс:

virtual bb::cascades::VisualNode* createItem(bb::cascades::ListView* listview,const QString& type); virtual void updateItem(bb::cascades::ListView* listview,bb::cascades::VisualNode* node,const QString& type,const QVariantList& indexPath, const QVariant& data); 

Своя реализация этих виртуальных методов позволяет создавать элементы для отображения в ListView и также заполнять их актуальными значениями. BlackBerry предоставляет примеры реализации. К сожалению, эти примеры не испольуют QML, а написаны целиком на C++. Но мне лично нравится использовать QML для UI. А ещё такой ООП-стиль, как в примере от BlackBerry, подразумевает наследование от ListItemProvider для каждого отдельно взятого ListView, а мне хотелось бы решить данную задачу раз и навсегда, так что у меня свой обобщённый ListItemProvider. Так как все проверки происходят во время работы приложения, шаблоны C++ — не вариант, давайте взглянем на следующую реализацию. Перед тем как я перейду к crateItem, краткая отсановка на addType, вспомогательном методе для декларирования обработчиков для каждого типа:

void ListViewItemProvider::addType(const QString& type, const QString& qmlasset, const listitem_callback& callback) {     bb::cascades::QmlDocument* doc = bb::cascades::QmlDocument::create(qmlasset);     if(!doc->hasErrors())     {         doc->setParent(this);         type_map.insert(type,doc);         callback_map.insert(type,callback);     }//TODO add additional error checking & handling } 

Этот метод добавляет различные обработчики для различных типов. Типы описываются в QML с помощью объектов QString, так что QMap<QString, QmlDocument*> будет достаточно для хранения поддерживаемых типов. Данный метод преобразует qml-ресурс (например, file:///partial.qmlприм. переводчика) в QmlDocument с помощью QmlDocument::create. Между прочим, QmlDocument::create на самом деле не возвращает QmlDocument*, как написано в примере выше. Он возвращает ссылку на служебный класс Builder для создания QmlDocument‘ов, затем, похоже, производит неявное преобразование в QmlDocument*. В принципе, здесь ничего интересного, идём дальше, метод createItem:

bb::cascades::VisualNode* ListViewItemProvider::createItem(bb::cascades::ListView* listview,const QString& type) {      if(type_map.find(type)!=type_map.end())      {           bb::cascades::Container* node = type_map[type]->createRootObject<bb::cascades::Container>();           return node;      }      Q_ASSERT_X(false,__FUNCTION__,type +" TYPE not handled");      bb::cascades::Container* con = new bb::cascades::Container(0);      bb::cascades::Label* label = new bb::cascades::Label(con);      label->setText("ERROR");      return con; } 

Данный код сначала проверяет регистрацию типа, а затем создаёт объект через метод QmlDocument::createRootObject, который возвращает указатель на созданный объект. Это шаблон, так что мы должны заранее знать тип объекта для создания. Пока я для себя определился, что для всех UI элементов я буду использовать Container в качестве корневого объекта. Возможно, здесь также подойдёт тип VisualNode*, который является возвращаемым значением метода createItem. Здесь самое интересное — обработка ошибки, как мы должны это сделать? Тут на помощь приходит Q_ASSERT_X и оповещает об ошибке. Но в случае сборки без отладки он ничего не сообщит, а тем не менее метод должен вернуть какое-либо значение. Про 0 ничего не сказано в документации, поэтому нет оснований думать, что это верное возвращаемое значение для данного метода. Но зато в документации явно указано, что возвращаемый указатель будет принадлежать ListView Даже если мы вернём 0 (к счастью, разработчики BB10 предусмотрели это), это утаит ошибку от тестировщика. Так что я решил возвращать небольшой Container с Label с текстом об ошибке. Может быть, я мог бы сформулировать сообщение лучше, но в данном варианте тестер скорее заметит эту ошибку. Как вариант, можно было бросить исключение, но после этого контроль передаётся обратно в Cascades API и Qt, а это не лучший вариант, так как Qt и Cascades не используют исключения, хоть они и поддерживаются в BB10.

Последнее, что нужно реализовать — метод updateItem. И снова не получается обойтись проще, ведь мы пишем обощённый код. В конце концов, загружаемый QML файл должен быть загружен с правильными значениями, что также явилось одной из причин, почему я решил написать велосипед реализовать обобщённый подход. Но есть вариант с заимствованием реализации для этого прямо из нашего класа — мы регистрировали callback’и для создания объектов нужных типов, теперь остаётся просто вызвать соответствующий callback в методе updateItem:

if(callback_map.find(type)!=callback_map.end()) { #ifndef USE_BOOST         (*callback_map[type])(node,indexPath,data); #else         callback_map[type](node,indexPath,data); #endif } 

До этого момента я мог не использовать/скрыть дефайн USE_BOOST, но для такого callback’а программист С++ должен сначала учесть boost::function. И так как BlackBerry утверждает, что boost поддерживается, я конечно же использую его. И тут выходит, что это не так-то просто, по крайней мере мой тулчайн падает с ошибкой в boost/type_traits/detail/cv_trait_impl.hpp. Я знаю, что boost используется многими, так что это скорее всего ошибка из-за настроек моего тулчайна или дистрибутива Linux. Ошибка судя по всему идёт из препроцессора, на версии GCC 4.6.3, в тексте ошибки сказано про несовпадение скобок. Я предпочёл пропатчить мою версию boost на локальной машине и сообщил об этом в сообщество boost и в BlackBerry. Если вы используете boost под BB10, то вам лучше использовать версию boost с GitHub’а BlackBerry. Так как не всем нужен boost, я также сделал версию без него, на случай если boost поломается снова.

И последнее, но не менее важное, реализация callback’a:

void ApplicationUI::callbackMyListItem(bb::cascades::VisualNode* node,const QVariantList& indexPath, const QVariant& data) {     bb::cascades::ImageView* image = node->findChild<bb::cascades::ImageView*>("imageview");     Q_ASSERT(image);     if(image)     {         QString name_image = "image"; //this must be correct!         QVariantMap map = data.toMap();         bool hasdata = map.contains(name_image);         Q_ASSERT(hasdata);         if(hasdata)             image->setImageSource(map[name_image].toUrl());     } } 

В данном случае путь до изображения задан. Указатель на VisualNode унаследован от QObject, так что дочерние объекты могут быть получены через findChild, который вернёт 0 в случае, если ничего не будет найдено. Поэтому уместно использовать здесь Q_ASSERT для проверки этого случая. Затем происходит поиск данных в QVariantMap. Так как нам в любом случае нужно какое-либо изображение, проверяется наличие элемента в контейнере QVariantMap. Если его нет, то Q_ASSERT снова приходит на помощь. Этот callback просто регистрируется с помощью boost::bind.

Поиск значений также может быть реализован через модель, но BB10 не поддерживает обычные модели из Qt, вместо этого BlackBerry реализовали свои классы моделей. В целом это хорошо, но мне лично больше нравятся модели из Qt, тем более это позволило бы использовать их при портировании Qt-приложения под Android, iOS, PC/Mac или даже Jolla. KDAB — один из наших «золотых» спонсоров — опубликовал решение, которое устраняет это недоразумение и делает Qt-модели пригодными для использования в Cascades.

IDE

Теперь, пару слов об IDE. Как я уже сказал ранее, IDE улучшилась с выходом версии 1.2. Среда улучшается, но в некоторых случаях всё равно остаётся далёкой от совершентсва. Редактор QML по-прежднему не достаточно хорош, зато теперь при падении не роняет всю IDE. Альтернативой мог бы стать Qt Creator, в нём поддержка QML также улучшилась. На данный момент, мне кажется Momentics IDE (базируется на Eсlipse — прим. переводчика) от BlackBerry лучше, чем QtCreator, если речь о разработке под Cascades. Во-первых, в QtCreator нет интеграции с Cascades вообще, поэтому автодополнение QML работать не будет, потому как в NDK отсуствтует необходимый для этого файл. По этой же причине не будет работать визуальный редактор QML. Qt, конечно, чуть лучше поддерживается в QtCreator, но в 1.2 версии NDK действительно много улучшений в этом направлении. Шаблоны проектов, предлагаемые QtCreator, не такие хорошие, как в Momentics, например, в них отсутствует интеграция кода локализации. Мне нравится то, что шаблоны в Momentics включают QTranslator в main.cpp. Momentics и QtCreator обма могут создать рабочее приложение под моё DevAlpha устройство, так что разработка под BB10 в QtCreator возможна, но есть куда расти.

Я загрузил исходные коды ListViewItemProvider, если вам нужно…

Популярные комментарии (1 шт)

— Кто хочет навестить морг?

ссылка на оригинал статьи http://habrahabr.ru/post/192064/

Записки Фрилансера: Делимся опытом, часть 1

imageЗдравствуйте, дорогие читатели Хабра!

Я разрабатываю приложения под iOS и Mac OS. Уже около года занимаюсь фрилансом и, переходя от клиента к клиенту, начал замечать, что в задаче разбираюсь одиножды; а при появлении похожего заказа, просто использую уже разработанные ранее модули. В серии статей «Записки Фрилансера» я постараюсь осветить некоторые часто встречающиеся в заказах аспекты; напишу подобие шпаргалки, прочитав которую, вы сможете быстро и безболезненно внедрить новую технологию в свой проект. Мои заметки ни в коем случае не претендуют на глубокое понимание процессов, но описывают легкий способ закончить заказ в срок.

Содержание:

  1. Часть 1: Работа с Файлами; Шаблон Singleton; Работа с Аудио; Работа с Видео; In-App Purchases
  2. Часть 2: Собственные всплывающие окна (Popups); Как использовать Modal Segue в Navigation Controller; Core Graphics; Работа с UIWebView и ScrollView
  3. Часть 3: Жизнь без Autolayout; Splash Screen; Работа с ориентацией девайса в iOS 6+; Сдвиг содержимого UITextField
  4. Часть 4: Google Analytics; Push Notifications; PSPDFKit; Вход в приложение через Facebook; Рассказать друзьям — Facebook, Twitter, Email
  5. Часть 5: Core Data; UITableView и UICollectionView

Работа с Файлами

Многие начинающие iOS разработчики натыкаются на следующую проблему: приложение корректно работает в симуляторе, но отказывается работать или работает некорректно на реальном устройстве. Одна из возможных проблем состоит в том, что приложение активно использует файловые ресурсы, неперенесенные в папку документов приложения. Политика Apple такова: вы не можете менять файлы приложения. Единственное место, где можно развлечься — это папка документов. Читать вы можете отовсюду (в рамках своего приложения), а вот писать только в папке документов.
Значит, при первом запуске, нужно перенести все документы для записи в папку документов! Запросто! Начнем с создания файла Config.h, добавим следующую строчку в Your_App_Name-Prefix.pch (этот файл автоматически добавлен во все файлы вашего проекта):

#import "Config.h" 

Отлично! Теперь все, что есть в Config.h, есть и во всем проекте! Давайте заполним этот файл:

#define pathToApplicationDirectory [[NSBundle mainBundle] bundlePath] // Путь к папке приложения #define pathToDocuments [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject] // Путь к документам  #define pathToSettings [[pathToDocuments URLByAppendingPathComponent:@"settings.plist"] path] // Путь к файлу настроек #define pathToPopups [[NSBundle mainBundle] pathForResource:@"popups" ofType:@"plist"] // Путь к файлу со всплывающими окнами 

Все это делается исключительно для нашего с вами удобства в будущем (работать с файлами мы будем много и хорошо бы избавиться от магических строк).
Теперь можно приступить к копированию файлов в папку с документами. Файлы settings.plist и popups.plist мы будем часто изменять, так что пункт с копированием необходим. Добавим следующий код в наш application: didFinishLaunchingWithOptions:

// Я юзер, а не лузер, так что перемещу ресурсы к документам! [self placeResourcesToDocumentsDirectory:@{      @"settings" : @"plist",      @"popups" : @"plist"}]; 

И, конечно же, сам метод placeResourcesToDocumentsDirectory:

Жми меня!

/*!  Метод, копирующий файлы из словаря в папку с документами  /param Словарь с именами файлов   */ - (void)placeResourcesToDocumentsDirectory:(NSDictionary *)resources {     // Проверим один из файлов, вдруг скопировано уже     if (![[NSFileManager defaultManager] fileExistsAtPath:pathToSettings) {                  for (NSString *fileName in [resources allKeys]) {             NSString *extension = resources[fileName];                          NSURL *storeURL = [pathToDocuments URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@", fileName, extension]];             NSURL *preloadURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:fileName ofType:extension]];             NSError *error = nil;             [[NSFileManager defaultManager] copyItemAtURL:preloadURL toURL:storeURL error:&error];         }     } } 

Вот и все! Как просто оказалось переместить файлы в нужную папку, не так ли? А получать доступ к файлу и работать с ним можно вот так:

NSMutableDictionary *settings = [NSMutableDictionary dictionaryWithContentsOfFile:pathToSettings]; settings[@"isThisAppCool"] = @YES; [settings writeToFile:pathToSettings atomically:YES]; 

Шаблон Singleton

Это скорее небольшой сниппет, который может облегчить вам работу с одиночками.
Singleton.h:

Жми меня!

<...> // Упростим доступ к синглтону #define coolSingleton [Singleton sharedSingleton]  @interface Singleton : NSObject + (Singleton *)sharedSingleton; <...> 

Singleton.m:

Жми меня!

<...> @implementation Singleton  static Singleton *sharedSingleton;  + (Singleton *)sharedSingleton {     if (sharedSingleton == nil) {         sharedSingleton = [[Singleton alloc] init];     }     return sharedSingleton; } <...> 

Здесь все просто. Во-первых, мы упростили одной дефиницией работу с синглтоном. Во-вторых, есть один статичный объект класса Singleton. В-третьих, по вызову метода класса sharedSingleton мы получим либо уже существующий объект, либо инициализируем новый. Всегда будет только один объект класса Singleton.

Работа с Аудио

Здесь нам придется поработать с классом AVAudioPlayer из фреймворка AVFoundation. Принцип действия прост: создаем объект класса AVAudioPlayer с именем определенного файла, подготавливаем его к воспроизведению и запускаем, когда нужно. Создадим простой синглтон, который и будет содержать все наши аудиоплееры. У нас будет два аудиоплеера: один для фоновой музыки, второй для воспроизведения звука нажатия кнопки.

Посмотрим на SimpleAudioPlayer.h:

Жми меня!

#import <Foundation/Foundation.h> #import <AVFoundation/AVFoundation.h>  #define audioPlayer [SimpleAudioPlayer sharedAudioPlayer]  @interface SimpleAudioPlayer : NSObject @property (nonatomic, retain) AVAudioPlayer *backgroundMusicPlayer; @property (nonatomic, retain) AVAudioPlayer *buttonSoundPlayer;  + (SimpleAudioPlayer *)sharedAudioPlayer;  @end 

И на SimpleAudioPlayer.m:

Жми меня!

#import "SimpleAudioPlayer.h"  @implementation SimpleAudioPlayer  static SimpleAudioPlayer *sharedAudioPlayer;  + (SimpleAudioPlayer *)sharedAudioPlayer {     if (sharedAudioPlayer == nil) {         sharedAudioPlayer = [[SimpleAudioPlayer alloc] init];     }     return sharedAudioPlayer; }  - (id)init {     self = [super init];     if (self) {         [self initAudioPlayers];     }     return self; }  /*!   Метод, инициализирующий аудиоплееры  */ - (void)initAudioPlayers {     NSURL *fileURL = [[NSURL alloc] initFileURLWithPath:pathToBackgroundAudio];     self.backgroundMusicPlayer = [[AVAudioPlayer alloc]                         initWithContentsOfURL:fileURL error:nil];     [self.backgroundMusicPlayer prepareToPlay];     self.backgroundMusicPlayer.numberOfLoops = -1;          fileURL = [[NSURL alloc] initFileURLWithPath:pathToButtonAudio];     self.buttonSoundPlayer = [[AVAudioPlayer alloc]                               initWithContentsOfURL:fileURL error:nil];     [self.buttonSoundPlayer prepareToPlay]; } 

Вот и все. Дефиниции путей к аудиофайлам можно прописать в Config.h. Стоит отметить, что мы указали отрицательное число для количества повторений фоновой музыки. Если вы установите отрицательное число у этого свойства — то аудиофайл будет повторяться бесконечно. То, что надо! Так же не стоит забывать о методе prepareToPlay — если подготовить все аудиоплееры, как только приложение запущено, то не будет маленькой задержки перед первым воспроизведением аудиофайла. А пользоваться нашим аудиоплеером можно вот так:

[audioPlayer.backgroundMusicPlayer play]; <...> [audioPlayer.backgroundMusicPlayer stop]; 

Работа с видео

Поработаем немного с фреймворком MediaPlayer. Фактически, следующий код можно добавить даже в метод viewDidAppear:

NSURL *url = [[NSURL alloc] initFileURLWithPath:pathToMovie]; MPMoviePlayerViewController *movieController = [[MPMoviePlayerViewController alloc] initWithContentURL:url]; [self presentMoviePlayerViewControllerAnimated:movieController]; [movieController.moviePlayer play]; 

Этот код просто показывает видеофайл по данному URL. В видеопроигрывателе от Apple уже есть все нужные кнопочки, чтобы видеопроигрыватель закрыть. Так что четыре строчки выше — минимальный набор символов для включения видео в ваше приложение.

In-App Purchases

Ну, и самое интересное на сегодня: фреймворк StoreKit! Правда, работать мы будем не напрямую с ним, но с MKStoreKit. Огромное спасибо MugunthKumar за великолепный фреймворк!

Все просто: отредактируйте файл MKStoreKitConfigs.plist под ваши нужды (там все интуитивно понятно) и используйте следующий код для проверки покупок:

if ([MKStoreManager isFeaturePurchased:@"me.identifier.coolapp.somesinglefeature"]) { // Юзер уже купил эту фишку! Нужно ее ему отдать! } if ([MKStoreManager isSubscriptionActive:@"me.identifier.coolapp.somesubscription"]) { // Вау! Да мы можем тянуть деньги ежемесячно, еженедельно, ежедневно! Но сегодня юзер уже заплатил, отдадим ему заслуженную плюшку } 

Для осуществления покупок используйте следующее:

[[MKStoreManager sharedManager] buyFeature:@"me.identifier.coolapp.somesinglefeature"                                  onComplete:^(NSString* purchasedFeature,                                               NSData* purchasedReceipt,                                               NSArray* availableDownloads) {      NSLog(@"Юзер купил: %@", purchasedFeature);  }                                onCancelled:^ {      NSLog(@"Юзер отказался покупать :( печально.");  }]; 

Заключение

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

Обо всех ошибках и неточностях статьи прошу писать в мой хабрацентр.

P.S. Если Вы хотите сотрудничать со мной, то мой профиль есть на одной из крупных бирж фрилансеров.

Стоит ли продолжать серию статей «Записки Фрилансера»? Выразите свое мнение в комментариях.

ссылка на оригинал статьи http://habrahabr.ru/post/192062/

Несколько интересностей и полезностей для веб-разработчика

Доброго времени суток уважаемые хабравчане. За последнее время я увидел несколько интересных и полезных инструментов/библиотек/событий, которыми хочу поделиться с Хабром.

Webflow


С помощью данного сервиса вы сможете сверстать кроссбраузерный отзывчивый макет за 55 минут. Очень красивый и удобный интерфейс. Идеальное решение для веб-дизайнеров, 26 000 из которых уже используют Webflow. Для создания двух проектов сервис бесплатный, а в дальнейшей перспективе вас ждут вполне демократичные цены. Инструмент реально «крутой».

Если Вам GUI для верстки не комильфо сам по себе как для меня, все равно рекомендую зарегистрироваться и экспортировать парочку responsive макетов. А еще есть простой генератор отзывчивого лэйаута и Responsive Patterns.

Parallax.js

Функциональный и простой инструмент для создания параллакс эффекта.


Для всех элементов на которых будет применяться эффект указываем класс layer и устанавливаем значение скорости движения в аттрибуте data-depth. В библиотеке есть ряд параметров:

<ul id="scene"   data-calibrate-x="false" // Отключает калибровку по горизонтали   data-calibrate-y="true"     data-invert-x="false"        data-invert-y="true"  // Устанавливает инверсию по вертикали   data-limit-x="false"   data-limit-y="10" // Устанавливает ограничение в 10 циклов для движения   data-scalar-x="2"   data-scalar-y="8" // Устанавливаем чувствительность движения   data-friction-x="0.2"   data-friction-y="0.8"> // Как я поннимаю это хаотичность движения элементов   <li class="layer" data-depth="0.00"><img src="layer6.png"></li>   <li class="layer" data-depth="0.20"><img src="layer5.png"></li>   <li class="layer" data-depth="0.40"><img src="layer4.png"></li>   <li class="layer" data-depth="0.60"><img src="layer3.png"></li>   <li class="layer" data-depth="0.80"><img src="layer2.png"></li>   <li class="layer" data-depth="1.00"><img src="layer1.png"></li> </ul> 

После чего передаем родительский элемент в Parallax конструктор:

var scene = document.getElementById('scene'); var parallax = new Parallax(scene); 

Intention.js

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

Device.js

Cкрипт по принципу Modernizr присваивает тегу HTML классы ios/android/windows/blackberry phone/tablet landscape/portrait, тем самым избавляет Вас от необходимости прописывать основные разрешения устройств в media queries.

Говоря о «кроссдевайсности» хочется также упомянуть про Risizer — Bookmark для для тестирования отзывчивого дизайна. Все мы знаем про множество подобных сервисов, но мне кажется, этот способ самый удобный.

GistBox


GistBox синхронизируется с вашим GitHub профилем и в нужном виде отображает Gist лист. Все ваши сниппеты отсортированы по меткам и всегда под рукой. Доступен как расширение для Chrome.

CSS Modal

Начну с того, что проект создан одним из членов команды Boilerplate. CSS Modal — самый простой способ добавить в своей проект адаптивное модальное окно:

Dotdotdot.js, Uikit, HTML2PDF

Недавно мне понадобилось сокращать абзацы многоточием. Но стандартный text-overflow работает только на одной строке с no-wrap. И на просторах интернета мне повстречался замечательный скрипт (dotdotdot.js), который отлично решает эту задачу.

Uikit — плюс один к существующим веб фреймворкам со своими особенностями.

HTML2PDF. Сервис написан на основе Phantom.js. Возможно пригодиться для оформления портфолио в .pdf.

Большое спасибо всем за внимание.

Понравилась ли Вам подборка?

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

Никто ещё не голосовал. Воздержавшихся нет.

ссылка на оригинал статьи http://habrahabr.ru/post/192012/

9 самых важных фактов о мобильной рекламе

eMarketer прогнозирует, что затраты на мобильную рекламу увеличатся почти до 37 млрд. долларов в 2016 году, по сравнению с 8,4 млрд. в 2012 году (дисплейная и контекстная).

1. Продажи смартфонов за 2014 год составят более 1 млрд. единиц

По данным IDC, продажи смартфонов впервые превысят объем в 1 млрд. уже в 2014 году. Совокупно продажи смартфонов и планшетов превысят продажи PC в соотношение 3:1 в 2013 году и 5:1 к 2017 году. Такое быстрое распространение смартфонов, в свою очередь, приведет к «буму» мобильного интернета и росту интереса маркетологов к данному рекламному каналу. Отныне, если маркетологи хотят быть первыми, их стратегия должна быть мобильной.

image

2. Смартфоны впервые обгонят по продажам «простые» телефоны в 2013 году

Наконец-то это становится реальностью! Мировые продажи смартфонов обгонят «фичерфоны», благодаря значительному снижению цен и распространению 3G/4G сетей. IDC прогнозирует объем отгрузок смартфонов в 2013 году на уровне 918,6 млн. штук, что составит около 50,1% от общего объема поставок в отрасли. Высокий спрос обеспечивают развивающиеся страны с большой численностью населения: Китай, Индия, Бразилия. Маркетологи должны коренным образом изменить свои маркетинговые стратегии, чтобы ответить на эти радикальные изменения в структуре пользовательских устройств и соответствующим им моделям потребления.

image

3. Мобильные устройства завладевают нашим медиа вниманием

image
Пользователи мобильного интернета уже тратят больше времени на своих устройствах, даже чем на просмотр ТВ. Среднее время пользование смартфонами составляет 108 минут от общих 7 часов медиа потребления в день, опережая использования компьютеров (93 минуты) и ТВ (92 минуты). Планшеты (37 минут) занимают 5-ое место по медиа потреблению, уже догоняя радио (52 минуты).

4. Мобильный трафик растет быстро, в отличие от затраты на мобильную рекламу

Мобайл составляет 15% от общего интернет-трафика и его доля растёт, в среднем, на 50% в год (и скорей всего, темп роста будет только ускоряться). Тем не менее, рекламные бюджеты не торопятся в след за пользователями мобильных устройств. Даже странах с развитой мобильной рекламой, как США, несоответствие медиавлияния и затрат на рекламу около 20 млрд. долларов.

image
image

5. Мобильные приложения оставили мобильный интернет далеко позади

Популярность мобильных приложений постоянно растет. Показы рекламы в мобильных приложениях составляют 75-80% от общего числа. Согласно опросу, 27% респондентов заявили, что использовали 6-10 приложений за последние 30 дней. Будь это бренд-менеджер, которые ищет точку контакта с потребителями или медиапланнер в поисках целевой аудитории — мобильные приложения будут играть главную роль в этом.

image

6. Мульти-скрин на подъеме

Мультискриннинг — это явление одновременного использования потребителем более 1 устройства. Так, примерно, 62% потребителей совмещают использование мобильного устройства с просмотром ТВ. Среди аудитории 20-34, в среднем, 69% потребителей ищут на своих мобильных устройствах информацию, которую только что видели по ТВ. Эти факты имеют огромное влияние для маркетологов, так как рекламные кампании могут становиться действительно многоканальными и дополняющими друг друга.

7. Rich Media форматы оказывают влияние на вовлечение потребителя

image

Мобайл предоставляет брендам простор для творчемства и креатива в рекламе, который до сих пор ограничен ТВ. Rich Media мобильная реклама до 4-х раз более эффективна по сравнению со стандартными баннерами, с точки зрения CTR. Также мобильная реклама размещенная внутри приложений, в среднем, в 1,7 раза более эффективна по отношению к мобильному интернету. Rich Media мобильная реклама достигает показателя CTR 1,53%, когда показывается внутри приложений и «всего» 1,12% в мобильном интернете (что тоже очень хороший результат). Для сравнения, стандартные баннеры имеют CTR 0,39% и 0,32% соответственно.

8. Потребители не против мобильной рекламы

Бренды и маркетологи ищут взаимодействия с потребителями через их мобильные устройства в течение всего дня. Наши гаджеты всегда находятся онлайн, увеличивая шансы рекламного сообщения быть увиденным. Согласно исследованиям, 59% всех пользователей мобильных устройств относятся к мобильной рекламе, также как к ТВ-рекламе и рекламе в «большом» интернете.

9. Мобильная реклама максимально приближает к покупке

image

Мобильная реклама напрямую влияет на поведение покупателей. Согласно результатам исследования, 75% всех опрошенных заявили, что они обнаружили что-то новое с помощью своих мобильных устройств. И почти половина (46%) сказали, что они купили что-то с помощью своих мобильных устройств. А 45% опрошенных сообщили, что реклама увиденная на их смартфонах и планшетах напрямую повлияла на их выбор непосредственно в магазине. И вправду, многие люди уже не расстаются с мобильными устройствами, даже находясь прямо перед полками с товаром.

По вашему мнению, в каком году «выстрелит» мобильная реклама в России?

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

Никто ещё не голосовал. Воздержавшихся нет.

ссылка на оригинал статьи http://habrahabr.ru/post/192082/

Удобная синхронизация файлов

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

Тогда я почти уже получил финансирование, но инвестиционный фонд с которым я работал в последний момент потребовал серьезную дjлю в бизнесе. И я решил что овчинка выделки не стоит. А потом как-то стало не до этого.

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

Да, скажете вы, есть облачные хранилища и зачем оно нужно когда есть Яндекс.Диск, Dropbox и куча облачных сервисов хранения файлов. Ну просто это не всегда приемлемо и удобно. Минусы:
1. Вы доверяете свои файлы третьей стороне без всяких гарантий.(Я не параноик, но вы ведь пне положите в такое хранилище ваши интимные данные)
2. Для доступа к ним нужен интернет. А это в России до сих пор не всегда возможно.

Качаем. Ставим на те устройства где нужен оперативный доступ к данным.

Я больше не лезу за фотками, файлами и музыкой на мобилу и не лезу на сервер чтобы забрать редко обновляемый бэкап.
В домашнем компьютере есть несколько папок:
Телефон
Сервер
Общая
Авто

Чтобы закинуть что-то в телефон, я просто копирую нужный файл в папку «Телефон». На моем телефоне он появится через несколько секунд
Чтобы обменяться документами с ребятами по работе, я кидаю в папку общая. И через несколько секунд этот файл появлятся у всех нужных мне людей – ЛОКАЛЬНО а не в облаке.

В машине в качестве медиа центра стоит Google Nexus, по 3G смотрит в интернеты. На домашнем компьютере кидаю карты, навигации и музыку в папку Авто. Все автоматом складывается в автомобиль. Из папки Авто/регистратор забираю интересные моменты снятые видео регистратором(девайс в машине всегда онлайн).

Экономит массу времени.

Пожелаем BotTorrent успехов в развитии их замечательного проекта.

PS
Спасибо Мурину Саше за наводку )

ссылка на оригинал статьи http://habrahabr.ru/post/192084/