30 августа 2010 г.

Создание шаблонов приложений в xCode (часть 1)

Часто при создании новых проектов приходится выполнять много рутинной работы: добавлять фраемворки и библиотеки, создавать стандартные классы и т.д. Стандартные шаблоны дают только общие несколько классов. В моих проектах всегда присутствует несколько стандартных классов, которые в процессе программирования обрастают разной функциональностью (так что их в библиотеки пихать нельзя). Я решил разобраться с механизмом шаблонов в xCode.

Шаблоны xCode лежат в папках:
/Developer/Library/Xcode/Project Templates/ - для Mac OS X проектов
/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Project Templates/ - для iOS проектов

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



Эта папка состоит из одного или нескольких проектов xCode и файла с правилами выбора проекта из этого списка (TemplateChooser.plist).



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

Открываем проект в xCode. И делаем с ним все что пожелаем: добавляем классы, ресурсы, plist-ы. При добавлении файлов свойство "Path type" выставляйте в "Relative to Enclosing Group".



После перезагрузки xCode при создании нового проекта появиться созданный шаблон



Если вы компилировали проект шаблона, не забудьте удалить папку "build" из проекта шаблона.

В этой статье ничуть не затронут файл TemplateChooser.plist, трогаем во второй части.

27 августа 2010 г.

Создание .ipa-архива из .app

1. Создаем папку Payload
2. Копируем в нее папку *.app
3. По желанию можно иконку приложения переименовать в iTunesArtwork
4. Упаковываем Payload и iTunesArtwork обычным zip архиватором

5. Переименовываем .zip архив в .ipa

Полученный файл .ipa можно добавлять в iTunes и синхронизировать с мобильным устройством.

PS: iTunes так-же поймет если в него кинуть папку .app

25 августа 2010 г.

Запись аудио с микрофона iPhone

Во времена прошивок 2.х для меня стало открытием, что подключив гарнитуру к iPod Touch можно использовать микрофон для записи аудио. А вот в 3-й прошивке появилось приложение "Диктофон", так что возможность подключения микрофона стала очевидной.

Есть несколько способов записи звука с вашего приложения. Я расскажу о наборе функций из фраемворка AudioToolbox.

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

Для задания формата входного буфера и параметров аудио используется структура AudioStreamBasicDescription. Эта структура передается в функцию AudioQueueNewInput, эта функция создает новый объект для записи.

  1. AudioStreamBasicDescription mDataFormat;
  2. mDataFormat.mFormatID = kAudioFormatLinearPCM;
  3. mDataFormat.mSampleRate = 44100;
  4. mDataFormat.mChannelsPerFrame = 1;
  5. mDataFormat.mBitsPerChannel = 16;
  6. mDataFormat.mBytesPerPacket =
  7. mDataFormat.mBytesPerFrame = mDataFormat.mChannelsPerFrame * sizeof(short int);
  8. mDataFormat.mFramesPerPacket = 1;
  9. mDataFormat.mFormatFlags = kLinearPCMFormatFlagIsBigEndian
  10. | kLinearPCMFormatFlagIsSignedInteger
  11. | kLinearPCMFormatFlagIsPacked;
  12. AudioQueueRef queue;
  13. AudioQueueNewInput(&mDataFormat, AQInputCallback,
  14. NULL, NULL, kCFRunLoopCommonModes, 0, &queue);


Вторым параметром задается функция-слушатель, которая вызывается по мере заполнения аудиобуферов. В ней и происходит вся работа по обработке аудио, будь то запись в файл или отсылка в сеть.

Теперь необходимо подготовить аудиобуферы в которые будет помещаться считанный аудиопоток. Функция AudioQueueAllocateBuffer выделяет память под буфер, а AudioQueueEnqueueBuffer добавляет к нашему объекту для записи полученный функцией AudioQueueNewInput.

  1. unsigned long frameSize = mDataFormat.mSampleRate * mDataFormat.mBytesPerFrame;
  2. AudioQueueBufferRef aBuffers[AUDIO_BUFFERS];
  3. for (int i=0; i<AUDIO_BUFFERS; i++)
  4. {
  5. AudioQueueAllocateBuffer(queue, frameSize, &aBuffers[i]);
  6. AudioQueueEnqueueBuffer(queue, aBuffers[i], 0, NULL);
  7. }


Подготовка закончена. Запускаем запись аудио функцией AudioQueueStart.

  1. AudioQueueStart(queue, NULL);


Остановить запись можно функцией AudioQueueStop:

  1. AudioQueueStop(queue, true);


И освобождение памяти от объекта queue функцией AudioQueueDispose

  1. AudioQueueDispose(queue, true);


Пример от Apple SpeakHere мне показался большим и сложным для понимания, мой более компактный.

24 августа 2010 г.

FileSharing копирование файлов через iTunes на/с iPhone

Начиная с версии 4.0 в iOS есть поддержка копирования пользовательских файлов на или с вашего iУстройства через iTunes (начиная с версии 9.1). Находится это в секции "Программы":



Эта папка из вашего приложения видна как папка "Documents"

Для включения поддержки в ваше приложение вам программировать практически ничего не нужно. В Info.plist вашего приложения включите ключ UIFileSharingEnabled и присвойте ему значение YES. Так iTunes понимает что папка "Documents" этого приложения доступна для передачи файлов.



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

  1. NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
  2. NSUserDomainMask, YES);
  3. NSError *error;
  4. NSFileManager *fileManager = [NSFileManager defaultManager];
  5. NSLog(@"%@", [fileManager contentsOfDirectoryAtPath:[paths objectAtIndex:0] error:&error]);


Небольшой пример.

20 августа 2010 г.

Веб-приложение для iPhone (ориентация и определение устройства)

Рассмотрим несколько простых трюков в на java-script которые помогут определить, что этот java-script запущен на iУстройстве а так-же подписаться на событие изменения положения устройства.

Строка navigator.appVersion дает много полезной информации о браузере в котором выполняется java-script. Можно например так узнать, выполняемся ли мы на iPhone:

  1. if (navigator.appVersion.indexOf('iPhone OS ') >= 0)
  2. {
  3. // this iPhone
  4. }

так же, браузер вызывает каждый раз при изменении ориентации устройства метод window.onorientationchange, ее можно легко переопределись:

  1. window.onorientationchange = function ()
  2. {
  3. switch ( window.orientation )
  4. {
  5. case 0:
  6. case 180:
  7. // portrait mode
  8. break;
  9. case -90:
  10. case 90:
  11. // landscape mode
  12. break;
  13. }
  14. };

еще переменная window.navigator.standalone указывает на то, запущено наше приложение в браузере или установлено как отдельное приложение.

как-то сухо все получилось, я учусь.
Пример доступен по классической ссылке, либо по qr-коду:

18 августа 2010 г.

Размер клавиатуры в iOS

Иногда необходимо узнать размер виртуальной клавиатуры из приложения. Например чтоб изменить положение/размер ваших визуальных компонентов на view. До появления iPad/iPhone 4 можно было смело использовать константы. С выходом устройств с различным разрешением экрана константы использовать нельзя.

Первое, что нам понадобится, зарегистрировать слушателя на появление клавиатуры на экране (делается это например в методе viewDidAppear: вашего UIViewController):

  1. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShown:) name:UIKeyboardWillShowNotification object:nil];

в конце не забываем отписаться от получения сообщений повеления клавиатуры

  1. [[NSNotificationCenter defaultCenter] removeObserver:self];

а вот и сама функция-слушатель:

  1. - (void) keyboardWillShown:(NSNotification*) aNotification
  2. {
  3.   NSDictionary* info = [aNotification userInfo];
  4.  
  5.   #ifdef __IPHONE_4_0
  6.   NSValue* aValue = [info objectForKey:UIKeyboardBoundsUserInfoKey];
  7.   if (!aValue)
  8.   {
  9.     aValue = [info objectForKey:UIKeyboardFrameEndUserInfoKey];
  10.   }
  11.   #else
  12.   NSValue* aValue = [info objectForKey:UIKeyboardBoundsUserInfoKey];
  13.   #endif
  14.  
  15.   CGSize keyboardSize = [aValue CGRectValue].size;
  16.   NSLog(@"%.1fx%.1f", keyboardSize.width, keyboardSize.height);
  17. }

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

Пример, таблица в каждом элементе которой есть UITextField при выборе которого таблица центруется на этом элементе.

16 августа 2010 г.

Установка приложения на устройство с прошивкой 3.х из Xcode 3.2.4

Аpple из новых версий Xcode убрали iPhone Simulator версий 2.х и 3.х. Так же базовой версией SDK можно поставить только версии 4.х. Не обязательно держать установленным две версии Xcode для поддержки из вашего приложения версий 3.х и 4.х. Из новой версии Xcode можно устанавливать приложения на устройства с прошивкой 3.0, для этого нужно в свойствах проекта выставить свойство "iOS Deployment Target" на нужную версию SDK вашего устройства.

По комментариям статьи.

PS: перед использованием функций которых нет в более ранних версиях SDK следует проверять их наличие

  1. if ([obj respondsToSelector:@selector(someSelector:)])
  2. {
  3.   [obj performSelector:self withObject:nil];
  4. }

13 августа 2010 г.

Bash размер истории

За размер истории в коммандной оболочке Bash отвечает переменная окружения HISTFILESIZE и HISTSIZE, в них находится количество комманд истории которая хранится. По умолчанию это чисто 500.

Простой коммандой прописываем в .bashrc новое значение глубины истории:
  1. $ echo "export HISTFILESIZE=3000" >> ~/.bashrc

12 августа 2010 г.

Фикс проблеммы сохранения скриншотов экрана iPod Touch 3.1.2

Многие знают, что при одновременном нажатии на вашем iУстройстве клавиш Home и Power делается снимок экрана, и этот снимок помещается в приложение фотографии. Удобная функция если нужно показать заказчику как выглядит приложение.

При очередной перепрошивке своего iPod Touch у меня перестали сохранятся снимки экрана в приложении "Фотографии". Я чесно долго терпел и мирился с этой проблеммой, пока наконец не захотел решить ее. Вряд ли эта проблемма присутсвует на не джеилбрейкнутых устройсвах, поэтому мое решение только для устройст на которых сделан джеилбрейк.

Просто нужно удалить на вашем устройстве папку /var/mobile/Media/DCIM с помощью любого файл менеджера. Например iFile прямо с устройства.

10 августа 2010 г.

Cтруктура mpeg4

MP4Box неправильно прописывал некоторые параметры субтитров при конвертировании видео для iPhone (статья). Можно было использовать Dumpster Atom Inspector для ручного редактирования нужных полей, но я хотел написать скрипт который делает все автоматически.

Файл формата MPEG4 (будь то .mov, .mp4 или .m4v) состоит из блоков, называемых атомами. Каждый атом имеет формат:
- байты 0-3 - длинна атома (включая эти 4 байта)
- байты 4-7 - имя атома (четырехсимвольная строка, выделенная строка на рисунке)
- байты 8-… - данные атома



Вот из таких атомов и состоит MPEG4 файл. В каждом видео файле должны присутствовать атомы с именами "ftyp", "moov" и "mdat".

В "ftyp" содержится тип этого файла и типы версий основных структур файла. Этот атом всегда первый в файле. Атом "mdat" содержит потоки видео, аудио, субтитров. Атом "moov" для меня был самым интересным, так как он содержит описание треков, информацию о тегах.

Данные атома "moov" (то что начинается с 8-го байта) имеют древовидную структуру состоящих из блоков такого-же формата что и атом (т.е. 4 байта - размер блока, 4-байта - имя блока, с 8-го байта - данные). Формат блока отределяет эго имя, например блок с именем "mvhd" занимает 108 байт, и понять, что там записано не составит труда посмотрев на содержимое этой секции в Atom Inspector.



Кстати о размере блоков, как я понимаю они записаны в "человеческом" виде, т.е. чем байт старше, тем он левее, в компьютере числа представлены в "перевернутом" виде - чем старше байт, тем он правее. Учтите это при получении размеров атомов.

Так же в атоме "moov" -> "udata" -> "meta" содержится информация о тегах iTunes (обложка фильма включительно). Каждый тег в этом блоке так-же представлен блоком формата атом.

Скрипт на Python который печатает структуру MPEG4 файла.

7 августа 2010 г.

Информация о сети

В iOS 4.0 Apple дала возможность разработчикам получить информацию о сети в которой находится телефон, а так-же о текущих звонках. Итак, фраемворк CoreTelephony. Он представлен всего несколькими классами, но из них можно вынести интересную информацию. Класс CTCarrier содержит информацию о сети, CTCall - о текущем звонке. Подписаться на получение изменений информации о звонках и изменении сети можно зарегистрировав слушателей subscriberCellularProviderDidUpdateNotifier и callEventHandler в классах CTTelephonyNetworkInfo и CTCallCenter.

Простой пример

4 августа 2010 г.

Local Notifications в iOS 4.0

Начиная с iOS SDK 4.0 появилась возможность отсылать локальные сообщения. Локальные сообщения - это сообщения пользователю от вашей программы в определенное время, не зависимо от того, запущено ли ваше приложение. Очень похоже на Push Notification но действует только в пределах одного устройства (без участия севера). Как и Push Notification Local Notification может быть доставлен пользователю как: звуковое сообщение, цифра на иконке приложения и как алерт. Не думайте, что при посылке Local Notification ваше приложение запустится, это произойдет только в том случае, если ваше Local Notification имеет тип алерт и пользователь на этом всплывающем окне нажал кнопку "View". Так что никакой важной информации не стоит доставлять через Local Notification, хотя эти сообщения и надежнее чем Push Notification так как не зависят от состояния сети.

Регистрация в системе Local Notification осуществляет метод scheduleLocalNotification: класса UIApplication. Параметром этого метода служит объект класса UILocalNotification. Код который осуществляет отсылку сообщения:

  1. UILocalNotification* alarm = [[[UILocalNotification alloc] init] autorelease];
  2.  if (alarm)
  3.  {
  4.   alarm.fireDate = [[NSDate date] dateByAddingTimeInterval:30.0];
  5.   alarm.timeZone = [NSTimeZone defaultTimeZone];
  6.   alarm.repeatInterval = 0;
  7.   alarm.alertBody = @"local notification";
  8.   UIApplication *app = [UIApplication sharedApplication];
  9.   [app scheduleLocalNotification:alarm];
  10.  }

Этот код создает объект alarm типа alert c текстом "local notification" и регистрирует его в системе как событие, которое произойдет через 30 сек.

Посмотреть зарегистрированные в системе от вашего приложении нотификации позволяет метод scheduledLocalNotifications. Отменить все нотификации можно методом cancelAllLocalNotifications.

Что э происходит если событие нотификации происходит при запущенном приложении? Системных событий тогда никаких не происходит (не звуков не проигрывается, не показывается всплывающее окно), но делегату вашего приложения приходит событие application:didReceiveLocalNotification: в котором можно обработать сообщение.

Пример работы.