Arm1.ru

Tag: «ios»

Анимация касания индикатора в UISlider

Анимация касания индикатора в UISlider

Стояла задача - при касании слайдера анимировать в приложении сделать анимированное увеличение ползунка. Так же, как это сделано у Apple в приложениях Apple Music и Podcasts в плеере, когда начинаешь перематывать позицию воспроизведения. Пока искал способ стандартными средствами, убил немало времени. Очень не хотелось писать прям свой кастомный слайдер, хотелось использовать системный UISlider, что мне, в итоге, и удалось.

Читать далее...

comment comments

SMS Антикредит

Надоел мне СМС-спам, который мне периодически приходит - то кредит предлагают, то ипотеку. Как раз, просматривая один из видосов с WWDC, увидел, что Apple дали API для фильтрации SMS-сообщений. Если сработает твой фильтр - то такие сообщения будут приходить без звукового или вибросигнала, втихую, и попадать в отдельную категорию спама в Сообщениях.

Недолго думая, запили такой вот фильтр и выложил в App Store. Надеюсь, кому ещё, кроме меня, пригодится.

comment comments

CompareShots v1.2

Вышло обновление CompareShots версии 1.2

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

Загрузить

comment comments

CompareShots v1.1

Вышло обновление CompareShots версии 1.1.

Из нового - прозрачность теперь меняется просто перетаскиванием пальца (горизонтально). Причём такую фичу попросил один из пользователей в письме. К тому же так прозрачность меняется плавнее.

Кроме того, переписал полностью приложение на Swift. Теперь у меня есть аж один зарелизенный проект на нём :) Но это только начало.

Что удивительно - обновление в App Store я отправил вчера, и менее чем через сутки оно уже прошло Review и вышло. Ни разу так быстро Apple на моей памяти так быстро не пропускали приложение.

Загрузить

comment comments

Сборка iOS проекта из консоли через xcodebuild

Оставлю себе шпаргалку, как сделать на bash скрипт, который соберёт тебе проект в ipa файл.

В папке с исходниками iOS-проекта делаем папку, например, scripts, в нем создаём файл build.sh

mkdir scripts
touch build.sh
chmod +x build.sh

В папку кладём наш provision profile. Скажем, называется он arm1.ru.mobileprovision. После этого внутрь build.sh кладём код:

#!/bin/bash

# идём в директорию скрипта
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd "${CURRENT_DIR}"

# вводим то же имя, которое в настройках проекта выбрано в Build Settings в разделе Code Signing Identity
CODE_SIGN_IDENTITY="iPhone Distribution: Your Code Signing Identity"

# имя provision profile, который лежит тут же
PROVISION="$PWD/arm1.ru.mobileprovision"

# имя схемы, которую собираем
SCHEME="appScheme"
WORKSPACE="$PWD/../your-app.xcworkspace"

echo "Building..."

BUILDDIR="$PWD/build"
DSYMDIR="$PWD/dSYM"

if [ ! -d "$BUILDDIR" ]; then
    mkdir -p "$BUILDDIR"
fi

if [ ! -d "$DSYMDIR" ]; then
    mkdir -p "$DSYMDIR"
fi

# ищем UUID в provision profile
UUID=`grep UUID -A1 -a "${PROVISION}" | grep -io "[-A-Z0-9]\{36\}"`


xcodebuild \
    -workspace "${WORKSPACE}" \
    -scheme "${SCHEME}" \
    -sdk iphoneos \
    -configuration Release \
    CODE_SIGN_IDENTITY="${CODE_SIGN_IDENTITY}" \
    PROVISIONING_PROFILE="${UUID}" \
    OBJROOT=$BUILDDIR \
    SYMROOT=$BUILDDIR

if [ $? != 0 ]; then
    echo "Build failed"
    exit 1
fi

echo "Packaging..."

NOW=$(date +"%d_%m_%Y_%H_%M_%S")

xcrun -sdk iphoneos PackageApplication -v "${BUILDDIR}/Release-iphoneos/${SCHEME}.app" -o "$PWD/${SCHEME}_${NOW}.ipa"
if [ $? != 0 ]; then
    echo "Packaging failed"
    exit 2
fi

mv "${BUILDDIR}/Release-iphoneos/${SCHEME}.app.dSYM" "${DSYMDIR}/${SCHEME}_${NOW}.app.dSYM"

echo "Build succeeded."

Вуаля, в папке со скриптом лежит appScheme.ipa - собранный проект. В папке dSYM лежат dSYM-файлы. В конце скрипта можно дописать что-нибудь. У меня в одном из проектов в конце идёт заливка билда на наш самодельный Testflight и отправка ссылки на установку всем кому нужно. Удобно, два раза кликнул по sh-файлу и всё собралось и отправилось.

comment comments

Как отключить жест swipe back в iOS 8

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

В общем для iOS 8 надо в текущем UIViewController добавить:

в YouViewController.h:

// ставим протокол обработки жеста @interface YouViewController : UIViewController

в YouViewController.m:

- (void)viewDidLoad {
  [super viewDidLoad];

  self.navigationController.interactivePopGestureRecognizer.enabled = NO;
  self.navigationController.interactivePopGestureRecognizer.delegate = self;
}

#pragma mark - UIGestureRecognizerDelegate

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
  // отключаем жест если это swipe back
  if ([gestureRecognizer isEqual:self.navigationController.interactivePopGestureRecognizer]) {
    return NO;
  } else {
    return YES;
  }
}
comment comments

CompareShots

CompareShots

Выпустил новое приложение - CompareShots.

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

Приложение позволяет выбрать 2 изображения из библиотеки устройства. Например, макет приложения или сайта от дизайнера, и скриншот того, что сделал разработчик, и проверить соответствие - действительно ли всё пиксель в пиксель свёрстано. Пока держишь палец на экране - показывается первая картинка. Убираешь - вторая. Есть слайдер прозрачности, можно наглядно увидеть, в каких местах несоответствие. Результат несоответствия в виде картинки можно расшарить или отправить по почте, или отправить в любой другое приложение, которое принимает картинки.

Приложения для iPhone и iPad.

Загрузить

comment comments

Итог айПодписок

айПодписки

Итак, в феврале Яндекс всех пользователей Яндекс.Подписок насильно перетащил на сервис Яндекс.Новости. Без предупреждения и как-то по-свински всё это получилось. Веб-версия стала недоступна. Но API работало, про его судьбу яндексоиды лишь говорили, что оно будет работать. Но про сроки и планы молчали. Собственно, сначала перестали подсасываться новые RSS-записи и API давал доступ только к старым записям, а потом разослали всем письма, что 31 марта 2015 года API они тоже убьют. Что и произошло. Поэтому я убрал из App Store приложение айПодписки и хочу подвести какие-то итоги для себя и для истории.

Приложение я написал весной 2014 года примерно за неделю, кодя по вечерам. В App Store оно появилось 17 апреля 2014 года, о чём я радостно писал у себя и в посте на Хабре. Пост набрал рейтинг +15 и принёс какое-то количество скачиваний и запросов на фичи. Приложение не дотянуло 17 дней до своего первого дня рождения :)

За этот год было:

Итог айПодписок

1321 скачивание приложения и всего 496 обновлений, когда я выпускал новые версии. Негусто, но это и приложение не массовое. RSS вообще считается технологией для роботов и гиков. Абсолютное большинство пользователей из России. 2 и 3 место - США и Украина, там поровну. Рекорд: 77 установок за сутки.

Итог айПодписок

1431.42 р. дохода на внутренних покупках. Изначально приложение было бесплатным, но с рекламой и её платным отключением. Из моих 4 приложений в App Store, это худший результат. С другой стороны, получить какие-то деньги за то, что учишься, получаешь опыт и решаешь какую-то свою проблему - тоже неплохо :)

Итог айПодписок

Большую часть времени приложение находилось в Топ-200 в своей категории (Новости) в России. На скрине выше - самые высокие позиции, на которые оно заползало в топах.

Итог айПодписок

С оценками всё не очень. Я нигде не просил людей поставить оценку приложения, поэтому ставили её либо энтузиасты и друзья, либо недовольные. Я, к сожалению, пропустил глюк очень досадный. Когда приложение запускалось в портретном режиме - кнопка «Все подписки» просто не появлялась. В итоге пользователь видел только приветственный экран и кнопку «убрать рекламу» за деньги :) И грустно, и смешно. Сам я постоянно приложением пользовался в горизонтальном режиме и попросту эту проблему не замечал, зато получил отзывы типа «работает только реклама».

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

В приложении показывается реклама из AdMob. За год было всего 71135 показов рекламы, которые принесли $23.87. То есть внутренняя покупка отключения рекламы принесла больше денег, чем сама реклама :)

Такие вот скромные, но итоги.

comment comments

айПодписки и закрытие Яндекс.Подписок

Так как Яндекс закрыл свой сервис Яндекс.Подписки и всех насильно перевёл всех в Яндекс.Новости, я думаю на этом айПодписки закончатся. Разве что они сделают публичный API для Яндекс.Новостей. Если он будет годным для перевода, то попробую перевести айПодписки на новый API. Но пока API нет. API самих Яндекс.Подписок пока работает, но сбор новых новостей из RSS-лент они остановили. У меня там последние новости за 5-6 февраля. Сам я перешёл на Feedly. Спасибо всем, кто пользовался :)

UPDATE: пришло письмо, что 31 марта API подписок закроется. Видимо, придётся и приложение из App Store убирать.

comment comments

Выход за пределы песочницы в iOS

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

Как известно, все приложения в iOS работают внутри песочницы, за пределы которой они выходить не могут. Все приложения из App Store устанавливаются в /var/mobile/Applications/ (приложения, которые ставятся в iPhone из Xcode - туда же), где для них создаётся отдельная папка с нечитаемым именем. Выходить за пределы этой папки нельзя. Ни для чтения, ни, тем более, для записи.

Выход за пределы песочницы в iOS
Это, например, папка гугловой игры Ingress.

Выход за пределы песочницы в iOS
А это айосный калькулятор, например, лежащий в /Applications.

Если мы хотим, например, прочитать в приложении SMS-сообщения телефона, то нам нужно прочитать файл /var/mobile/Library/SMS/sms.db - это обычная SQLite база данных, без шифрования и защиты. Её можно скачать с джейлбрейкнутого телефона и открыть любой тулзой, которая умеет открывать SQLite-файлы, и посмотреть все SMS-ки и даже фигачить по ней sql-запросы для поиска и прочего.

Выход за пределы песочницы в iOS
Тут, например, лежит файл со всеми смс-ками iPhone.

Так вот, доступа к этому файлу из песочницы нет. И Jailbreak от этого не спасает, он даёт полный доступ к файловой системе, но только если ты работаешь вне песочницы.

Чтобы приложение работало вне песочницы - его нужно переместить из /var/mobile/Applications/ в директорию /Applications. Тогда приложение будет в папке системных программ, будет иметь доступ на брейкнутом девайсе к файловой системе, не будет удаляться из телефона удержанием пальца на иконке и всё такое.

И вот боль тут в том, Xcode ну никак не может установить приложение туда, только в песочницу. Можно делать это руками - заходить в телефон, перемещать через какой-нибудь iFunBox, и вот такой вот геморрой каждый раз. И самая боль в том, что ты теряешь удобство отладки. Нельзя запустить в Xcode апп на девайсе и спокойно смотреть в консольку - что же там приложение твоё пишет - работает или нет?

Никакие твики из Cydia, типа дающие для приложений доступ к файловой системе даже для приложений из песочницы, не дали эффекта. По крайней мере, у меня на iOS 7.1.2. Говорят, что даже если запустить приложение от root'а, но всё равно в песочнице - и это не даст прав на чтение системных директорий. Хотя, такое ощущение, что раньше такое работало, но и джейлбрейки были другие.

Такой вот ад. В ближайшее время буду пробовать найденные в инете скрипты для автоматизации перемещения приложения внутри айфона после билда через SSH с отлавливанием syslog'а. Хочу ещё расписать, чего я наковырял внутри айфона в плане «где что хранится», но позже, по окончании ада :)

comment comments

Клиент Linode для iPhone

Пользуюсь уже несколько лет услугами хостера Linode.com, где держу свою VPS-ку с сайтами/проектами и для всяких личных целей.

У них есть API, на основе которого, как я понимаю, они сделали клиент для iPhone.

Клиент это, по-моему, просто перекрасен.

Клиент Linode для iPhone Клиент Linode для iPhone

Читать далее...

comment comments

Пишем клиент для Яндекс.Метрики для iPhone

Примеры создания графиков в PNChart есть на странице проекта на Github. С регистрацией приложения всё понятно - добавляем приложение, отмечаем права, которые приложение хочет получить. При авторизации пользователь должен будет разрешить или отклонить разрешение на получение данных, да и будет видеть - какие права просит приложение.

OAuth-авторизация

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

Технически OAuth-авторизация делается довольно просто - добавляем в приложение собственную URL-Scheme (например, myapp), в настройках приложения на сайте Яндекса (там, где регистрировали приложение) указываем в качестве Callback URI что-то вроде myapp://, после чего кодим, чтобы после авторизации приложение пыталось открыть url по адресу myapp://, таким образом url откроется в нашем приложении, а в нём уже все данные, включая access_token.

На Хабре есть отличная статья именно с такой авторизацией и именно для Яндекса, которая сэкономила мне время, поэтому особо больше описывать авторизацию не буду, чтобы не повторяться. После авторизации у нас есть token, который мы и сохраним.

Технический нюанс: логично, что когда-нибудь пользователь захочет разлогиниться в приложении. Чтобы кто-нибудь не имел доступа к статистике кроме него, или, например, чтобы залогиниться под другим аккаунтом. При авторизации через браузер (то есть, через UIWebView), мало удалить полученный ранее token. Ваша авторизация в UIWebView запоминается, поэтому при разлогинивании пользователя нужно почистить cookies в песочнице приложения, иначе при авторизации вы автоматически залогинитесь в тот же аккаунт. Простого кода в моём случае было достаточно:

 NSHTTPCookie *cookie;
 NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
 for (cookie in [storage cookies]) {
     [storage deleteCookie:cookie];
 }
 [NSUserDefaults standardUserDefaults] synchronize]; 

Сохранение token'а

Важный момент для параноиков - сохранение полученного token. Можно, конечно, положить его в NSUserDefaults - но говорят, при наличии, например, jailbreak, это не безопасно и можно расковырять этот token. Хорошая практика - хранить ключи доступа, логины и прочие важные штуки в KeyChain, там всё зашифровано и секьюрно.

Ковыряя его возможности в первый раз, даже со статьями на родном языке, возникает лёгкое желание побиться головой обо что-нибудь. Разбираться в этом всём, когда тебе нужно просто сохранить n-символов, а потом их оттуда взять, очень не хочется. Для ленивых вроде меня, Apple к документации приложила класс KeychainItemWrapper. Название само за себя говорит - обёрка вокруг KeyChain. Правда, класс этот написан давно и без поддержки ARC, но на github легко можно найти его форк с поддержкой ARC. Вот этот, например (его ещё и через CocoaPods можно подключить).

Ну а теперь сохранение и удаление token'а сводится к нескольким строчкам:

 // сохранение
 KeychainItemWrapper *keychainWrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"OAuthToken" accessGroup:nil];
 [keychainWrapper setObject:@"accountName" forKey:(__bridge id)(kSecAttrAccount)];
 [keychainWrapper setObject:tokenToSave forKey:(__bridge id)(kSecValueData)]; 
 // удаление
 KeychainItemWrapper *keychainWrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"OAuthToken" accessGroup:nil];
 [keychainWrapper resetKeychainItem]; 

Получение различных данных

Лично мне больше нравится получать данные в JSON. С запросами всё довольно просто - шлём нужный запрос к методу из API, в заголовок включаем Authorization: OAuth . Кстати, token можно и как get-параметр передавать, например:
https://api-metrika.yandex.ru/counters?oauth_token=ACCESS_TOKEN

Раз уж мы параноим с самого начала насчёт безопасности, то запросы все можно слать через https (но можно и через http). Получаем данные в JSON, парсим их через встроенный NSJSONSerialization:

dataObject = [NSJSONSerialization JSONObjectWithData:result options:NSJSONReadingMutableContainers error:&e]; 

Результат, например, для получения списка счётчиков:

айМетрик

Ну а дальше полученные данные нужно как-то отобразить.

Отображение

Если смотреть на web-интерфейс Метрики, то там в основном 2 представления данных - таблицы с текстовыми данными и графики.

С таблицами всё, вроде, понятно - используем UITableView. Почти все данные содержат одни и те же общие показатели - просмотры, визиты, уникальные посетители, глубина просмотра, время на сайте, показатель отказов. Поэтому я сделал класс-потомок UITableViewCell, чтобы не плодить кучу одинакового кода в каждом из UIViewController, после чего в методе

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

мы просто создаём ячейку с уже полученными данными, например:

 NSString *CellIdentifier = [NSString stringWithFormat:@"CellId_%li_%li", (long)indexPath.section, (long)indexPath.row];
 SourcesTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
 if ( cell == nil ) {
     cell = [[SourcesTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier
     textOnLabel:@"просмотры" labelTextColor:[UIColor black]];
 }
 
 // задаём значения
 [cell 
     changeValuesWithpageViewsNum:pageViewsNum 
     visitTime:visitTime 
     denials:denialsValue 
     visitsNum:visitsNum
 ];

Время, проведённое на сайте, кстати, возвращается в секундах, поэтому его ещё надо красиво вывести в формате мм:сс, а процент отказов - от 0 до 1, поэтому 50% вернётся как 0.5.

Для рисования графиков есть несколько готовых решений. Кое что, опять же, можно найти на Хабре. Лично мне на гитхабе приглянулся PNChart - минималистично, симпатично. Подключаем через CocoaPods и в путь.

Пишем клиент для Яндекс.Метрики для iPhone

Примеры создания графиков в PNChart есть на странице проекта на Github.

Компонуем всё в UITabsViewController, немножко оформляем и получаем на выходе быструю статистику в кармане.

Пишем клиент для Яндекс.Метрики для iPhone

Результат я решил выгрузить в AppStore, желающие могут установить.

Для стимула продолжать разработку, сделал приложение платным.

Полезные ссылки:
CocоaPods — мощное средство в руках Objective-C разработчика
Яндекс OAuth авторизация в iOS
PNChart (рисование графиков)
KeychainItemWrapper (обёртка вокруг KeyChain)

Читать далее...

comment comments

Обновление айМетрик 1.1

Обновление айМетрик 1.1Обновление айМетрик 1.1Обновление айМетрик 1.1

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

Плюс поправил пару косяков.

Обновление айМетрик 1.1

comment comments

айПодписки - клиент для Яндекс.Подписок для iPad

айПодписки - клиент для Яндекс.Подписок для iPad

Давно хотел написать это приложение. Странно, но с тех пор, как 8 месяцев назад Яндекс открыли API для Яндекс.Подписки в честь закрытия Google Reader, не появилось ни одного клиента для iOS. А иметь такое приложение мне и самому хотелось. Начал читать новости дома за чашкой чая, потом продолжил в iPad читать в дороге - все прочитанное уже отмечено как прочитанное, красота. Да и в целом мне удобнее читать новости с iPad. Как-то, web-версия подписок не особо быстро работает.

В приложении можно не только читать новости и делиться ими в соц. сетях или добавить в Список для чтения в Safari, но и управлять подписками - добавлять новые новостные ленты и удалять уже добавленные.

Нет приложения - напиши сам :) Приложение бесплатное.

App Store badge

comment comments

айМетрик - клиент для Яндекс.Метрики для iPhone

 айМетрик айМетрик

Выпустил мобильный клиент для Яндекс.Метрики для iPhone. Первая версия умеет: 

  • сводка посещаемости (визиты, просмотры, посетители);
  • график посещаемости по дням;
  • отчёт об источниках переходов на ваш сайт;
  • отчёт по переходам из поисковых систем;
  • отчёт по переходам с других сайтов;
  • отчёт по поисковым фразам, с помощью которых посетители нашли ваш сайт;
  • отображение списка ваших счётчиков.

comment comments

AllCafe для iPhone в разделе Все о Москве App Store

Мелочь, а приятно. В российском App Store появилась отдельная подборка «Все о Москве», куда попало написанное мною приложение.

AllCafe в App Store

AllCafe в App Store

comment comments

Этот день 2.0

Этот день 2.0

Выпустил обновление приложения Этот день. Передизайнил под iOS 7 (что примерно равно тому, чтобы убрать вообще почти всё оформление). Стало, по-моему, приятнее и лаконичнее.

Заюзал стандартный UIActivityViewController для шаринга через Facebook, Twitter и другие встроенные вещи, типа отправки по email, sms/iMessage и т.д., плюс прикрутил для шаринга в ВК Vkontakte-iOS-SDK вместо самописной хрени, что была раньше. Правда, визуальных отличий особо для пользователя нет, да и пока у меня руки дошли всё наконец допилить, появился уже официальный SDK для iOS от ВК, но ковырять его, чтобы заменить то, что уже работает, не стал.

Попутно избавился от уже ненужного SBJson для работы с JSON, т.к. можно смело пользоваться уже встроенным в iOS NSJSONSerialization. Решил встроить в приложение мобильную Яндекс.Метрику, чтобы считать нормально пользователей и смотреть - кто, например, какой версия iOS пользуется и на каком девайсе. Пока наблюдаю за цифрами, познавательно.

Такое вот продолжение маленького проекта-хобби. Буду думать, чего бы в нём ещё запилить, и буду писать следующее приложение. А пока - качаем/обновляемся:

Загрузить

comment comments

Отлавливание остановки карты в Google Maps SDK для iOS

Дополнение вот к этому посту. Там я указал в проблемах, что в гуглокартах реализовано только событие mapView: didChangeCameraPosition:, которое срабатывает на каждый чих карты, даже если палец ещё не оторван от экрана. Если вести медленно пальцем по карте, а на карте должны появляться какие-то метки, которые получаются извне http-запросами, то оно умудряется отправлять по 10-15 запросов в секунду. Это, конечно же, неприемлимо, особенно в условиях мобильного интернета.

Но с этим можно справиться своими средствами, а точнее с помощью UIPanGestureRecognizer.

Код-шпаргалка:

// где-то в коде проекта
panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(mapDidChange:)];
[panRecognizer setMinimumNumberOfTouches:1];
[panRecognizer setMaximumNumberOfTouches:1];

// GoogleMapsView это гуглокарта, класс GMSMapView
GoogleMapsView.gestureRecognizers = @[panRecognizer];

//.. метод, реагирующий на отпускание касания
-(void)mapDidChange:(UIPanGestureRecognizer *)pan {
    if ( panRecognizer.state == UIGestureRecognizerStateBegan ) {
        // ничего не делать, но можно и что-нибудь делать
    }
    if ( panRecognizer.state == UIGestureRecognizerStateEnded ) {
        // палец отпущен, можно делать то, что нужно, например:
        [self sendMapRequest];
    }
}

Всё, навешивается тач на гуглокарту. Когда палец отпущен - делаем действия. Теоретически этим можно даже заменить метод mapView: didChangeCameraPosition: если нужно.

comment comments

Замена UIDevice uniqueIdentifier

Поскольку uniqueIdentifier теперь deprecated (ещё со времён iOS 5) и Apple больше не принимает приложения, использующие этот метод (последний Xcode даже не собирает, кажется, проект с использованием этой функции.

Погуглил, чем заменить и собрал из разных решений одно:

NSString *uuid = @"";
		
if ([[UIDevice currentDevice] respondsToSelector:@selector(identifierForVendor)]) {
    // This is will run if it is iOS6 or later
    uuid = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
} else {
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    id uuidId = [defaults objectForKey:@"deviceUuid"];
    if (uuidId)
        uuid = (NSString *)uuidId;
else { // Create universally unique identifier (object) CFUUIDRef uuidObject = CFUUIDCreate(kCFAllocatorDefault); // Get the string representation of CFUUID object. uuid = (__bridge_transfer NSString *)CFUUIDCreateString(kCFAllocatorDefault, uuidObject); CFRelease(uuidObject); [defaults setObject:uuid forKey:@"deviceUuid"]; } }

Apple предлагает использовать теперь метод identifierForVendor. Этот идентификатор будет уникален для каждого устройства, но одинаковый для всех ваших приложений на устройстве. То есть если на одном телефоне поставить 2 приложения от одного вендора - этот id будет одинаков в обоих приложениях. Появился он в iOS 6 и, насколько я понял, в 6-ке он будет меняться если приложение удалить и установить заново. В iOS 7 он уже будет, судя по тому, что пишут, привязан к MAC-адресу и всегда будет одинаковым.

У метода identifierForVendor был баг в iOS 6.0 - у устройств, обновившихся по воздуху, возвращалось значение с нулями. В 6.0.1 и старше это пофиксили.

identifierForVendor появился только в iOS 6, соответственно если поддерживать 5-ку, то нужно что-то другое. Тут на помощь приходит CFUUIDCreate. ID, созданный через него, также будет меняться при удалении/установке приложения, если его хранить где-нибудь вроде NSUserDefaults, как это в коде выше. Если хранить в KeyChain - то можно избежать его смены после установке. Хотя насколько это нужно - лично мне не очень понятно. Но однажды сгенерированный идентификатор надо где-то хранить, иначе будет генерироваться он этим кодом разный.

Такая вот шпаргалка.

comment comments

Пристальный взгляд на иконки в iOS 7

иконки в iOS 7

Перевод статьи с iconfinder.com.

Мы следили за конференцией WWDC компании Apple и более пристально взглянули на новую версию их популярной ОС для мобильных устройств iOS 7. Посмотрим на иконки в iOS 7 поближе.

Читать далее...

comment comments

Использование Google Maps SDK for iOS

Использование Google Maps SDK для iOS

В декабре Google выпустили SDK для встраивания своих карт в iOS-приложение. Последние дни я ковырялся в них, пытаясь встроить их свой проект. Небольшое описание того, как их встроить.

Читать далее...

comment comments

Про экранирование URL'ов изображений с русскими именами в Objective-C

Начало начальство у нас вставлять в новости у нас на портале изображения, названные русскими именами, вроде Фото-Ресторана.jpg.

Картинки из новостей у нас показываются в приложении, соответственно по этим URL'ам они в приложение и тянутся.

Соответственно приходит в приложение URL уже закодированный, например:


http://allcafe.ru/s/pic/news/!_2012/2012_12/%D0%A0%D0%B5%D1%81%D1%82%D0%BE%D1%80%D0%B0%D0%BD-Graf-in-%D0%A1%D0%B0%D0%BD%D0%BA%D1%82-%D0%9F%D0%B5%D1%82%D0%B5%D1%80%D0%B1%D1%83%D1%80%D0%B3-%D0%B7%D0%B8%D0%BC%D0%BD%D1%8F%D1%8F-%D1%82%D0%B5%D1%80%D1%80%D0%B0%D1%81%D0%B0.jpg

В приложении используется класс AsynchronousUIImage, найденный мною около года назад в интернетах. Класс просто асинхронно загружает картинку с помощью NSURLConnection.

- (void)loadImageFromURL:(NSString *)anUrl {
    anUrl = [anUrl stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:anUrl] 
        cachePolicy:NSURLRequestReturnCacheDataElseLoad 
        timeoutInterval:30.0
    ];

    connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}

Собственно, в stringByAddingPercentEscapesUsingEncoding оказался затык. Т.к. URL'ы приходят уже экранированными, то, похоже, происходило повторное экранирование и картинка просто не загружалась. Ну а если не прописан из протокола NSURLConnectionDelegate метод connection:didFailWithError: - то это чревато падением приложения, с чем я и столкнулся.

Итог ковыряний забавен, если есть URL'ы с кириллицей - то экранировать их надо или на сервере перед отправкой в клиент, или в клиенте. Если экранировать и там, и там - то не работает. Если нигде не экранировать - то тоже не работает :)

Благо я справился на серверной стороне ничего не сломав и избежав срочного обновления клиента. На Android, кстати, такой проблемы нет.

comment comments

Оптимизация iOS-приложения для экрана iPhone 5

  1. Для xib-файлов для View выставить Size: Freeform. Тогда интерфейс на всю высоту экрана растянется. Если надо всё кастомно - то можно выставить для вида размер для 3.5-дюймового экрана или 4-дюймового и программно нужный подставлять.
    Оптимизация ios-приложения для экрана iPhone 5
  2. Самое главное - добавить стартовую картинку для 4-дюймового экрана. Без неё апп почему-то думает, что экран маленький. И да, если раньше никакой стартовой картинки не использовалось - теперь походу придётся.
    Оптимизация ios-приложения для экрана iPhone 5

Как оно со Storyboard пока не знаю. И ещё мелкая полезность - если в коде надо определить - широкий ли экран или обычный, то можно а prefix.pch задефайнить:

#define IS_WIDESCREEN ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )568 ) < DBL_EPSILON ) 

и проверять в коде:

if ( IS_WIDESCREEN == YES ) {
    // юзать интерфейс для 4 дюймов
} else {
    // юзать интерфейс для 3.5 дюймов
}
comment comments

Приложение Этот день теперь и для iPhone

Приложение Этот день

Наконец дописал приложение «Этот день» так, что оно теперь ставится не только на iPad, но и на iPhone.

Полная версия | Lite версия

comment comments

Этот день для iPad 1.1

Этот день 

Вышла версия 1.1 "Этот день" для iPad. Изначально я хотел сделать листалку событий пальцем и мелочь поправить, но как-то это затянулось. Когда наконец закодил - 2 или 3 раза делал Reject приложения из App Store, т.к. находил дикие косяки, от которых приложение просто падало или криво показывалось. Пока исправил всё критичное - вышел новый iPad, поэтому следом я добавил графику для него. А под самый конец обнаружил, что шаринг в ВК требует капчу, пришлось гуглить и писать её обработку. 

В общем, изначально мелкие изменения вылились в довольно нехилый для такого аппа changelog:

  • поддержка iPad с Retina-экраном;
  • события теперь можно пролистывать пальцем; 
  • при публикации события в Facebook или ВКонтакте в приложении теперь появляется уведомление об успешной публикации; 
  • при публикации событий в Facebook или ВКонтакте они публикуются теперь в более понятном виде с полной датой; 
  • добавлена обработки капчи при публикации во ВКонтакте;
  • исправлена ошибка, в результате которой длинные события не публиковались;
  • небольшие доработки и исправления ошибок.

Обновляемся. А кто ещё не установил - ставим!

comment comments

AllCafe для iPhone 2.0

AllCafe для iPhoneВышла AllCafe для iPhone 2.0. 

Что нового в версии 2.0
- Новое оформление информации о ресторане;
- просмотр фотографий ресторанов;
- новое оформление новостей ресторана;
- просмотр фотографий к новости;
- новое оформление отзывов о ресторане;
- просмотр фоторафий в отзыве и всех фотографий из всех отзывов посетителей;
- добавлены журналы ресторанов;
- различные исправления и дополнения.

Читать далее...

comment comments
local_offer ios allcafe

Как определить Retina Display на iPad/iPhone

Внимание! Если вам нужно просто понять, какой экран у вас на вашем iPad, iPhone или iPod - просто перейдите по ссылке: https://arm1.ru/retina/

 

Пытаясь понять, как мне на Objective-C определить наличие Retina-экрана в устройстве, пришлось погуглить. Нашёл такое решение, которое запишу для шпаргалки.

Получаем размеры экрана:

CGRect screenBounds = [[UIScreen mainScreen] bounds];

Возвращает размер экрана, обычно 320x480, даже на iPhone 4, iPhone 4S и iPod Touch вернёт 320x480 (иначе вроде как старые приложения падают). Для iPad возвращает 768x1024 - и на iPad/iPad 2, и на новом iPad с Retina Display.

Получаем масштаб экрана:

CGFloat screenScale = [[UIScreen mainScreen] scale];

Для всех НЕ-retina-экранов вернёт 1.0f. Для Retina-экранов вернёт 2.0f. Касается всех iOS-девайсов.

Посему - имея размеры экрана, характерные для форм-фактора (телефон/айпод или планшет) и зная масштаб - мы можем высчитать настоящий размер экрана девайса:

CGSize screenSize = CGSizeMake(screenBounds.size.width * screenScale, screenBounds.size.height * screenScale);

Если выполнить такой код:

CGRect screenBounds = [[UIScreen mainScreen] bounds];
NSLog(@"%f x %f", screenBounds.size.width, screenBounds.size.height);
    
CGFloat screenScale = [[UIScreen mainScreen] scale];
NSLog(@"%f", screenScale);
    
CGSize screenSize = CGSizeMake(screenBounds.size.width * screenScale, screenBounds.size.height * screenScale);
NSLog(@"%f x %f", screenSize.width, screenSize.height);

то мы увидим в консоли все размеры. В данном случае - запускал на эмуляторе iPad Retina:

Как определить Retina Display на iPad/iPhone

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

P.S. что касается картинок, то нужно создать всего 2 картинки, например "image.png" и такую же картинку в 2 раза больше с именем "image@2x.png", и дальше пользоваться только первой. Например:

[UIImage imageNamed:@"image.png"];

Если на устройстве Retina - то приложние автоматически подхватит файл с большим разрешением (image@2x.png).

comment comments

Полезная штука Prefix.pch

Открыл для себя полезную штуку в разработке под iOS - файл Prefix.pch. pch - Precompiled Header. 

Судя по описанию -  Precompiled Headers компилируются, кэшируются и потом автоматически включаются в каждый компилируемый файл. Поэтому, если есть какой-то класс, который нужен везде или в почти везде в проекте - можно внутри Prefix.pch-файла, который автоматически создаётся в новом проекте, сделать include этого класса и он будет доступен везде. Мне вот совсем не нравилось в каждом View Controller'е снова и снова инклудить один класс, который нужен почти везде. 

Читать далее...

comment comments

Увеличение громкости iPhone 4

Увеличение громкости iPhone 4

 

У меня iPhone  4 из Франции. В телефонах для продажи во Франции Apple обязали ограничить громкость звука в наушниках, мол, заботятся о своих гражданах и не хотят, чтобы они глохли. Мне, в условиях метро, громкости в итоге не хватает, к тому же наушники бывают разные, какие-то играют громче, у каких-то сопротивление другое и они играют тише. 

В общем это ограничение можно обойти. Для этого нужно сделать Jailbreak, после чего получить доступ к файловой системе и изменить пару plist-файлов. Вариантов много - от установки какого-нибудь Терминала через Cydia, до монтирования телефона как раздела на компе. Мне больше нравится второй вариант - поставил Phone Disk - он подклчил мой телефон как раздел, а дальше надо пошариться по файловой системе.

Читать далее...

comment comments

AllCafe для iPhone 1.1.2

AllCafe для iPhoneВыпустили мелкое обновление AllCafe для iPhone.  Основные изменений - поддержка iPhone 3G (то бишь поддержка всей 4-й ветки iOS) и сжатие фотографий при добавлении отзывов на клиенте. К тому же теперь они загружаются в правильной ориентации (вертикальной или горизонтальной). 

Что приятно - заапрувили всего лишь за сутки обновление. Похоже ревьюверы работают в ускоренном режиме сейчас - с 22 декабря по 29 декабря даже iTunes Connect будет закрыт на праздники.


 

comment comments
local_offer allcafe ios

Этот день для iPad

Этот день 

Запустили с товарищем приложение для iPad под названием "Этот день". С почином меня. Подробнее...

comment comments

Встраивание Facebook sdk в ios-приложение

Шпаргалка. Инструкция по тому, где скачать и как встроить - в документации Facebook. Несмотря на то, что обновляли они проект на Github недавно (23 ноября на данный момент), в нагрузку идёт у них старая версия фреймворка для работы с JSON. А, т.к. в мой проект уже встроена более новая версия фреймворка приложение не компилилось.

Решение:

  1. после добавления SDK в проект удалить папку JSON из SDK Facebook;
  2. в файле FBRequest.m заменить строку #import "JSON.h" на #import "SBJson.h";
  3. в том же файле строку SBJSON *jsonParser = [[SBJSON new] autorelease] заменить на SBJsonParser *jsonParser = [[SBJsonParser new] autorelease].

Должно работать. 

comment comments

Приложение AllCafe для iPhone

Приложение AllCafe для iPhoneМы наконец-то сделали первую версию AllCafe для iPhone, которую уже можно показывать.

Сколько раз вы попадали в ситуацию, когда приходишь в заведение - а там нет мест или просто вам там сегодня не нравится? Я - довольно часто. И первая мысль - а где можно ещё посидеть/поесть/попить рядом?

На нашем портале  AllCafe.ru собрана довольно большая база ресторанов по всей России. А также отзывов пользователей о них. Мы сделали приложение для iPhone, которое, кстати, весил всего около 200 кб. С его помощью можно посмотреть на карте ближайшие заведения, либо увидеть их списком с примерным расстоянием.

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

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

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

Собственно - это моё первое приложение для iOS. С почином меня.

Ссылка на приложение в iTunes: AllCafe для iPhone

AllCafe для iPhone

Читать далее...

comment comments
local_offer allcafe ios

Использование UINavigationController вместе с UITabBarController

Появилась нужна использовать в проекте и UINavigationController, и UITabBarController одновременно. Два способа как это сделать.

Читать далее...

comment comments

Mac OS X Lion и iCloud

Apple iCloud
 

Ну вот, вышла новая макось с поддержкой iCloud, да и сам iCloud вышел из beta.  Кстати, в этом посте я плюю на ваш траффик, но заботливо предупреждаю - скрины в png с размером от 150 кб до 1 мб =) 

Читать далее...

comment comments

Работа с JSON (парсинг) в Objective-C при разработке под iOS

Ещё один пост, чтобы у самого в голове улеглось. Про работу с JSON в Objective-C на примере парсинга твитов из публичной ленты Twitter.

Читать далее...

comment comments

Резюме встречи Apple Developers Community #7

Побывал сегодня на Apple Developers Community #7. Рассказывали про продвижение ios-приложений. По сути докладывались два человека - представитель компании Nevosoft, которая, похоже, на конвеере фигачит игры, и один инди-разработчик.

Читать далее...

comment comments