<?xml version="1.0" encoding="windows-1251"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<atom:link href="https://programir.mybb.ru/export.php?type=rss" rel="self" type="application/rss+xml" />
		<title>ПрограМир</title>
		<link>http://programir.mybb.ru/</link>
		<description>ПрограМир</description>
		<language>ru-ru</language>
		<lastBuildDate>Wed, 14 Aug 2013 12:11:22 +0400</lastBuildDate>
		<generator>MyBB/mybb.ru</generator>
		<item>
			<title>Проверка подключения к Интернету на iOS, доступность ресурса, определе</title>
			<link>http://programir.mybb.ru/viewtopic.php?pid=51#p51</link>
			<description>&lt;p&gt;Задача проверки подключения к сети достаточно популярная и встречается в большинстве приложений. Самое простое что может прийти на ум — попытаться загрузить главную страничку какого-либо сайта. Но это не самый лучший вариант. Во-первых он требует загрузки данных; во-вторых если ресурс недоступен — нам нужно будет дождаться тайм-аута, который по-умолчанию составляет 30 секунд (но его можно и изменить); в третьих — … и так далее.&lt;/p&gt;
						&lt;p&gt;Apple предлагает класс, который называется Reachability. Этот класс несуществует в iOS SDK, но его исходный код можно загрузить с сайта developer.apple.com отсюда. По ссылке — проект, демонстрирующий возможности Reachability. Загрузить его можно нажав кнопку «Download Sample code». Чтобы подключить к вашему проекту этот класс, нужно скопировать и добавить в проект два файла — Reachability.h и Reachability.m. Так же требуется добавить в проект SystemConfiguration-framework.&lt;/p&gt;
						&lt;p&gt;Каждый созданный объект класса Reachability информирует нас о доступности конкретного вида подключения либо хоста. Это значит, что для проверки доступности сайта &lt;a href=&quot;http://apple.com&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;http://apple.com&lt;/a&gt; и &lt;a href=&quot;http://google.com&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;http://google.com&lt;/a&gt; нам понадобятся разные объекты Reachability. Хотя, можно просто проверить доступность интернета.&lt;/p&gt;
						&lt;p&gt;Проверка соеденения до конкретного хоста&lt;/p&gt;
						&lt;p&gt;Итак, класс Reachability добавлен в проект и SystemConfiguration-framework подключен. Чтобы проверить соеденение до конкретного хоста, создадим объект Reachability следующим образом:&lt;/p&gt;
						&lt;p&gt;Reachability* appleReachability = [Reachability reachabilityWithHostName: @&amp;quot;www.apple.com&amp;quot;];&lt;br /&gt;Конечно же, в любой момент времени мы можем захотеть проверить состояние подключения/доступности. Делается это путем проверки свойства currentReachabilityStatus:&lt;/p&gt;
						&lt;p&gt;NetworkStatus status = appleReachability.currentReachabilityStatus;&lt;/p&gt;
						&lt;p&gt;switch (status) {&lt;br /&gt;&amp;#160; &amp;#160; case NotReachable:&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; // хост недоступен&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; break;&lt;br /&gt;&amp;#160; &amp;#160; case ReachableViaWiFi:&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; // доступен через WiFi&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; break;&lt;br /&gt;&amp;#160; &amp;#160; case ReachableViaWWAN:&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; // доступен через 3G или EDGE&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; break;&lt;br /&gt;}&lt;br /&gt;Проверка подключения к интернету и к Wi-Fi&lt;/p&gt;
						&lt;p&gt;Чтобы проверять подключение к интернету, нужно создать Reachablility следующим образом:&lt;/p&gt;
						&lt;p&gt;Reachability* internetReachability = [Reachability reachabilityForInternetConnection];&lt;br /&gt;Для проверки подключения к Wi-Fi:&lt;br /&gt;Reachability* wifiReachability = [Reachability reachabilityForLocalWiFi];&lt;br /&gt;Проверка состояния осуществляется все так же через свойство currentReachabilityStatus.&lt;/p&gt;
						&lt;p&gt;Уведомление об изменении состояния&lt;/p&gt;
						&lt;p&gt;Конечно же, можно всегда вручную проверять состояние подключения, но можно и получать уведомления об изменении состояния. Для этого нужно подписаться на kReachabilityChangedNotification:&lt;/p&gt;
						&lt;p&gt;[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(reachabilityChanged:) name: kReachabilityChangedNotification object: nil];&lt;br /&gt;И, конечно же, определить метод, который будет слушать уведомление:&lt;/p&gt;
						&lt;p&gt;- (void) reachabilityChanged: (NSNotification* )note&lt;br /&gt;{&lt;br /&gt;&amp;#160; &amp;#160; Reachability* curReach = [note object];&lt;br /&gt;&amp;#160; &amp;#160; NetworkStatus status = curReach.currentReachabilityStatus;&lt;/p&gt;
						&lt;p&gt;&amp;#160; &amp;#160; switch (status) {&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; case NotReachable:&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; // хост недоступен&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; break;&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; case ReachableViaWiFi:&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; // доступен через WiFi&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; break;&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; case ReachableViaWWAN:&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; // доступен через 3G или EDGE&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; break;&lt;br /&gt;&amp;#160; &amp;#160; }&lt;br /&gt;}&lt;br /&gt;После подписки на уведомления, нужно «попросить» объект Reachability генерировать уведомления об изменении состояния:&lt;/p&gt;
						&lt;p&gt;Reachability* internetReachability = &amp;lt;#объект Reachability#&amp;gt;;&lt;br /&gt;[internetReachability startNotifier];&lt;br /&gt;Теперь мы будем получать уведомления об изменении состояния подключения.&lt;/p&gt;</description>
			<author>mybb@mybb.ru (Gates)</author>
			<pubDate>Wed, 14 Aug 2013 12:11:22 +0400</pubDate>
			<guid>http://programir.mybb.ru/viewtopic.php?pid=51#p51</guid>
		</item>
		<item>
			<title>Публикация изображения из приложения в Instagram</title>
			<link>http://programir.mybb.ru/viewtopic.php?pid=50#p50</link>
			<description>&lt;p&gt;Суть данного способа заключается в передаче изображения из вашего приложения прямо в Instagram-экран с фильтрами (ответ на вопрос «почему так?» можно найти здесь). Так как в данном примере будет использоваться изображение, полученное с камеры iOS устройства — логично разделить его на 2 этапа:&lt;/p&gt;
						&lt;p&gt;1. Запуск камеры и получение снимка (для этого воспользуемся классом UIImagePickerController:).&lt;br /&gt;UIImagePickerController *picker = [[UIImagePickerController alloc] init];&lt;br /&gt;picker.delegate = self;&lt;br /&gt;// Включаем редактирование изображений (масштабирование и перемещение)&lt;br /&gt;picker.allowsEditing = YES;&lt;br /&gt;// Устанавливаем в качестве источника получения данных камеру&lt;br /&gt;picker.sourceType = UIImagePickerControllerSourceTypeCamera;&lt;br /&gt;[self presentModalViewController: picker animated: YES];&lt;br /&gt;[picker release];&lt;/p&gt;
						&lt;p&gt;2. Открытие полученного изображения в приложении Instagram (для этого воспользуемся классом UIDocumentInteractionController)&lt;br /&gt;-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage : (UIImage *)image editingInfo:(NSDictionary *)editingInfo&lt;br /&gt;{&lt;br /&gt;&amp;#160; &amp;#160; NSString *documentsDirectory = [NSHomeDirectory() stringByAppendingPathComponent:@&amp;quot;Documents&amp;quot;];&lt;br /&gt;&amp;#160; &amp;#160; // Сохраняем изображение с разрешением *.igo&lt;br /&gt;&amp;#160; &amp;#160; NSString *savedImagePath = [documentsDirectory stringByAppendingPathComponent:@&amp;quot;image.igo&amp;quot;];&lt;br /&gt;&amp;#160; &amp;#160; NSData *imageData = UIImagePNGRepresentation(image);&lt;br /&gt;&amp;#160; &amp;#160; [imageData writeToFile:savedImagePath atomically:YES];&lt;/p&gt;
						&lt;p&gt;&amp;#160; &amp;#160; // Проверяем, доступно ли приложение Instagram на данном устройстве&lt;br /&gt;&amp;#160; &amp;#160; NSURL *instagramURL = [NSURL URLWithString:@&amp;quot;instagram://app&amp;quot;];&lt;br /&gt;&amp;#160; &amp;#160; if ([[UIApplication sharedApplication] canOpenURL:instagramURL]) {&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;NSURL *imageUrl = [NSURL fileURLWithPath:savedImagePath];&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;UIDocumentInteractionController * docController = [[UIDocumentInteractionController alloc] init];&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;docController.delegate = self;&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;[docController retain];&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;// Указываем тип программ для открытия&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;docController.UTI = @&amp;quot;com.instagram.exclusivegram&amp;quot;;&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;[docController setURL:imageUrl];&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;// Показываем меню выбора доступных приложений для отображения изображения&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;[docController presentOpenInMenuFromRect:CGRectZero inView:self.view animated:YES];&lt;br /&gt;&amp;#160; &amp;#160; } else {&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; // Если приложение Instagram не найдено - показываем соответствующее сообщение&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@&amp;quot;Warning&amp;quot;&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;message:@&amp;quot;You have no installation Instagram App&amp;quot; delegate:self cancelButtonTitle:@&amp;quot;OK&amp;quot;&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;otherButtonTitles:nil];&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; [alert show];&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; [alert release];&lt;br /&gt;&amp;#160; &amp;#160; }&lt;br /&gt;}&lt;br /&gt;В нашем случае для открытия изображения будет использоваться только Instagram. Для использования всех подходящих приложений устройства нужно заменить строки&lt;br /&gt;NSString *savedImagePath = [documentsDirectory stringByAppendingPathComponent:@&amp;quot;image.igo&amp;quot;];&lt;br /&gt;docController.UTI = @&amp;quot;com.instagram.exclusivegram&amp;quot;; &lt;br /&gt;на&lt;br /&gt;NSString *savedImagePath = [documentsDirectory stringByAppendingPathComponent:@&amp;quot;image.ig&amp;quot;];&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; docController.UTI = @&amp;quot;com.instagram.photo&amp;quot;;&lt;br /&gt;В случае загрузки изображения из другого источника нужно предварительно сохранить его в формате PNG или JPEG (преимущественно JPEG). Для получения лучшего результата рекомендуется использовать JPEG с разрешением как минимум 612 x 612 пикселей, иначе оно не будет загружено (пользователю будет показано сообщение о невозможности загрузки).&lt;/p&gt;</description>
			<author>mybb@mybb.ru (Gates)</author>
			<pubDate>Wed, 14 Aug 2013 12:10:25 +0400</pubDate>
			<guid>http://programir.mybb.ru/viewtopic.php?pid=50#p50</guid>
		</item>
		<item>
			<title>Новое в iOS 5. Показываем определение слова или фразы внутри приложени</title>
			<link>http://programir.mybb.ru/viewtopic.php?pid=49#p49</link>
			<description>&lt;p&gt;Одним из нововведений iOS 5 является UIReferenceLibraryViewController, который предоставляет возможность отображения информации о значении, произношении, происхождении как слова, так и фразы. Но есть одно НО: его нельзя использовать для отображения списка слов, публикации контента и создания автономного словаря.&lt;/p&gt;
						&lt;p&gt;Например, чтобы отобразить значение слова iPhone, мы должны воспользоваться примером:&lt;br /&gt;UIReferenceLibraryViewController *referenceLibraryVC = [[UIReferenceLibraryViewController alloc] initWithTerm:@&amp;quot;iPhone&amp;quot;];&lt;br /&gt;[self presentModalViewController:referenceLibraryVC animated:YES];&lt;br /&gt;[referenceLibraryVC release];&lt;br /&gt;Мы также можем проверять, есть ли значение нужного слова в словаре с помощью статического метода dictionaryHasDefinitionForTerm (если его нет — например, показывать сообщение):&lt;br /&gt;if(![UIReferenceLibraryViewController dictionaryHasDefinitionForTerm:@&amp;quot;iPhone&amp;quot;])&lt;br /&gt;&amp;#160; &amp;#160; {&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:@&amp;quot;Слово не найдено&amp;quot; delegate:nil cancelButtonTitle:@&amp;quot;OK&amp;quot; otherButtonTitles:nil, nil];&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; [alert show];&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; [alert release];&lt;br /&gt;&amp;#160; &amp;#160; }&lt;br /&gt;На следующих картинках можно увидеть результат поиска слова, фразы и случая, когда ничего не найдено&lt;/p&gt;</description>
			<author>mybb@mybb.ru (Gates)</author>
			<pubDate>Wed, 14 Aug 2013 12:10:00 +0400</pubDate>
			<guid>http://programir.mybb.ru/viewtopic.php?pid=49#p49</guid>
		</item>
		<item>
			<title>Создаем UITextField с закругленными краями</title>
			<link>http://programir.mybb.ru/viewtopic.php?pid=48#p48</link>
			<description>&lt;p&gt;Как-то задумал я сделать UITextField с закругленными краями, такой же как поле поиска в мобильном Safari. К сожалению в iOS SDK нету такого типа поля, есть с недостаточно закругленными краями. Первое что приходит на ум — использовать UIImageView с картинкой, а поверх него положить UITextField. Решение подходит, если поле будет оставаться таких же размеров с какими было создано. Но мне нужно было выполнять анимации над полем, в таком случае пришлось бы пересчитывать размеры картинок в блоке анимации, что порождало бы лишний код. К тому же, хотелось чтобы текстовое поле существовало неразрывно с его закругленными краями. В общем, такое решение не годится. Попытки установить угол закругления через QuartzCore и CALayer так же не дали нужного результата.&lt;/p&gt;
						&lt;p&gt;Сделаем небольшую постановку задачи: создать текстовое поле с закругленными краями, которое можно использовать как и через Interface Builder, так и создавая кодом. Поле должно быть неразрывно с картинкой, отображающей закругленные края. Текст должен помещаться внутрь заданной области и не перекрывать края закругления.&lt;/p&gt;
						&lt;p&gt;Приступаем к решению&lt;/p&gt;
						&lt;p&gt;Для начала, я решил подготовить картинки этих самых закругленных полей. Тут очень помог сайт teehanlax.com с iOS 5 GUI PSD. В итоге я получил вот это:&lt;br /&gt;&lt;/p&gt;
						&lt;p&gt;Как видите, я выделил центральную часть, которая будет растягиваться и два закругленных края.&lt;/p&gt;
						&lt;p&gt;Так как я хотел получить нечто целое, хорошо бы создать свой класс текстового поля и наследовать его от UITextField:&lt;/p&gt;
						&lt;p&gt;@interface RoundedTextField : UITextField&lt;br /&gt;@end&lt;br /&gt;Первым делом следует перекрыть метод setBorderStyle: так как он может повлиять на вид поля, а я этого очень не хотел.&lt;/p&gt;
						&lt;p&gt;-(void) setBorderStyle:(UITextBorderStyle)borderStyle&lt;br /&gt;{&lt;br /&gt;&amp;#160; &amp;#160; [super setBorderStyle:UITextBorderStyleNone];&lt;br /&gt;}&lt;br /&gt;Если кто-то задумает изменить стиль границ поля (например, Interface Builder) — этого не получится.&lt;/p&gt;
						&lt;p&gt;Немного про Interface builder: так как я планировал добавлять это поле через Interface Builder и класс поля унаследован от UITextField — можно смело добавлять на вид стандартный UITextField, изменив ему Custom class.&lt;br /&gt;&lt;/p&gt;
						&lt;p&gt;На картинке вы видите текстовое поле с типом границ UITextBorderStyleRoundedRect, но это никак не влияет на наше поле т.к. мы перекрыли метод установки типа границ.&lt;/p&gt;
						&lt;p&gt;Отрисовка картинок&lt;/p&gt;
						&lt;p&gt;Закругленные края и центральную часть я решил рисовать в методе drawRect:, выглядело это так:&lt;/p&gt;
						&lt;p&gt;-(void) drawRect:(CGRect)rect&lt;br /&gt;{&lt;br /&gt;&amp;#160; &amp;#160; UIImage* leftImage&amp;#160; &amp;#160;= [UIImage imageNamed:@&amp;quot;leftRoundedField.png&amp;quot;];&lt;br /&gt;&amp;#160; &amp;#160; UIImage* rightImage&amp;#160; = [UIImage imageNamed:@&amp;quot;rightRoundedField.png&amp;quot;];&lt;br /&gt;&amp;#160; &amp;#160; UIImage* centerImage = [UIImage imageNamed:@&amp;quot;centerRoundedField.png&amp;quot;];&lt;/p&gt;
						&lt;p&gt;&amp;#160; &amp;#160; // левая граница, рисуем сразу от начала&lt;br /&gt;&amp;#160; &amp;#160; [leftImage drawAtPoint:CGPointZero];&lt;br /&gt;&amp;#160; &amp;#160; // правая граница&lt;br /&gt;&amp;#160; &amp;#160; [rightImage drawAtPoint:CGPointMake(self.bounds.size.width - rightImage.size.width, 0)];&lt;br /&gt;&amp;#160; &amp;#160; // центральная часть&lt;br /&gt;&amp;#160; &amp;#160; CGRect centerImageRect = {&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; {leftImage.size.width, 0}, // точка&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; {self.bounds.size.width-leftImage.size.width-rightImage.size.width, centerImage.size.height} // размеры&lt;br /&gt;&amp;#160; &amp;#160; };&lt;br /&gt;}&lt;br /&gt;Получилось вот что:&lt;br /&gt;&lt;/p&gt;
						&lt;p&gt;Как видим, текст перекрывает левую границу, что никуда не годится. Отложив на время решение этой проблемы, я решил посмотреть как анимируется изменение размеров поля. Я запустил такую анимацию:&lt;/p&gt;
						&lt;p&gt;[UIView animateWithDuration:1.0 animations:^{&lt;br /&gt;&amp;#160; &amp;#160; self.textField.frame = CGRectInset(self.textField.frame, -50.0, 0);&lt;br /&gt;}];&lt;br /&gt;И получил такой результат:&lt;br /&gt;&lt;/p&gt;
						&lt;p&gt;Очевидно, что нужно перерисовывать картинки при изменении фрейма, я перекрыл метод изменения фрейма. Сделал, чтобы при изменении фрейма содержимое рисовалось заново (вызывался метод drawRect:):&lt;/p&gt;
						&lt;p&gt;-(void) setFrame:(CGRect)frame&lt;br /&gt;{&lt;br /&gt;&amp;#160; &amp;#160; [super setFrame:frame];&lt;br /&gt;&amp;#160; &amp;#160; [self setNeedsDisplay];&lt;br /&gt;}&lt;br /&gt;Результат был следующий, и он был таким как мне нужен. Но в процессе анимации поле выглядело деформированным:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
						&lt;p&gt;Подумав, что это никак не исправить, я решил не использовать рисование в методе drawRect:. Я решил добавлять картинки границ используя UIImageView (уж они то точно не деформируются!), но не отдельно от UITextField, а на него. При таком подходе можно избавиться от перекрытия метода setFrame:.&lt;/p&gt;
						&lt;p&gt;-(void) configureTextField&lt;br /&gt;{&lt;br /&gt;&amp;#160; &amp;#160; UIImage* leftImage&amp;#160; &amp;#160;= [UIImage imageNamed:@&amp;quot;leftRoundedField.png&amp;quot;];&lt;br /&gt;&amp;#160; &amp;#160; UIImage* rightImage&amp;#160; = [UIImage imageNamed:@&amp;quot;rightRoundedField.png&amp;quot;];&lt;br /&gt;&amp;#160; &amp;#160; UIImage* centerImage = [UIImage imageNamed:@&amp;quot;centerRoundedField.png&amp;quot;];&lt;/p&gt;
						&lt;p&gt;&amp;#160; &amp;#160; UIImageView* leftImageView = [[[UIImageView alloc] initWithImage:leftImage] autorelease];&lt;br /&gt;&amp;#160; &amp;#160; leftImageView.frame = CGRectMake(0, 0, leftImage.size.width, leftImage.size.height);&lt;br /&gt;&amp;#160; &amp;#160; UIImageView* rightImageView = [[[UIImageView alloc] initWithImage:rightImage] autorelease];&lt;br /&gt;&amp;#160; &amp;#160; rightImageView.frame = CGRectMake(self.bounds.size.width-rightImage.size.width, 0, rightImage.size.width, rightImage.size.height);&lt;br /&gt;&amp;#160; &amp;#160; rightImageView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;&lt;br /&gt;&amp;#160; &amp;#160; UIImageView* centerImageView = [[[UIImageView alloc] initWithImage:centerImage] autorelease];&lt;br /&gt;&amp;#160; &amp;#160; centerImageView.frame = CGRectMake(leftImage.size.width, 0, self.bounds.size.width - leftImage.size.width - rightImage.size.width, self.bounds.size.height);&lt;br /&gt;&amp;#160; &amp;#160; centerImageView.autoresizingMask = UIViewAutoresizingFlexibleWidth;&lt;br /&gt;&amp;#160; &amp;#160; [self addSubview:centerImageView];&lt;br /&gt;&amp;#160; &amp;#160; [self addSubview:leftImageView];&lt;br /&gt;&amp;#160; &amp;#160; [self addSubview:rightImageView];&lt;br /&gt;}&lt;br /&gt;Этот метод следуюет вызывать, когда поле создается:&lt;/p&gt;
						&lt;p&gt;// если создается из Interface Builder&lt;br /&gt;-(id) initWithCoder:(NSCoder *)aDecoder&lt;br /&gt;{&lt;br /&gt;&amp;#160; &amp;#160; self = [super initWithCoder:aDecoder];&lt;br /&gt;&amp;#160; &amp;#160; [self configureTextField];&lt;br /&gt;&amp;#160; &amp;#160; return self;&lt;br /&gt;}&lt;/p&gt;
						&lt;p&gt;// если создается из кода&lt;br /&gt;-(id) initWithFrame:(CGRect)frame&lt;br /&gt;{&lt;br /&gt;&amp;#160; &amp;#160; self = [super initWithFrame:frame];&lt;br /&gt;&amp;#160; &amp;#160; [self configureTextField];&lt;br /&gt;&amp;#160; &amp;#160; return self;&lt;br /&gt;}&lt;br /&gt;Результат анимации получился таким как и требовалось.&lt;/p&gt;
						&lt;p&gt;Уменьшаем границы области ввода&lt;/p&gt;
						&lt;p&gt;Осталась одна проблема — сделать так, чтобы текст не перекрывал закругленную границу. У UITextField существует набор методов, которые возвращают границы для рисования конкретных частей — области ввода, кнопки «Clear» и т.д. Эти методы не стоит вызывать напрямую, но всегда можно переопределить. Вот они:&lt;/p&gt;
						&lt;p&gt;- (CGRect)borderRectForBounds:(CGRect)bounds;&lt;br /&gt;- (CGRect)textRectForBounds:(CGRect)bounds;&lt;br /&gt;- (CGRect)placeholderRectForBounds:(CGRect)bounds;&lt;br /&gt;- (CGRect)editingRectForBounds:(CGRect)bounds;&lt;br /&gt;- (CGRect)clearButtonRectForBounds:(CGRect)bounds;&lt;br /&gt;- (CGRect)leftViewRectForBounds:(CGRect)bounds;&lt;br /&gt;- (CGRect)rightViewRectForBounds:(CGRect)bounds;&lt;br /&gt;Нужно перекрыть нужные методы и возвращать уменьшенную область. Я определил метод, который из оригинальной области делает ту, которая не перекрывает закругленные края:&lt;/p&gt;
						&lt;p&gt;- (CGRect) adjustBoundsFromBounds:(CGRect)bounds&lt;br /&gt;{&lt;br /&gt;&amp;#160; &amp;#160; return CGRectInset(bounds, 10.0, 0);&lt;br /&gt;}&lt;br /&gt;А далее просто перекрыл нужные методы:&lt;/p&gt;
						&lt;p&gt;- (CGRect)borderRectForBounds:(CGRect)bounds&lt;br /&gt;{&lt;br /&gt;&amp;#160; &amp;#160; return [super borderRectForBounds:[self adjustBoundsFromBounds:bounds]];&lt;br /&gt;}&lt;br /&gt;- (CGRect)textRectForBounds:(CGRect)bounds&lt;br /&gt;{&lt;br /&gt;&amp;#160; &amp;#160; return [super textRectForBounds:[self adjustBoundsFromBounds:bounds]];&lt;br /&gt;}&lt;br /&gt;- (CGRect)editingRectForBounds:(CGRect)bounds&lt;br /&gt;{&lt;br /&gt;&amp;#160; &amp;#160; return [super editingRectForBounds:[self adjustBoundsFromBounds:bounds]];&lt;br /&gt;}&lt;br /&gt;- (CGRect)leftViewRectForBounds:(CGRect)bounds&lt;br /&gt;{&lt;br /&gt;&amp;#160; &amp;#160; return [super leftViewRectForBounds:[self adjustBoundsFromBounds:bounds]];&lt;br /&gt;}&lt;br /&gt;- (CGRect)rightViewRectForBounds:(CGRect)bounds&lt;br /&gt;{&lt;br /&gt;&amp;#160; &amp;#160; return [super rightViewRectForBounds:[self adjustBoundsFromBounds:bounds]];&lt;br /&gt;}&lt;br /&gt;Как видите, в каждом методе я вызываю метод родительского класса, чтобы он правильно посчитал область. Но область я передаю уже уменьшенную.&lt;/p&gt;
						&lt;p&gt;Получилось вот что (и кнопка «Clear» на месте!):&lt;br /&gt;&lt;/p&gt;
						&lt;p&gt;Велосипед&lt;/p&gt;
						&lt;p&gt;Как оказалось, я изобрел очередной велосипед с рисованием картинок. Подсказал в Twitter @talissman и @dlebedev в комментариях.&lt;/p&gt;
						&lt;p&gt;Можно использовать свойство background у UITextField. Для этого я подготовил новую картинку:&lt;br /&gt;&lt;/p&gt;
						&lt;p&gt;Изменился метод configureTextfield:&lt;/p&gt;
						&lt;p&gt;-(void) configureTextField&lt;br /&gt;{&lt;br /&gt;&amp;#160; &amp;#160; UIImage* background = [[UIImage imageNamed:@&amp;quot;bg.png&amp;quot;] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 15, 0, 15)];&lt;/p&gt;
						&lt;p&gt;&amp;#160; &amp;#160; [self setBackground:background];&lt;br /&gt;}&lt;br /&gt;Обновленный проект с исходным кодом можно загрузить по этой ссылке.&lt;/p&gt;</description>
			<author>mybb@mybb.ru (Gates)</author>
			<pubDate>Wed, 14 Aug 2013 12:09:25 +0400</pubDate>
			<guid>http://programir.mybb.ru/viewtopic.php?pid=48#p48</guid>
		</item>
		<item>
			<title>Опять показываем PDF</title>
			<link>http://programir.mybb.ru/viewtopic.php?pid=47#p47</link>
			<description>&lt;p&gt;Не секрет, что PDF документ в приложении можно показать в UIWebView. Но, что делать, если вас не устраивает то, как UIWebView отображает документ? Может быть вы хотите сделать свой механизм перехода по страницам?&lt;/p&gt;
						&lt;p&gt;В iOS PDF можно отобразить через CoreGraphics. При использовании этого подхода каждая страница рисуется отдельно. Т.е. если нужно сделать переход по страницам в документе — нужно будет все делать вручную.&lt;/p&gt;
						&lt;p&gt;Итак, предположим, что у нас в Bundle приложения лежит PDF-файл idevby.pdf. Как его отобразить? Создадим класс PDFView, который будет это отображать:&lt;/p&gt;
						&lt;p&gt;@interface PDFView : UIView&lt;br /&gt;@end&lt;br /&gt;В классе перекроем метод drawRect::&lt;/p&gt;
						&lt;p&gt;- (void)drawRect:(CGRect)rect&lt;br /&gt;{&lt;br /&gt;&amp;#160; &amp;#160; // путь до документа&lt;br /&gt;&amp;#160; &amp;#160; CFURLRef url = (CFURLRef)[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@&amp;quot;idevby&amp;quot; ofType:@&amp;quot;pdf&amp;quot;]];&lt;/p&gt;
						&lt;p&gt;&amp;#160; &amp;#160; // открываем документ&lt;br /&gt;&amp;#160; &amp;#160; CGPDFDocumentRef pdfDocument = CGPDFDocumentCreateWithURL(url);&lt;br /&gt;&amp;#160; &amp;#160; // если в документе нет страниц - ничего отображать не нужно&lt;br /&gt;&amp;#160; &amp;#160; if ( CGPDFDocumentGetNumberOfPages(pdfDocument) &amp;gt; 0 )&lt;br /&gt;&amp;#160; &amp;#160; {&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; // получим первую страницу (страницы нумеруются с первой)&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; CGPDFPageRef pdfPage = CGPDFDocumentGetPage(pdfDocument, 1);&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; // получим необходимое преобразование&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; CGAffineTransform t = CGPDFPageGetDrawingTransform(pdfPage, kCGPDFCropBox, self.bounds, 0, 1);&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; CGContextRef context = UIGraphicsGetCurrentContext();&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; CGContextSaveGState(context);&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; // применим преобразование&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; CGContextTranslateCTM(context, 0, self.bounds.size.height);&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; CGContextScaleCTM(context, 1, -1);&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; CGContextConcatCTM(context, t);&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; // отрисуем страницу&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; CGContextDrawPDFPage(context, pdfPage);&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; CGContextRestoreGState(context);&lt;br /&gt;&amp;#160; &amp;#160; }&lt;br /&gt;&amp;#160; &amp;#160; // закроем документ&lt;br /&gt;&amp;#160; &amp;#160; CGPDFDocumentRelease(pdfDocument);&lt;br /&gt;}&lt;br /&gt;Рассмотрим основные моменты.&lt;/p&gt;
						&lt;p&gt;При отрисовке PDF-страницы мы сохраняем состояние контекста, а затем восстанавливаем его:&lt;br /&gt;CGContextSaveGState(context);&lt;br /&gt;...&lt;br /&gt;CGContextRestoreGState(context);&lt;br /&gt;Этого можно было бы и не делать, но если вы захотите рисовать что-то после отрисовки PDF-страницы — это необходимо, иначе то, что вы нарисуете может быть искривлено.&lt;/p&gt;
						&lt;p&gt;Нам необходимо отразить контекст по вертикали, иначе документ будет показан «вверх ногами». Это связано с тем, что в данном случае ось Y направлена снизу вверх.&lt;/p&gt;
						&lt;p&gt;CGContextTranslateCTM(context, 0, self.bounds.size.height);&lt;br /&gt;CGContextScaleCTM(context, 1, -1);&lt;br /&gt;Ну и самое интересное:&lt;/p&gt;
						&lt;p&gt;CGAffineTransform t = CGPDFPageGetDrawingTransform(pdfPage, kCGPDFCropBox, self.bounds, 0, 1);&lt;br /&gt;...&lt;br /&gt;CGContextConcatCTM(context, t);&lt;/p&gt;
						&lt;p&gt;CGContextDrawPDFPage(context, pdfPage);&lt;br /&gt;Функция CGPDFPageGetDrawingTransform возвращает трансформацию для контекста, которую нужно применить, чтобы целиком отобразить заданную область страницы в заданной области view. Трансформацию применяет функция CGContextConcatCTM. Первым аргументом функции CGPDFPageGetDrawingTransform является страница, которую хотим отобразить. Второй аргумент — область документа для отображения. Области определены так:&lt;/p&gt;
						&lt;p&gt;enum CGPDFBox {&lt;br /&gt;kCGPDFMediaBox = 0,&lt;br /&gt;kCGPDFCropBox = 1,&lt;br /&gt;kCGPDFBleedBox = 2,&lt;br /&gt;kCGPDFTrimBox = 3,&lt;br /&gt;kCGPDFArtBox = 4&lt;br /&gt;};&lt;br /&gt;Третий аргумент — область на view, где будет отображена страница, в данном случае мы используем всю область. Четвертый аргумент — угол вращения в градусах, должен быть обязательно кратным 90. Пятый аргумент — сохранять ли пропорции сторон при отрисовке.&lt;/p&gt;
						&lt;p&gt;Конечно-же, этот способ более трудоемкий, чем способ с использованием UIWebView. Но, используя такой подход, мы можем получить больше возможностей для кастомизации отображения PDF. Есть и минус данного подхода — если в документе есть ссылки — они не будут активными и необходимо будет применять дополнительные средства.&lt;/p&gt;
						&lt;p&gt;На этом все, спасибо за внимание. Загрузить исходный код можно отсюда.&lt;/p&gt;</description>
			<author>mybb@mybb.ru (Gates)</author>
			<pubDate>Wed, 14 Aug 2013 12:08:51 +0400</pubDate>
			<guid>http://programir.mybb.ru/viewtopic.php?pid=47#p47</guid>
		</item>
		<item>
			<title>Выводим список шрифтов доступных в iOS</title>
			<link>http://programir.mybb.ru/viewtopic.php?pid=46#p46</link>
			<description>&lt;p&gt;Как-то на форуме был вопрос о шрифтах в iOS. Там и родился этот пример. Перед нами простая задача: вывести все шрифты, которые доступны в iOS. Получать список шрифтов и их названия мы будем через класс UIFont. Класс имеет два статических метода для получения семейства шрифтов и названия шрифтов в каждом семействе.&lt;/p&gt;
						&lt;p&gt;Чтобы вывести список — достаточно создать UITableView, а методы UITableViewDataSource реализовать так:&lt;/p&gt;
						&lt;p&gt;-(NSString *) tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section&lt;br /&gt;{&lt;br /&gt;&amp;#160; &amp;#160; return [[UIFont familyNames] objectAtIndex:section];&lt;br /&gt;}&lt;/p&gt;
						&lt;p&gt;-(NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section&lt;br /&gt;{&lt;br /&gt;&amp;#160; &amp;#160; return [[UIFont fontNamesForFamilyName:[[UIFont familyNames] objectAtIndex:section]] count];&lt;br /&gt;}&lt;br /&gt;-(NSInteger) numberOfSectionsInTableView:(UITableView *)tableView&lt;br /&gt;{&lt;br /&gt;&amp;#160; &amp;#160; return [[UIFont familyNames] count];&lt;br /&gt;}&lt;br /&gt;-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath&lt;br /&gt;{&lt;br /&gt;&amp;#160; &amp;#160; static NSString* cellID = @&amp;quot;cellID&amp;quot;;&lt;br /&gt;&amp;#160; &amp;#160; UITableViewCell* cell;&lt;br /&gt;&amp;#160; &amp;#160; cell = [tableView dequeueReusableCellWithIdentifier:cellID];&lt;br /&gt;&amp;#160; &amp;#160; if ( !cell )&lt;br /&gt;&amp;#160; &amp;#160; {&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID] autorelease];&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; cell.textLabel.text = @&amp;quot;Привет, Мир!&amp;quot;;&lt;br /&gt;&amp;#160; &amp;#160; }&lt;br /&gt;&amp;#160; &amp;#160; NSString* fontName = [[UIFont fontNamesForFamilyName:[[UIFont familyNames] objectAtIndex:indexPath.section]] objectAtIndex:indexPath.row];&lt;br /&gt;&amp;#160; &amp;#160; cell.textLabel.font = [UIFont fontWithName:fontName size:25.0];&lt;br /&gt;&amp;#160; &amp;#160; return cell;&lt;br /&gt;}&lt;/p&gt;
						&lt;p&gt;С помощью статического метода familyNames в классе UIFont мы получаем названия всех семейств шрифтов, которые доступны в системе. А с помощью статического метода fontNamesForFamilyName: можно получить названия всех шрифтов в семействе.&lt;/p&gt;</description>
			<author>mybb@mybb.ru (Gates)</author>
			<pubDate>Wed, 14 Aug 2013 12:08:22 +0400</pubDate>
			<guid>http://programir.mybb.ru/viewtopic.php?pid=46#p46</guid>
		</item>
		<item>
			<title>Как установить IPA без iTunes</title>
			<link>http://programir.mybb.ru/viewtopic.php?pid=45#p45</link>
			<description>&lt;p&gt;Известно, что IPA можно установить на iOS устройство с помощью iTunes. Но есть особенность, что один девайс всегда нужно синхронизировать с одним iTunes. А что делать, если нужно установить IPA, но устройство не синхронизировано с вашим iTunes? Конечно же, в этом случае можно использовать iTunes, но все содержимое устройства будет «испорчено».&lt;/p&gt;
						&lt;p&gt;На помощь приходит iPhone Configuration Utility. Она существует как для Mac, так и для Windows. Подключите устройство и запустите утилиту.&lt;/p&gt;
						&lt;p&gt;Важно, чтобы UDID вашего устройства был добавлен в профайл, которым подписывали IPA. Откройте раздел «Applications» в iPhone Configuration Utility и нажмите «Add», выберите ваш IPA файл.&lt;br /&gt;&lt;/p&gt;
						&lt;p&gt;Затем, выберите ваше устройство в списке слева. У меня это «iLime». Откройте вкладку «Provisioning Profiles» и найдите в списке профайл, которым подписано IPA, нажмите «Install». Этого делать не нужно, если профайл уже стоит на устройстве. Если вы незнаете название профайла — его легко найти т.к. скорее всего около одного него будет кнопка «Install».&lt;/p&gt;
						&lt;p&gt;После этого откройте вкладку «Applications», найдите нужное приложение и нажмите «Install». Если вы все сделали правильно — на вашем устройстве появится новое приложение.&lt;/p&gt;</description>
			<author>mybb@mybb.ru (Gates)</author>
			<pubDate>Wed, 14 Aug 2013 12:06:23 +0400</pubDate>
			<guid>http://programir.mybb.ru/viewtopic.php?pid=45#p45</guid>
		</item>
		<item>
			<title>Добавляем свойства к уже существующему классу</title>
			<link>http://programir.mybb.ru/viewtopic.php?pid=44#p44</link>
			<description>&lt;p&gt;Добрый день. Не так давно передо мной встала задача добавить свойство к уже существующему классу(у меня это был UIButton) начал искать, и на странице эплов нашел то, что мне было нужно. А теперь подробнее, что же именно я там нашел.&lt;br /&gt;В Objective-C есть такая замечательная вещь как рантайм он позволяет творить чудеса, если знать как этим пользоваться. Эту возможность я тут и буду использовать.&lt;br /&gt;Сначала необходимо добавить в проект файл категории для требуемого объекта, в итоге если категория называется AddProperty, а целевой класс UIButton, то название файла будет таким: UIButton+AddProperty.&lt;/p&gt;
						&lt;p&gt;Далее, в файл UIButton+AddProperty.h пишем следующее:&lt;/p&gt;
						&lt;p&gt;@interface UIButton (AddProperty)&lt;br /&gt;@property(retain,nonatomic) NSString* someText; // добавляемое свойство&lt;br /&gt;@end&lt;br /&gt;Тут все понятно, теперь нужно синтезировать это свойство, обычным способом этого не сделать. Переходим в файл UIButton+AddProperty.m, подключаем заголовочный файл #import . Среди все прелестей доступных нам после подключения этого файла, в данный момент нас интересуют следующие две функции:&lt;br /&gt;void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)&lt;br /&gt;id objc_getAssociatedObject(id object, const void *key)&lt;/p&gt;
						&lt;p&gt;в целом, я думаю понятно что они делают.&lt;br /&gt;Сейчас приведу рабочий код с этими функциями, а затем разберем все:&lt;/p&gt;
						&lt;p&gt;#import &amp;quot;UIButton+AddProperty.h&amp;quot;&lt;br /&gt;#import &lt;br /&gt;static NSString* keyProperty = @&amp;quot;keySomeTextProperty&amp;quot;;&lt;br /&gt;@implementation UIButton (AddProperty)&lt;br /&gt;-(void) setSomeText:(NSString *)someText{&lt;br /&gt;&amp;#160; &amp;#160; objc_setAssociatedObject(self, keyProperty, someText, OBJC_ASSOCIATION_RETAIN_NONATOMIC); // устанавливаем пару ключ-значение&lt;br /&gt;}&lt;br /&gt;-(NSString*) someText{&lt;br /&gt;&amp;#160; &amp;#160; id value = objc_getAssociatedObject(self, keyProperty); // получаем объект по ключу&lt;br /&gt;&amp;#160; &amp;#160; return (NSString*)value;&lt;br /&gt;}&lt;br /&gt;@end&lt;br /&gt;Теперь подробности:&lt;br /&gt;функция void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)&lt;br /&gt;четыре параметра:&lt;br /&gt;object — объект, для которого создается свойство&lt;br /&gt;key — ключ, обеспечивающий уникальность свойства, в рамках данного объекта(понятно, что он долежн быть уникален)&lt;br /&gt;value — значение свойства&lt;br /&gt;policy — политика присовения свойств. На выбор есть несколько вариантов, в зависимости от требуемого эффекта(их можно посмотреть в файле runtime.h):&lt;br /&gt;OBJC_ASSOCIATION_ASSIGN&lt;br /&gt;OBJC_ASSOCIATION_RETAIN_NONATOMIC&lt;br /&gt;OBJC_ASSOCIATION_COPY_NONATOMIC&lt;br /&gt;OBJC_ASSOCIATION_RETAIN&lt;br /&gt;OBJC_ASSOCIATION_COPY&lt;br /&gt;Названия этих констант соответствуют требуемой политики поведения свойств в присвоении объектов.&lt;br /&gt;функция id objc_getAssociatedObject(id object, const void *key)&lt;br /&gt;имеет всего два параметра:&lt;br /&gt;object — объект, у которого мы ищем данное свойство&lt;br /&gt;key — ключ для поиска&lt;br /&gt;Возвращает нам эта функция указатель на объект, сам объект мы потом без проблем приводим к нужному нам типу.&lt;/p&gt;
						&lt;p&gt;Вот в общем-то и все, что необходимо, для того, что бы добавить требуемое свойство в уже существующий класс. Добавление методов к существующему классу тут намеренно не рассматривалось, т.к. этой информации везде хватает, а по поводу добавления свойст ее не особо-то и много, только на stackoverflow нашел что-то внятное, но потом дочитывал на developer.apple.com откуда и была взята основная информация.&lt;/p&gt;</description>
			<author>mybb@mybb.ru (Gates)</author>
			<pubDate>Wed, 14 Aug 2013 12:05:54 +0400</pubDate>
			<guid>http://programir.mybb.ru/viewtopic.php?pid=44#p44</guid>
		</item>
		<item>
			<title>Новое в iOS 6: Публикация в Facebook, Twitter, Sina’s Weibo</title>
			<link>http://programir.mybb.ru/viewtopic.php?pid=43#p43</link>
			<description>&lt;p&gt;В iOS 6 делиться контентом из приложения стало еще проще с помощью нового фреймворка Social.framework. Он обеспечивает доступ к трем социальным сетям: Facebook, Sina’s Weibo (для тех, кто не знает — это китайский микроблог, своего рода гибрид между Facebook и Twitter) и Twitter, чем вытесняет появившийся в iOS 5 Twitter.framework. Эти типы доступны в Social.framework:&lt;/p&gt;
						&lt;p&gt;SLServiceTypeFacebook&lt;br /&gt;SLServiceTypeTwitter&lt;br /&gt;SLServiceTypeSinaWeibo&lt;/p&gt;
						&lt;p&gt;Рассмотрим пример публикации в Facebook. Для этого добавим в проект Social.framework и подключим в заголовочном файле&lt;/p&gt;
						&lt;p&gt; #import &amp;quot;Social /Social.h&amp;quot; и в обработчике, например, кнопки напишем:&lt;br /&gt; if([SLComposeViewController isAvailableForServiceType:SLServiceTypeFacebook])&lt;br /&gt;{&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; SLComposeViewController *controller = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeFacebook];&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; SLComposeViewControllerCompletionHandler myBlock = ^(SLComposeViewControllerResult result)&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; {&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; if (result == SLComposeViewControllerResultCancelled)&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; NSLog(@&amp;quot;Cancelled&amp;quot;);&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; else NSLog(@&amp;quot;Done&amp;quot;);&lt;/p&gt;
						&lt;p&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; [controller dismissViewControllerAnimated:YES completion:Nil];&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; };&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; controller.completionHandler =myBlock;&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; [controller setInitialText:@&amp;quot;Test FB Post From Mobile&amp;quot;];&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; [controller addURL:[NSURL URLWithString:@&amp;quot;http://idev.by&amp;quot;]];&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; [controller addImage:[UIImage imageNamed:@&amp;quot;Test FB Image.png&amp;quot;]];&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; [self presentViewController:controller animated:YES completion:Nil]; &lt;br /&gt;&amp;#160; &amp;#160; } else NSLog(@&amp;quot;UnAvailable&amp;quot;); &lt;br /&gt;Для Twitter и Sina’s Weibo нужно будет только изменить тип SLServiceTypeFacebook на нужный.&lt;/p&gt;</description>
			<author>mybb@mybb.ru (Gates)</author>
			<pubDate>Wed, 14 Aug 2013 12:05:32 +0400</pubDate>
			<guid>http://programir.mybb.ru/viewtopic.php?pid=43#p43</guid>
		</item>
		<item>
			<title>Собираем и запускаем Redis на iOS</title>
			<link>http://programir.mybb.ru/viewtopic.php?pid=42#p42</link>
			<description>&lt;p&gt;Как мы знаем, Redis — документо-ориентированное сетевое журналируемое хранилище данных типа «ключ-значение» с открытым исходным кодом (так гласит источник). Все нижеследующее сделано «just for fun». Я не могу предложить применения Redis на iOS. Если у вас другая точка зрения — поделитесь в комментариях.&lt;/p&gt;
						&lt;p&gt;Подготовка&lt;/p&gt;
						&lt;p&gt;Идем на сайт Redis и ищем кнопку «Download». Загружаем исходники последней стабильной версии, в данный момент это 2.4.17. В архиве много всяких файлов, но нас интересует папка src. Как можно видеть из папки deps — у проекта есть некоторые зависимости. Но для сборки redis-server они не нужны. Судя по make-файлу, из исходников мы можем получить сам redis-server, бенчмарки, тесты и Command Line Interface. Осталось создать любой iOS проект в XCode.&lt;/p&gt;
						&lt;p&gt;Первая попытка&lt;/p&gt;
						&lt;p&gt;Просто перетянем папку src в XCode-проект, при этом игнорируем make-файл. Для начала будем собирать для iPhone Simulator. Жмем «Build» или Command-B. XCode выдаст много ошибок — первая (и самая простая) попытка провалилась. Можно, конечно-же, попробовать разобраться почему произошли эти ошибки, но я решил пойти другим путем.&lt;/p&gt;
						&lt;p&gt;Иной подход&lt;/p&gt;
						&lt;p&gt;Смело удаляем все файлы Redis из проекта. Откроем make-файл в папке src и посмотрим, что не все файлы из папки src нужны для сборки redis-server. Найдем вот такую строчку:&lt;br /&gt;OBJ = adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o vm.o pubsub.o multi.o debug.o sort.o intset.o syncio.o slowlog.o bio.o memtest.o&lt;br /&gt;Получается, что нам в проект нужно добавить *.h и *.c файлы с такими именами. Что и предлагаю сделать. После того, как все файлы добавлены в проект, пробуем собрать. Пока ошибка всего одна: не хватает файла ae_kqueue.c. Добавим его в проект. Так же и с fmacros.h, version.h, endian.h. Но как бы мы ни старались, файла release.h мы не найдем. Для этого нужно запустить скрипт mkreleasehdr.sh из папки src, он сгенерирует файл release.h, который мы и добавим в проект.&lt;/p&gt;
						&lt;p&gt;Очередной раз жмем «Build» и видим что ошибки остались. Все они в файле ae_kqueue.h. Решается это добавлением:&lt;br /&gt;#include &amp;quot;ae.h&amp;quot;&lt;br /&gt;#include &amp;quot;zmalloc.h&amp;quot;&lt;br /&gt;#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;Правим код&lt;/p&gt;
						&lt;p&gt;Пробуем снова собрать. Обнаруживаем ошибку, что функция main в проекте у нас существует в двух местах. Оно-то и понятно, ведь redis-server хочет запускаться сам по себе. Открываем файл redis.c и изменяем имя функции main на redisMain, получиться вот так:&lt;/p&gt;
						&lt;p&gt;int redisMain(int argc, char **argv)&lt;br /&gt;И ее объявление добавляем в файл redis.h. Пробуем собрать. Теперь проект должен собираться без ошибок и предупреждений.&lt;/p&gt;
						&lt;p&gt;Теперь нужно сделать, чтобы Redis стартовал при старте приложения. Откроем файл main.m и добавим:&lt;br /&gt;#import &amp;quot;redis.h&amp;quot;&lt;br /&gt;А так же добавим перед вызовом UIApplicationMain:&lt;br /&gt;redisMain(argc, argv);&lt;br /&gt;Запустим проект в симуляторе и увидим черный экран. Оно и не удивительно, ведь Redis однопоточен и он блокирует тот поток, в котором запущен. Поэтому стоит создать для него новый поток и запустить его там. Поэтому сделаем вот так:&lt;/p&gt;
						&lt;p&gt;dispatch_queue_t redisQueue = dispatch_queue_create(&amp;quot;redis.queue&amp;quot;, DISPATCH_QUEUE_CONCURRENT);&lt;br /&gt;dispatch_async(redisQueue, ^{&lt;br /&gt;&amp;#160; &amp;#160; redisMain(argc, argv);&lt;br /&gt;});&lt;br /&gt;Запускаем проект и видим радостные логи:&lt;/p&gt;
						&lt;p&gt;[1709] 06 Oct 16:36:23 # Warning: no config file specified, using the default config. In order to specify a config file use &#039;redis-server /path/to/redis.conf&#039;&lt;br /&gt;[1709] 06 Oct 16:36:23 # Warning: 32 bit instance detected but no memory limit set. Setting 3.5 GB maxmemory limit with &#039;noeviction&#039; policy now.&lt;br /&gt;[1709] 06 Oct 16:36:23 * Server started, Redis version 2.4.17&lt;br /&gt;[1709] 06 Oct 16:36:23 * The server is now ready to accept connections on port 6379&lt;br /&gt;[1709] 06 Oct 16:36:23 - 0 clients connected (0 slaves), 629440 bytes in use&lt;br /&gt;2012-10-06 16:36:23.423 Redis[1709:c07] Application windows are expected to have a root view controller at the end of application launch&lt;br /&gt;[1709] 06 Oct 16:36:28 - 0 clients connected (0 slaves), 629440 bytes in use&lt;br /&gt;Еще немного изменений&lt;/p&gt;
						&lt;p&gt;А теперь пробуем запустить на девайсе. Но встречаем такую ошибку:&lt;br /&gt;redis.c:1835:42: No member named &#039;__eip&#039; in &#039;struct __darwin_arm_thread_state&#039;&lt;br /&gt;Найдем место, где возникает эта ошибка, это строчка&lt;br /&gt;return (void*) uc-&amp;gt;uc_mcontext-&amp;gt;__ss.__eip; в функции&lt;br /&gt;static void *getMcontextEip(ucontext_t *uc)&lt;br /&gt;По-видимому это какая-то платформозавимая инструкция. Я не специалист в архитектуре ARM, поэтому не нашел ничего лучшего чем заменить эту строчку на:&lt;br /&gt;return NULL;&lt;br /&gt;Я считаю, ничего плохого не случилось, т.к. функция getMcontextEip используется для вывода стека вызова при возникновении ошибки.&lt;br /&gt;Пробуем еще раз запустить на девайсе — приложение должно заработать.&lt;/p&gt;
						&lt;p&gt;Пробуем&lt;/p&gt;
						&lt;p&gt;Для проверки работы Redis на девайсе соберем redis-cli. Зайдите в папку src и наберите комманду:&lt;br /&gt;make redis-cli&lt;br /&gt;Затем:&lt;br /&gt;./redis-cli IP-адрес-телефона&lt;br /&gt;Нужно чтобы телефон и компьютер находился в одной сети.&lt;br /&gt;Если вы все сделали правильно, в логе приложения вы увидите что-то вроде:&lt;br /&gt;[3631] 06 Oct 16:49:22 - Accepted 192.168.1.2:55165&lt;br /&gt;[3631] 06 Oct 16:49:26 - 1 clients connected (0 slaves), 637328 bytes in use&lt;br /&gt;Итоги&lt;/p&gt;
						&lt;p&gt;Как мы знаем, Redis хранит все данные в оперативной памяти, поэтому рассчитывать на постоянное хранение данных не приходиться. Конечно же, Redis умеет сбрасывать данные в файл, а затем при старте его считывать. Но все равно это не может является супер-альтернативой хранения данных в iOS. Для этого стоит использовать CoreData. Если очень хочется хранить данные в оперативной памяти — CoreData это умеет тоже.&lt;/p&gt;
						&lt;p&gt;Если вам стало интересно — можем продолжить…&lt;/p&gt;
						&lt;p&gt;Проект можно загрузить отсюда.&lt;/p&gt;</description>
			<author>mybb@mybb.ru (Gates)</author>
			<pubDate>Wed, 14 Aug 2013 12:05:05 +0400</pubDate>
			<guid>http://programir.mybb.ru/viewtopic.php?pid=42#p42</guid>
		</item>
		<item>
			<title>iOS 6. MapKit</title>
			<link>http://programir.mybb.ru/viewtopic.php?pid=41#p41</link>
			<description>&lt;p&gt;В iOS был существенно переработан MapKit. Разработчики Apple отказались от Google maps, и создали свой картографический сервис. Карты стали dynamic(© apple), это позволило: красиво переворачивать ярлыки, избежать размытия при масштабировании и пр. API работы с MapKit не изменились, но появились дополнения.&lt;/p&gt;
						&lt;p&gt;Встречайте MKMapItem (описание здесь).&lt;/p&gt;
						&lt;p&gt;У MKMapItem есть свойства name, phoneNumber, url дают дополнительную информацию о месте на которое указывает экземпляр класса.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
						&lt;p&gt;Свойство placemark хранит координаты. Свойство isCurrentLocation указывает на текущее положение девайса (Важно: как только isCurrentLocation устанавливается в YES, placemark устанавливается в nil).&lt;/p&gt;
						&lt;p&gt;MKPlacemark *myPlacemark = [[MKPlacemark alloc] initWithCoordinate:CLLocationCoordinate2DMake(53.90, 27.56) addressDictionary:nil];&lt;br /&gt;MKMapItem *myPoint = [[MKMapItem alloc] initWithPlacemark:myPlacemark];&lt;br /&gt;Чтобы открыть приложение карты с выбранной точкой, вызываем instance-метод openInMapsWithLaunchOptions:&lt;/p&gt;
						&lt;p&gt;[myPoint openInMapsWithLaunchOptions:nil];&lt;/p&gt;
						&lt;p&gt;Если необходимо запустить приложение карты с несколькими «булавками», собираем все MKMapItem в NSArray и вызываем class-метод openInMapsItems: launchOptions:&lt;/p&gt;
						&lt;p&gt;NSArray *mapItems = @[point1, point2, point3];&lt;br /&gt;[MKMapItem openMapsWithItems:mapItems launchOptions:nil];&lt;/p&gt;
						&lt;p&gt;В вышеописанные методы передается параметр NSDictionary *launchOptions. В этом параметре можно описать: тип отображаемой карты (схема, спутник, гибрид), центр отображаемого участка карты, отображаемый район, отображать ли траффик.&lt;/p&gt;
						&lt;p&gt;NSDictionary *options = @{&lt;br /&gt;&amp;#160; &amp;#160; MKLaunchOptionsMapTypeKey :[NSNumber numberWithInteger:MKMapTypeHybrid],&lt;br /&gt;&amp;#160; &amp;#160; MKLaunchOptionsMapCenterKey :[NSValue valueWithMKCoordinate:CLLocationCoordinate2DMake(53.90, 27.56)],&lt;br /&gt;&amp;#160; &amp;#160; MKLaunchOptionsMapSpanKey :[NSValue valueWithMKCoordinateSpan:MKCoordinateSpanMake(1, 1)]};&lt;br /&gt;Еще одна опция которую стоит рассмотреть отдельно – MKLaunchOptionsDirectionsModeKey. Передав этот ключ приложению карты, оно создает маршрут (доступно 2 режима: walking/driving). В качестве начальной точки будет взят первый объект из массива, а конечная – последний объект, вне зависимости от количества объектов в массиве.&lt;/p&gt;
						&lt;p&gt;MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeDriving&lt;/p&gt;
						&lt;p&gt;Отобразить текущее положение девайса на карте не сложно:&lt;/p&gt;
						&lt;p&gt;MKMapItem *currentPoint = [MKMapItem mapItemForCurrentLocation];&lt;br /&gt;[currentPoint openInMapsWithLaunchOptions:nil];&lt;br /&gt;Работать с этим экземпляром так же, как и с обычными MKMapItem, за исключением свойства placemark — оно имеет значение nil, свойство isCurrentLocation будет иметь значение YES.&lt;/p&gt;
						&lt;p&gt;Самое интересное: теперь приложения можно запускать прямо из стандартного приложения карты.&lt;br /&gt;&lt;/p&gt;
						&lt;p&gt;Нажимаем кнопку «навигации», вводим начальную и конечную точку маршрута.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
						&lt;p&gt;Выбираем тип транспорта и, по нажатию на кнопку «Route», переходим к выбору приложения.&lt;br /&gt;&lt;/p&gt;
						&lt;p&gt;В верхней части таблицы отображены установленные приложения, ниже – приложения из App Store, которые ассоциированы с выбранным районом и транспортом.&lt;br /&gt;По нажатию на «Route» открываем приложение.&lt;/p&gt;
						&lt;p&gt;Для создания «routing app» необходимо: задекларировать приложение как routing, указать для какого района ваше приложение, и «ловить» запуск из стандартного приложения карт.&lt;/p&gt;
						&lt;p&gt;Открываем в xCode (версия 4.5 и выше) файл проекта, выбираем target, настраиваем checkboxes: “accept transit routing request” – разрешаем приложению запускаться из стандартных карт; выбираем те виды транспорта, для которых написано приложение:&lt;br /&gt;&lt;/p&gt;
						&lt;p&gt;Приложение задекларировано как routing app. Займемся определением района для нашего приложения.&lt;br /&gt;&lt;/p&gt;
						&lt;p&gt;Требования к файлу, описывающему район:&lt;br /&gt;•	GeoJSON формат (.geojson подробнее);&lt;br /&gt;•	Описывайте один мультиполигон (может содержать несколько полигонов как на третьем рисунке);&lt;br /&gt;•	Сделайте ваши полигоны максимально простыми (до 20 полигонов по 20 точек на каждый);&lt;br /&gt;•	Файл не является частью проекта и загружается отдельно через iTunesConnect.&lt;/p&gt;
						&lt;p&gt;И последний шаг, перехватываем запуск приложения. Для этого в AppDelegate реализовываем метод application: openURL: sourceApplication: annotation:&lt;/p&gt;
						&lt;p&gt;- (BOOL)application:(UIApplication *)application&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; openURL:(NSURL *)url&lt;br /&gt;&amp;#160; sourceApplication:(NSString *)sourceApplication&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;annotation:(id)annotation&lt;br /&gt;{&lt;br /&gt;&amp;#160; &amp;#160; if ([MKDirectionsRequest isDirectionsRequestURL:url]) {&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; MKDirectionsRequest *request = [[MKDirectionsRequest alloc]&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; initWithContentsOfURL:url];&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; MKMapItem *startItem = [request source];&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; MKMapItem *endItem = [request destination];&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; // YOUR CODE HERE&lt;br /&gt;	}&lt;br /&gt;&amp;#160; &amp;#160; return YES;&lt;br /&gt;}&lt;br /&gt;Что делать далее с полученными MKMapItem? Это зависит от вас, но не забывайте проверить свойство isCurrentLocation == YES (placemark в этом случае будет nil).&lt;/p&gt;
						&lt;p&gt;Ну и на закуску пример.&lt;/p&gt;</description>
			<author>mybb@mybb.ru (Gates)</author>
			<pubDate>Wed, 14 Aug 2013 12:04:30 +0400</pubDate>
			<guid>http://programir.mybb.ru/viewtopic.php?pid=41#p41</guid>
		</item>
		<item>
			<title>Как вводить в UITextField только цифры?</title>
			<link>http://programir.mybb.ru/viewtopic.php?pid=40#p40</link>
			<description>&lt;p&gt;Очень простой способ вводить в UITextField только цифры:&lt;/p&gt;
						&lt;p&gt;-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{&lt;br /&gt;&amp;#160; &amp;#160; if(![string intValue] &amp;amp;&amp;amp; ![string isEqualToString:@&amp;quot;&amp;quot;] &amp;amp;&amp;amp; ![string isEqualToString:@&amp;quot;0&amp;quot;]) { return NO; } else { return YES; }&lt;br /&gt;}&lt;br /&gt;У меня в проекте возникла необходимость вводить только цифры и если на iPhone это решается установкой UITextField.keyboardType в соответствующее значение, то на iPad у пользователя сохраняется возможность ввода произвольного текста. Описанный выше способ позволяет решить эту проблему в одну строчку.&lt;/p&gt;</description>
			<author>mybb@mybb.ru (Gates)</author>
			<pubDate>Wed, 14 Aug 2013 12:04:02 +0400</pubDate>
			<guid>http://programir.mybb.ru/viewtopic.php?pid=40#p40</guid>
		</item>
		<item>
			<title>Используем Emoji в своих приложениях</title>
			<link>http://programir.mybb.ru/viewtopic.php?pid=39#p39</link>
			<description>&lt;p&gt;Думаю большинство из нас знает, что такое Emoji и все видели клавиатуру в Emoji в iOS. В целом, Emoji — специальные символы, которые отображаются в виде картинок. Вся прелесть в том, что картинки нам не нужны, система сама все обработает. Их можно безболезненно вставлять в UILabel или UITextView и т.д.&lt;/p&gt;
						&lt;p&gt;Каждый Emoji-символ имеет определенный код в Unicode. Например, левый верхний Emoji на картинке имеет код 0x1F604. Чтобы отобразить такой символ в своем приложении необходимо создать строку с кодом такого символа. Такой символ можно задать строкой:&lt;br /&gt;@&amp;quot;\U0001F604&amp;quot;;&lt;br /&gt;Но данная строка нам не дает представления о том, что это за символ и что он означает. Для удобства я создал несколько классов для доступа к Emoji.&lt;/p&gt;
						&lt;p&gt;Как использовать?&lt;/p&gt;
						&lt;p&gt;Я выделил четыре категории значков и для каждой категории создал отдельный класс. Категории — Emoticons, Transport, MapSymbols, Pictographs. Все методы в классе — статические. Каждый метод (за исключением одного) называется так же как и Emoji-символ, который метод возвращает. Вернемся к символу с кодом 0x1F604. Его название — «Smiling Face With Open Mouth And Smiling Eyes». Чтобы получить строку, которая содержит этот символ, нужно сделать так:&lt;/p&gt;
						&lt;p&gt;[EmojiEmoticons smilingFaceWithOpenMouthAndSmilingEyes]&lt;br /&gt;Чтобы отобразить значок в своем приложении можно назначить такую строку любому компоненту, который может отображать текст. Например, UITextView:&lt;/p&gt;
						&lt;p&gt;UITextView* textView = [[UITextView alloc] initWithFrame:self.view.bounds];&lt;br /&gt;textView.text = [EmojiEmoticons smilingFaceWithOpenMouthAndSmilingEyes];&lt;br /&gt;С помощью этих классов вы сможете получить доступ к 679 Emoji. Но, к сожадению некоторые не поддерживаються на iOS. Чтобы получить все Emoji, нужно вызвать метод allEmoji у класса Emoji:&lt;br /&gt;[Emoji allEmoji]&lt;br /&gt;Так же, в каждом классе есть метод начинающийся на all и который возвращает все символы из данной категории.&lt;br /&gt;[EmojiEmoticons allEmoticons];&lt;br /&gt;[EmojiMapSymbols allMapSymbols];&lt;br /&gt;[EmojiPictographs allPictographs];&lt;br /&gt;[EmojiTransport allTransport];&lt;br /&gt;Исходный код классов вы можете найти в моем репозитории на GitHub или в репозитории idev.by.&lt;/p&gt;</description>
			<author>mybb@mybb.ru (Gates)</author>
			<pubDate>Wed, 14 Aug 2013 12:03:26 +0400</pubDate>
			<guid>http://programir.mybb.ru/viewtopic.php?pid=39#p39</guid>
		</item>
		<item>
			<title>Audio Unit в iOS. Часть 1, введение.</title>
			<link>http://programir.mybb.ru/viewtopic.php?pid=38#p38</link>
			<description>&lt;p&gt;Очень хотелось написать несколько статей о AudioUnit в iOS. Я уж было написал большую часть о том, как можно применять эффекты к звуку с помощью Audio Unit (далее я буду писать просто юнит). Но в процессе понял, что непосвященному читателю будет тяжело понять написанное и поэтому решил начать с малого.&lt;/p&gt;
						&lt;p&gt;Для начала разберемся, что такое Audio Unit. Как гласит Wikipedia, AudioUnit — этой некий плагин в OS X (iOS) для генерации, обработки либо другого управления звуком с минимальной задержкой. В коде юнит выступает указателем на struct.&lt;/p&gt;
						&lt;p&gt;Каждый юнит выполняет свою конкретную задачу, они делятся на типы и подтипы. Юниты можно использовать по одиночке, либо связывать их в граф. Юниты, связанные графом обрабатывают звук в определенной последовательности, звук от одного юнита переходит к другому. Например, при помощи графа достаточно легко сделать приложение-караоке, в котором будет воспроизводиться музыка и в тот же момент будет идти запись звука с микрофона в файл.&lt;/p&gt;
						&lt;p&gt;Набор юнитов в iOS достаточно ограничен по сравнению с OS X, но с каждой версией iOS их становиться все больше. Все написанное здесь нужно воспринимать в контексте iOS 6, т.к. на сегодняшний день она обладает наибольшим количеством юнитов среди всех версий iOS.&lt;/p&gt;
						&lt;p&gt;Как мы видим AudioUnit&lt;/p&gt;
						&lt;p&gt;Как я и говорил, в коде юнит выступает указателем на структуру. Например вот так:&lt;/p&gt;
						&lt;p&gt;AudioUnit unit;&lt;br /&gt;На самом деле на указатель это не похоже, но где-то в недрах написано:&lt;/p&gt;
						&lt;p&gt;typedef struct OpaqueAudioComponentInstance *&amp;#160; &amp;#160;AudioComponentInstance;&lt;br /&gt;typedef AudioComponentInstance AudioUnit;&lt;br /&gt;Для того, чтобы однозначно определить юнит, нам нужно определиться с тремя параметрами: производитель, тип и подтип. Подтип можно считать названием юнита. Производителем в нашем случае всегда будет выступать Apple. Что касается типов, то в iOS они следующие:&lt;/p&gt;
						&lt;p&gt;Output — юниты ввода/вывода звука, запись/воспроизведение&lt;br /&gt;Generator — юниты, которые служат источником звука для других юнитов&lt;br /&gt;Mixer — юниты для смешивания звука, например, на вход поступает два потока звука, а выходит один смешанный поток звука&lt;br /&gt;Effect — различные звуковые эффекты&lt;br /&gt;FormatConverter — юниты, которые могут изменять формат звука&lt;br /&gt;Так же существуют типы юнитов, которые работают с MIDI устройствами, но я их рассматривать не буду. В этот список не вошли типы юнитов, которые присутствуют на OS X, но не присутствуют на iOS. Подтипы юнитов разные для каждой категории. В следующих статьях мы будем рассматривать отдельно некоторые подтипы юнитов.&lt;/p&gt;
						&lt;p&gt;Итак, как же создать юнит? Как говорилось выше, нам нужно определиться с тремя параметрами. Эти параметры необходимо записать в структуру AudioComponentDescription. Затем необходимо найти нужный компонент и с помощью него создать юнит. Выглядит это вот так:&lt;/p&gt;
						&lt;p&gt;AudioUnit unit; // юнит&lt;/p&gt;
						&lt;p&gt;AudioComponentDescription acd; // описание компонента&lt;br /&gt;memset(&amp;amp;acd, 0, sizeof(AudioComponentDescription));&lt;br /&gt;acd.componentManufacturer = kAudioUnitManufacturer_Apple;&lt;br /&gt;acd.componentType = kAudioUnitType_Effect;&lt;br /&gt;acd.componentSubType = kAudioUnitSubType_Delay;&lt;br /&gt;// находим необходимый компонент&lt;br /&gt;AudioComponent component = AudioComponentFindNext(NULL, &amp;amp;acd);&lt;br /&gt;// создаем юнит&lt;br /&gt;AudioComponentInstanceNew(component, &amp;amp;unit);&lt;br /&gt;Изменяя тип и подтип компонента, мы можем получать другие юниты. Данный код нам предоставит экземпляр юнита, но такой юнит еще не готов к работе. После получения экземпляра юнита, его необходимо инициализировать. При инициализации юнит захватывает требуемые ему ресурсы и производит необходимые действия для того, чтобы перейти в свое основное состояние. Такое разделение обусловлено тем, что некоторые свойства можно устанавливать только когда юнит находится в не инициализированном состоянии, например, формат потока звука. О свойствах юнитов и форматах звука мы поговорим ниже.&lt;/p&gt;
						&lt;p&gt;Чтобы инициализировать юнит достаточно выполнить:&lt;/p&gt;
						&lt;p&gt;AudioUnitInitialize(unit);&lt;br /&gt;После этого юнит перейдет в свое основное состояние и будет готов обрабатывать поступающий в него звук. После того, как юнит нам больше не нужен, необходимо освободить все ресурсы:&lt;/p&gt;
						&lt;p&gt;AudioUnitUninitialize(unit);&lt;br /&gt;AudioComponentInstanceDispose(unit);&lt;br /&gt;Шины и Scope&lt;/p&gt;
						&lt;p&gt;Юнит имеет входные и выходные шины (bus). Есть юниты не имеющие входных шин, например, юнит, который воспроизводит аудио-файл. Шины нумеруют с 0. Например, юнит RemoteIO, который выводит звук на устройство и вводит звук с микрофона, имеет две шины (0 для воспроизведения, 1 для записи). У каждой шины есть scope, чаще всего используются значения Input (вход звука в шину), Output (выход звука из шины), Global (оба случая).&lt;/p&gt;
						&lt;p&gt;Рассмотрим шину 0 юнита RemoteIO. Через эту шину мы вводим звук в юнит, который необходимо воспроизвести, а юнит через эту шину выводит звук уже на звуковое устройство (наушники, динамики). Когда мы вводим звук в юнит через шину мы используем Input-Scope (вход) шины 0, а когда юнит выводит свой звук используется Output-Scope (выход) шины 0. Такая же история с шиной 1.&lt;br /&gt;&lt;/p&gt;
						&lt;p&gt;Юнит с эффектом Delay имеет одну шину, ее номер — 0. Когда мы отправляем звук на эту шину мы используем Input-Scope (вход) шины 0, звук выходит из юнита через Output-Scope (выход) шины 0.&lt;br /&gt;&lt;/p&gt;
						&lt;p&gt;Обработка ошибок&lt;/p&gt;
						&lt;p&gt;Почти все функции, которые мы вызываем для работы с юнитами (и не только они) возвращают код ошибки (OSStatus), если она случилась. Если ошибки не было, функция возвращает noErr. Если произошла ошибка стоит разобраться почему она возникла, поэтому каждый вызов следует оборачивать в проверку. Для этого я определяю функцию:&lt;/p&gt;
						&lt;p&gt;OSStatus CAError(OSStatus result, const char *file, int line)&lt;br /&gt;{&lt;br /&gt;&amp;#160; &amp;#160; if (result == noErr) return noErr;&lt;br /&gt;&amp;#160; &amp;#160; fprintf(stderr, &amp;quot;Error in %s in %d\n&amp;quot;, file, line);&lt;br /&gt;&amp;#160; &amp;#160; return result;&lt;br /&gt;}&lt;br /&gt;И определяю макрос:&lt;br /&gt;#define CA(x) CAError(x,__FILE__,__LINE__)&lt;br /&gt;Теперь каждый вызов можно оборачивать в макрос CA. Например, вот так:&lt;br /&gt;CA(AudioUnitInitialize(unit));&lt;br /&gt;Если произойдет ошибка, то в консоль будет выведен файл и строка, где произошла ошибка.&lt;br /&gt;Форматы звука&lt;/p&gt;
						&lt;p&gt;Не секрет, что звук может иметь разный формат и необходимо как-то этот формат описывать. Звук бывает сжатый и не сжатый. Юниты работают с не сжатым звуком, поэтому мы так же будем рассматривать не сжатый звук. Формат звука описывается структурой AudioStreamBasicDescription. Выглядит она вот так:&lt;/p&gt;
						&lt;p&gt;struct AudioStreamBasicDescription&lt;br /&gt;{&lt;br /&gt;&amp;#160; &amp;#160; Float64 mSampleRate;&lt;br /&gt;&amp;#160; &amp;#160; UInt32&amp;#160; mFormatID;&lt;br /&gt;&amp;#160; &amp;#160; UInt32&amp;#160; mFormatFlags;&lt;br /&gt;&amp;#160; &amp;#160; UInt32&amp;#160; mBytesPerPacket;&lt;br /&gt;&amp;#160; &amp;#160; UInt32&amp;#160; mFramesPerPacket;&lt;br /&gt;&amp;#160; &amp;#160; UInt32&amp;#160; mBytesPerFrame;&lt;br /&gt;&amp;#160; &amp;#160; UInt32&amp;#160; mChannelsPerFrame;&lt;br /&gt;&amp;#160; &amp;#160; UInt32&amp;#160; mBitsPerChannel;&lt;br /&gt;&amp;#160; &amp;#160; UInt32&amp;#160; mReserved;&lt;br /&gt;};&lt;br /&gt;По большей части, вам не прийдется заполнять поля этой структуры вручную. Назначение некоторых полей понятно из названия, а вот некоторых нет. Далее приведено описание полей структуры, но для ясности стоит определиться что такое packet (пакет), frame (фрейм), sample (отсчет). Мы с вами (и юниты) будем работать с форматом PCM. В таком случае, файл состоит из отсчетов, которые объединяются в фреймы, а фреймы объединяются в пакеты. Количество фреймов в пакете, их размер, порядок отсчетов — все это задается в структуре AudioStreamBasicDescription.&lt;/p&gt;
						&lt;p&gt;mSampleRate — частота дискретизации звука. Показывает количество отсчетов (samples), которое содержит одна секунда звука. Каждый отсчет имеет конкретное количественное значение и представлен конкретным типом (например, Float32 или SInt16).&lt;br /&gt;mFormatID — определяет основной вид данных в потоке звука. Мы будем работать с импульсно-кодовой модуляцией (PCM). Это поле в таком случае равно kAudioFormatLinearPCM&lt;br /&gt;mFormatFlags — флаги, описывающие формат. В основном описывает тип, которым представлен звук (kAudioFormatFlagIsFloat или kAudioFormatFlagIsSignedInteger), порядок байтов (kAudioFormatFlagIsBigEndian), порядок расположения каналов (kAudioFormatFlagIsNonInterleaved). Non-Interleaved означает что каждый фрейм (frame) звука содержит один отсчет канала (sample). Interleaved означает, что фрейм содержит в себе отсчет для каждого канала звука (для моно — один, для стерео — два и т.д.).&lt;br /&gt;mBytesPerPacket — размер пакета в байтах&lt;br /&gt;mFramesPerPacket — количество фреймов в пакете, чаще всего один фрейм на один пакет. В сжатом формате может быть другое количество.&lt;br /&gt;mBytesPerFrame — размер фрейма в байтах. Если звук Non-Interleaved то это будет и размером одного отсчета.&lt;br /&gt;mChannelsPerFrame — количество каналов звука в фрейме. Если звук Non-Interleaved то это поле будет равно еденице т.к. такой звук несет в каждом фрейме один канал.&lt;br /&gt;mBitsPerChannel — бит на канал&lt;br /&gt;mReserved — не используется&lt;br /&gt;Свойства&lt;/p&gt;
						&lt;p&gt;Юниты имеют общий набор свойств и свойства уникальные для каждого юнита. Каждое свойство представлено определенным форматом — это может быть специальная структура или флаг. Свойства могут быть только для чтения, либо для чтения и записи. Некоторые свойства можно устанавливать только в неинициализированном юните.&lt;/p&gt;
						&lt;p&gt;Например, возмем свойство kAudioUnitProperty_StreamFormat. Это — формат звука, который будет входить/выходить из юнита. Свойства устанавливаются либо получаются для конкретной шины и для конкретного Scope. Можно использовать Global-Scope для обоих случаев.&lt;/p&gt;
						&lt;p&gt;Функция установки свойства выглядит вот так:&lt;br /&gt;OSStatus AudioUnitGetProperty(&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;AudioUnit&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; inUnit, // юнит&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;AudioUnitPropertyID	inID, // свойство&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;AudioUnitScope&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;inScope, // scope&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;AudioUnitElement&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;inElement, // шина&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;void *&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;outData, // куда записать значение свойства&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;UInt32 *&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;ioDataSize // на входе - размер передаваемых данных, на выходе - кол-во записанных данных&lt;br /&gt;); &lt;br /&gt;Получим формат звука из юнита:&lt;/p&gt;
						&lt;p&gt;AudioUnit unit;&lt;br /&gt;UInt32 propertySize = sizeof(AudioStreamBasicDescription);&lt;br /&gt;AudioStreamBasicDescription format;&lt;br /&gt;CA(AudioUnitGetProperty(unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &amp;amp;format, &amp;amp;propertySize));&lt;br /&gt;В данном случае мы получили формат звука, который необходимо подавать юниту на вход шины 0. Обратите внимание, что мы передаем и получаем размер данных, которые передали для сохранения значения свойства.&lt;/p&gt;
						&lt;p&gt;Установка свойства выглядит так:&lt;/p&gt;
						&lt;p&gt;OSStatus AudioUnitSetProperty(&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;AudioUnit	&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; inUnit, // юнит&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;AudioUnitPropertyID&amp;#160; &amp;#160; inID, // свойство&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;AudioUnitScope&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;inScope, // scope&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;AudioUnitElement&amp;#160; &amp;#160; &amp;#160; &amp;#160;inElement, // шина&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;const void *&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;inData, // значение свойства, которое необходимо установить&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;UInt32&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;inDataSize // размер передаваемых данных&lt;br /&gt;);&lt;br /&gt;UInt32 shouldAllocateBuffer = 1;&lt;br /&gt;CA(AudioUnitSetProperty(unit, kAudioUnitProperty_ShouldAllocateBuffer, kAudioUnitScope_Global, 0, &amp;amp;shouldAllocateBuffer, sizeof(UInt32)));&lt;br /&gt;Как видим, отличий от получения свойства не так уж и много. Разница лишь в том, что мы не получаем размер назад.&lt;/p&gt;
						&lt;p&gt;Параметры&lt;/p&gt;
						&lt;p&gt;Параметры, по сути, являются теми же свойствами, только они влияют на то, что юнит делает со звуком. Например, параметр kDelayParam_DelayTime для юнита AUDelay задает время в секундах, через которое повториться звук. Установка и получение параметра очень похожа на установку и получение свойства:&lt;br /&gt;OSStatus AudioUnitSetParameter(&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; AudioUnit&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;inUnit, // юнит&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; AudioUnitParameterID	inID, // ID параметра&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; AudioUnitScope&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; inScope, // scope (вход или выход)&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; AudioUnitElement&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; inElement, // шина&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; AudioUnitParameterValue&amp;#160; &amp;#160;inValue, // значение параметра&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; UInt32&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; inBufferOffsetInFrames // будет всегда равно 0&lt;br /&gt;);&lt;br /&gt;AudioUnitParameterValue delayValue = 1.5;&lt;br /&gt;CA(AudioUnitSetParameter(unit, kDelayParam_DelayTime, kAudioUnitScope_Global, 0, delayValue, 0));&lt;br /&gt;Получение параметра:&lt;br /&gt;AudioUnitParameterValue value;&lt;br /&gt;CA(AudioUnitGetParameter(unit, kDelayParam_DelayTime, kAudioUnitScope_Global, 0, &amp;amp;value));&lt;br /&gt;Аудиофайлы&lt;/p&gt;
						&lt;p&gt;Так как мы будем работать со звуком, и довольно часто этот звук будет сохранен в аудиофайлы, нам необходимо будет читать эти самые файлы со звуком. Для этого мы будем использовать ExtendedAudioFile API. Данное API позволяет «на лету» переводить звук из одного формата в другой, что нам будет очень полезно. API имеет небольшой набор функций: открыть/создать файл, запись/чтение, получить позицию/сдвинуть позицию.&lt;/p&gt;
						&lt;p&gt;Звук для чтения и записи всегда будет передаваться через структуру AudioBufferList:&lt;br /&gt;struct AudioBufferList&lt;br /&gt;{&lt;br /&gt;&amp;#160; &amp;#160; UInt32&amp;#160; &amp;#160; &amp;#160; mNumberBuffers; // количество буферов&lt;br /&gt;&amp;#160; &amp;#160; AudioBuffer mBuffers[1]; // массив буферов, на самом деле количество переменное&lt;br /&gt;};&lt;br /&gt;struct AudioBuffer&lt;br /&gt;{&lt;br /&gt;&amp;#160; &amp;#160; UInt32&amp;#160; mNumberChannels; // количество каналов в буфере&lt;br /&gt;&amp;#160; &amp;#160; UInt32&amp;#160; mDataByteSize; // размер буфера в байтах&lt;br /&gt;&amp;#160; &amp;#160; void*&amp;#160; &amp;#160;mData; // указатель на данные&lt;br /&gt;};&lt;br /&gt;Для работы с аудио-файлами будет использоваться специальная структура ExtAudioFileRef. Как и юнитам, аудиофайлам можно выставлять свойства. Нам очень сильно понадобиться свойство kExtAudioFileProperty_ClientDataFormat. Оно позволяет установить формат, в который мы хотим конвертировать звук при чтении, и формат звука, который мы будем передавать для записи. Так же очень полезным оказывается свойство kExtAudioFileProperty_FileLengthFrames, которое предоставляет нам количество фреймов в файле.&lt;/p&gt;
						&lt;p&gt;Утилита afconvert&lt;/p&gt;
						&lt;p&gt;Данная утилита на OS X позволит конвертировать любой аудиофайл в формат, который нам нужен будет для работы. Делается это так:&lt;br /&gt;afconvert -d F32@44100 -o MyFaforiteSong.mp3 MyFavoriteSong.caf&lt;br /&gt;Выше я писал о том, что мы будем работать с не сжатым звуком. Команда выше как раз позволяет преобразовать сжатый звук в не сжатый и пригодный для работы. В итоге мы получим звук, который представлен форматом Float32 и имеет частоту дискретизации 44100.&lt;/p&gt;
						&lt;p&gt;Это было небольшое введение, которое поможет лучше понимать то, что будет написано в следующих статьях. В следующих статьях мы рассмотрим некоторые юниты, а так же их совместное использование в графе. Спасибо за внимание!&lt;/p&gt;</description>
			<author>mybb@mybb.ru (Gates)</author>
			<pubDate>Wed, 14 Aug 2013 12:03:02 +0400</pubDate>
			<guid>http://programir.mybb.ru/viewtopic.php?pid=38#p38</guid>
		</item>
		<item>
			<title>Audio Unit в iOS. Часть 2, строим граф и проигрываем файлы</title>
			<link>http://programir.mybb.ru/viewtopic.php?pid=37#p37</link>
			<description>&lt;p&gt;В этой статье мы продолжим знакомиться с Audio Unit. Если вы не читали введение, обязательно прочитайте, т.к. возможно будет трудно понять содержание этой и следующих статей. В данной статье мы быстро пробежимся по AudioFileID, рассмотрим как строить граф из юнитов, а так же как воспроизвести звуковой файл.&lt;/p&gt;
						&lt;p&gt;Происходить это все будет с помощью двух юнитов — AudioFilePlayer и RemoteIO. Рекомендую загрузить исходный код проекта т.к. весь код из статьи — это пояснения к проекту. Реализация «плеера» находиться в классе Player.&lt;/p&gt;
						&lt;p&gt;Для статьи я подготовил 6 звуковых файлов, взятых с этого сайта.&lt;/p&gt;
						&lt;p&gt;AudioFileID&lt;/p&gt;
						&lt;p&gt;AudioFileID — это еще один тип данных, который может представлять аудио файл в iOS. В контексте данной статьи нам нужно знать, как открыть, закрыть и получить свойства таких файлов. Файл открывается с помощью функции AudioFileOpenURL, которой необходимо передать URL файла, режим доступа и указатель на структуру AudioFileID:&lt;br /&gt;AudioFileID audioFile;&lt;br /&gt;CFURLRef url = ...;&lt;br /&gt;AudioFileOpenURL(url, kAudioFileReadPermission, 0, &amp;amp;audioFile); // открываем в режиме чтения&lt;br /&gt;Закрывается файл функцией AudioFileClose. Для получения свойства нам нужно будет вызвать две функции — узнать размер, а затем и значение свойства. Рассмотрим на примере получения количества пакетов в файле:&lt;br /&gt;UInt32 propertySize;&lt;br /&gt;UInt32 packetsCount;&lt;br /&gt;AudioFileGetPropertyInfo(audioFile, kAudioFilePropertyAudioDataPacketCount, &amp;amp;propertySize, NULL); // узнаем размер&lt;br /&gt;AudioFileGetProperty(audioFile, kAudioFilePropertyAudioDataPacketCount, &amp;amp;propertySize, &amp;amp;packetsCount); // получаем значение свойства&lt;br /&gt;В обоих случаях мы передаем структуру AudioFileID, свойство которое хотим получить и его размер. Последний аргумент функции AudioFileGetPropertyInfo — флаг, который скажет нам о том, можно ли изменять свойство. В данной ситуации он нам не нужен и мы можем передать NULL. Последний аргумент функции AudioFileGetProperty — есть указатель, куда сохранить значение свойства.&lt;br /&gt;AUAudioFilePlayer&lt;/p&gt;
						&lt;p&gt;AUAudioFilePlayer — это юнит. Данный юнит умеет воспроизводить аудифайлы представленные через AudioFileID. Под «воспроизводить» я имею ввиду чтение аудио-файла и передача звука другому юниту, другими словами AUAudioFilePlayer умеет генерировать звук. Юнит не имеет входных шин, но имеет выходную, через которую он и отправляет дальше считанный из файла(ов) звук. Один AUAudioFilePlayer-юнит может воспроизводить несколько аудиофайлов, которые необходимо обозначить заранее. Но, к сожалению, в один момент времени он может воспроизводить один файл.&lt;/p&gt;
						&lt;p&gt;Перед запуском юнита, его необходимо настроить. Последовательность настройки может быть такой:&lt;/p&gt;
						&lt;p&gt;Установка времени старта юнита&lt;br /&gt;Установка проигрываемых файлов&lt;br /&gt;Указание количества фреймов, которые должны быть загружены перед воспроизведением&lt;br /&gt;Время юнита&lt;/p&gt;
						&lt;p&gt;Мы начнем с внутреннего времени юнита. У юнита есть свое внутреннее время, которое представляется структурой AudioTimeStamp:&lt;br /&gt;struct AudioTimeStamp&lt;br /&gt;{&lt;br /&gt;&amp;#160; &amp;#160; Float64&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;mSampleTime;&lt;br /&gt;&amp;#160; &amp;#160; UInt64&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; mHostTime;&lt;br /&gt;&amp;#160; &amp;#160; Float64&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;mRateScalar;&lt;br /&gt;&amp;#160; &amp;#160; UInt64&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; mWordClockTime;&lt;br /&gt;&amp;#160; &amp;#160; SMPTETime&amp;#160; &amp;#160; &amp;#160; &amp;#160;mSMPTETime;&lt;br /&gt;&amp;#160; &amp;#160; UInt32&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; mFlags;&lt;br /&gt;&amp;#160; &amp;#160; UInt32&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; mReserved;&lt;br /&gt;};&lt;br /&gt;Как видим, структура может вести подсчет времени в нескольких единицах. В каких единицах выражено время, можно определить с помощью поля mFlags. AUAudioFilePlayer использует mSampleTime. Это время, измеряемое количеством отсчетов с некоторого момента (старта юнита или графа). Перед запуском юнита, нужно определиться, в какое время он начнет свою работу. Мы хотим чтобы юнит запустился сразу вместе с графом, поэтому подготовим такую структуру:&lt;/p&gt;
						&lt;p&gt;AudioTimeStamp startTime;&lt;br /&gt;memset (&amp;amp;startTime, 0, sizeof(AudioTimeStamp));&lt;br /&gt;startTime.mFlags = kAudioTimeStampSampleTimeValid; // обозначим в каких единицах идет отсчет&lt;br /&gt;startTime.mSampleTime = -1; // запустить при первой же возможности&lt;br /&gt;Далее необходимо установить данную структуру, как значение свойства kAudioUnitProperty_ScheduleStartTimeStamp, которое и обозначает время старта юнита:&lt;br /&gt;AudioUnit playerUnit = ...;&lt;br /&gt;CA(AudioUnitSetProperty(playerUnit, kAudioUnitProperty_ScheduleStartTimeStamp, kAudioUnitScope_Global, 0, &amp;amp;startTime, sizeof(AudioTimeStamp)));&lt;br /&gt;После запуска юнита он отсчитывает свое время, которое так же выражено структурой AudioTimeStamp. Получить его можно вот так:&lt;br /&gt;AudioTimeStamp timeStamp;&lt;br /&gt;AudioUnit playerUnit = ...;&lt;br /&gt;UInt32 propSize = sizeof(AudioTimeStamp);&lt;br /&gt;CA(AudioUnitGetProperty(playerUnit, kAudioUnitProperty_CurrentPlayTime, kAudioUnitScope_Global, 0, &amp;amp;timeStamp, &amp;amp;propSize));&lt;br /&gt;Установка проигрываемых файлов&lt;/p&gt;
						&lt;p&gt;Далее необходимо обозначить файлы, которые будет воспроизводить юнит. Для этого существует свойство kAudioUnitProperty_ScheduledFileIDs, в которое можно установить массив из AudioFileID.&lt;/p&gt;
						&lt;p&gt;AudioUnit playerUnit = ...;&lt;br /&gt;AudioFileID *files = ...; // массив из AudioFileId&lt;br /&gt;int filesCount = ...; // количество файлов&lt;br /&gt;CA(AudioUnitSetProperty(playerUnit, kAudioUnitProperty_ScheduledFileIDs, kAudioUnitScope_Global, 0, files, sizeof(AudioFileID) * filesCount));&lt;br /&gt;Количество фреймов для старта&lt;/p&gt;
						&lt;p&gt;Юниту нужно указать, сколько фреймов загрузить перед началом воспроизведения. Это можно сделать так:&lt;br /&gt;UInt32 prime = 0; // ни одного фрейма&lt;br /&gt;AudioUnit playerUnit = ...;&lt;br /&gt;CA(AudioUnitSetProperty(playerUnit, kAudioUnitProperty_ScheduledFilePrime, kAudioUnitScope_Global, 0, &amp;amp;prime, sizeof(prime)));&lt;br /&gt;В данном случае мы указали 0 и это является тоже правильным значением. Дело в том, что такой вызов не вернет управление до тех пор, пока не считает заданное количество фреймов.&lt;br /&gt;Воспроизведение файлов&lt;/p&gt;
						&lt;p&gt;Чтобы файл начал воспроизводиться нужно запланировать (schedule) данное деяние. Для этого нужно заполнить структуру ScheduledAudioFileRegion и установить ее в свойство kAudioUnitProperty_ScheduledFileRegion. Структура выглядит так:&lt;br /&gt;struct ScheduledAudioFileRegion {&lt;br /&gt;	AudioTimeStamp mTimeStamp; // внутреннее время юнита, в которое должно начаться воспроизведение&lt;br /&gt;	ScheduledAudioFileRegionCompletionProc&amp;#160; mCompletionProc; // коллбэк для уведомления о событиях&lt;br /&gt;	void * mCompletionProcUserData; // любые данные&lt;br /&gt;	struct OpaqueAudioFileID * mAudioFile; // AudioFileID который хотим играть&lt;br /&gt;	UInt32 mLoopCount; // сколько раз повторить проигрывание&lt;br /&gt;	SInt64 mStartFrame; // с какого фрейма начать&lt;br /&gt;	UInt32 mFramesToPlay; // сколько фреймов играть&lt;br /&gt;};&lt;br /&gt;Благодаря полям mStartFrame и mFramesToPlay мы можем не воспроизводить весь файл целиком, а лишь некоторые его части. Если мы хотим воспроизвести файл целиком, нужно выставить mStartFrame в 0, а mFramesToPlay в полное количество фреймов в файле. Узнать количество фреймов в файле можно умножив поле mFramesPerPacket структуры AudioStreamBasicDescription на количество пакетов в файле. О том, как узнать количество пакетов в файле мы рассматривали выше. Так же все это вы сможете увидеть в исходном коде проекта, прикрепленном к статье.&lt;/p&gt;
						&lt;p&gt;Если мы хотим, чтобы воспроизведение началось как можно быстрее, «прямо сейчас», то нам нужно выставить поле mTimeStamp в текущее время юнита (о том как его узнать написано выше). Для подстраховки, можно добавить некоторое количество фреймов.&lt;/p&gt;
						&lt;p&gt;AUGraph&lt;/p&gt;
						&lt;p&gt;Как я и говорил в введении — мы можем строить граф из юнитов. В данной статье мы построим граф из двух юнитов — AudioFilePlayer и RemoteIO. По-сути, граф содержит в себе вершины (nodes), которые связанны между собой. Как связаны вершины — решаем мы сами. Звук «идет» от одной вершине к другой, и в каждой вершине он обрабатывается юнитом, который находится в вершине. На звук может быть наложен эффект, звук может отправиться на динамики и т.д. Мы построим вот такой граф:&lt;br /&gt;&lt;/p&gt;
						&lt;p&gt;Как видим, AudioFilePlayer не имеет входных шин, зато имеет выходную. RemoteIO имеет входную шину 0 (поданный в нее звук отправится на динамики) и выходную шину 1 (это звук, записанный с микрофона, в этой статье не пригодится). Количество входов и выходов в/из вершины в точности повторяет количество шин в юните, который находиться в этой вершине. AudioFilePlayer будет читать аудиофайл и отправлять в RemoteIO, который уже отправит звук на динамики.&lt;/p&gt;
						&lt;p&gt;Граф представляется структурой AUGraph. Граф может быть открыт/закрыт, запущен/остановлен, инициализирован/не инициализирован. Процедура построения графа такая:&lt;/p&gt;
						&lt;p&gt;Создать граф&lt;br /&gt;Добавить в него вершины (юниты)&lt;br /&gt;Соединить вершины&lt;br /&gt;Открыть граф&lt;br /&gt;Инициализировать&lt;br /&gt;Настроить юниты&lt;br /&gt;Запустить&lt;br /&gt;Итак, мы хотим создать граф:&lt;br /&gt;AUGraph graph;&lt;br /&gt;CA(NewAUGraph(&amp;amp;graph));&lt;br /&gt;После создания графа мы должны добавить вершины в граф. Вершины создаются на основе юнитов, а в частности с помощью AudioComponentDescription. Для юнита RemoteIO будет выглядеть так:&lt;br /&gt;AudioComponentDescription acd;&lt;br /&gt;acd.componentManufacturer = kAudioUnitManufacturer_Apple;&lt;br /&gt;acd.componentType = kAudioUnitType_Output;&lt;br /&gt;acd.componentSubType = kAudioUnitSubType_RemoteIO;&lt;br /&gt;AUNode rioNode;&lt;br /&gt;CA(AUGraphAddNode(graph, &amp;amp;acd, &amp;amp;rioNode));&lt;br /&gt;А так же для AudioFilePlayer:&lt;br /&gt;acd.componentManufacturer = kAudioUnitManufacturer_Apple;&lt;br /&gt;acd.componentType = kAudioUnitType_Generator;&lt;br /&gt;acd.componentSubType = kAudioUnitSubType_AudioFilePlayer;&lt;br /&gt;AUNode playerNode;&lt;br /&gt;CA(AUGraphAddNode(graph, &amp;amp;acd, &amp;amp;playerNode));&lt;br /&gt;Тут фигурирует тип AUNode, который хранит в себе вершину. Нам следует всегда сохранять созданную вершину, т.к. далее нам нужно будет связать вершины и получить юниты, которые находятся в вершинах.&lt;/p&gt;
						&lt;p&gt;Следует обратить внимание, что мы не создаем юниты как таковые, а создаем вершины, которые будут содержать в себе юниты (поэтому нам и необходим AudioComponentDescription).&lt;/p&gt;
						&lt;p&gt;После того, как вершины созданы, можно их соединить:&lt;br /&gt;CA(AUGraphConnectNodeInput(graph, playerNode, 0, rioNode, 0));&lt;br /&gt;В данном случае мы направляем звук из выходной шины 0 вершины playerNode во входную шину 0 вершины rioNode.&lt;br /&gt;Далее необходимо открыть граф:&lt;br /&gt;CA(AUGraphOpen(graph));&lt;br /&gt;После открытия юниты в вершинах будут созданы, но еще не будут инициализированны. На данном этапе самое время получить указатели на юниты, которые находятся в вершинах графа. Для этих целей существует функция AUGraphNodeInfo. Для RemoteIO юнита:&lt;br /&gt;AudioUnit rioUnit; // сам юнит&lt;br /&gt;CA(AUGraphNodeInfo(graph, rioNode, NULL, &amp;amp;rioUnit));&lt;br /&gt;Для AudioFilePlayer:&lt;br /&gt;AudioUnit playerUnit;&lt;br /&gt;CA(AUGraphNodeInfo(graph, playerNode, NULL, &amp;amp;playerUnit));&lt;br /&gt;Как видим, функция принимает на вход граф и вершину. В качестве третьего аргумента можно передать указатель на AudioComponentDescription, в который запишется описание юнита, но в данном случае нам это не нужно и мы передаем NULL. Последний аргумент — указатель на AudioUnit, куда будет записан юнит.&lt;/p&gt;
						&lt;p&gt;Далее можно инициализировать граф:&lt;br /&gt;CA(AUGraphInitialize(graph));&lt;br /&gt;После этой строчки юниты будут проинициализированны, они «захватят» необходимые ресурсы и будут готовы к работе. Остается только запустить граф:&lt;/p&gt;
						&lt;p&gt;CA(AUGraphStart(graph));&lt;br /&gt;После вызова AUGraphStart граф начнет работать. Но все же некоторые юниты нуждаются в «настройке» и поэтому есть риск вообще ничего не услышать. Например, AudioFilePlayer нужно настраивать так, как описано было выше.&lt;/p&gt;
						&lt;p&gt;Если мы больше не нуждаемся в графе, мы должны сделать вот так:&lt;br /&gt;AUGraphStop (graph);&lt;br /&gt;AUGraphUninitialize (graph);&lt;br /&gt;AUGraphClose(graph);&lt;br /&gt;DisposeAUGraph(graph);&lt;br /&gt;Граф необязательно всегда должен находиться в запущенном состоянии. Мы безболезненно можем его останавливать и запускать функциями AUGraphStop и AUGraphStart. Например, если хотим приостановить звук. Так останавливать граф нужно, если мы хотим изменить связи вершин.&lt;/p&gt;
						&lt;p&gt;Заключение&lt;/p&gt;
						&lt;p&gt;Исходный код проекта находится тут. Там вы найдете реализацию графа с двумя юнитами — RemoteIO и AudioFilePlayer.&lt;/p&gt;
						&lt;p&gt;Возможно, статья получилось несколько запутанной, очень рекомендую просмотреть исходный код проекта и по-эксперементировать с юнитами. В статье я постарался описать необходимые этапы создания аудио-графа и настройки AudioFilePlayer. Если что-то забыл описать — все это вы найдете в прикрепленном проекте.&lt;/p&gt;</description>
			<author>mybb@mybb.ru (Gates)</author>
			<pubDate>Wed, 14 Aug 2013 12:02:34 +0400</pubDate>
			<guid>http://programir.mybb.ru/viewtopic.php?pid=37#p37</guid>
		</item>
		<item>
			<title>Audio Unit в iOS. Часть 3, накладываем эффект Delay</title>
			<link>http://programir.mybb.ru/viewtopic.php?pid=36#p36</link>
			<description>&lt;p&gt;В этой статье мы увидим магию будем накладывать эффект Delay на звук. Сделано будет все на основе исходного кода прошлой статьи, дописывать будем именно туда.&lt;/p&gt;
						&lt;p&gt;Как я и говорил, с помощью Audio Unit мы можем накладывать эффекты на звук, мы рассмотрим это на примере графа с тремя вершинами: AudioFilePlayer, Delay, RemoteIO. Звук из плеера будет поступать на вход эффекта, а из эффекта будет идти на RemoteIO, который свою очередь отправит звук на динамики.&lt;/p&gt;
						&lt;p&gt;Как добавить юнит в граф&lt;/p&gt;
						&lt;p&gt;Интересующий нас юнит с эффектом имеет тип kAudioUnitType_Effect и подтип kAudioUnitSubType_Delay. Добавить вершину с таким юнитом в граф можно так:&lt;br /&gt;AudioComponentDescription acd;&lt;br /&gt;acd.componentManufacturer = kAudioUnitManufacturer_Apple;&lt;br /&gt;acd.componentType = kAudioUnitType_Effect;&lt;br /&gt;acd.componentSubType = kAudioUnitSubType_Delay;&lt;/p&gt;
						&lt;p&gt;AUGraph graph = ...;&lt;br /&gt;AUNode delayNode;&lt;br /&gt;CA(AUGraphAddNode(graph, &amp;amp;acd, &amp;amp;delayNode));&lt;br /&gt;Так же, как и для других юнитов, мы можем получить сам юнит из вершины (обязательно после открытия графа!):&lt;br /&gt;AUGraph graph = ...;&lt;br /&gt;AudioUnit delayUnit;&lt;br /&gt;CA(AUGraphNodeInfo(graph, delayNode, NULL, &amp;amp;delayUnit));&lt;br /&gt;Теперь соединим вершины:&lt;br /&gt;AUGraph graph = ...;&lt;br /&gt;AUNode playerNode = ...;&lt;br /&gt;AUNode delayNode = ...;&lt;br /&gt;AUNode rioNode = ...;&lt;br /&gt;CA(AUGraphConnectNodeInput(graph, playerNode, 0, delayNode, 0));&lt;br /&gt;CA(AUGraphConnectNodeInput(graph, delayNode, 0, rioNode, 0)); &lt;br /&gt;Первым вызовом AUGraphConnectNodeInput мы подключаем выход плеера ко входу эффекта, а вторым вызовом AUGraphConnectNodeInput мы подключаем выход эффекта ко входу RemoteIO.&lt;br /&gt;&lt;/p&gt;
						&lt;p&gt;Параметры юнита Delay&lt;/p&gt;
						&lt;p&gt;Как было сказано в введении, у юнитов кроме свойств есть еще и параметры. Параметры влияют на то, что юнит делает со звуком. Итак, Delay имеет 4 параметра:&lt;/p&gt;
						&lt;p&gt;kDelayParam_WetDryMix — влияет на то, сколько (по громкости) мы слышим оригинального звука и звука измененного эффектом. Диапазон от 0 до 100. Например, если значение 30, то мы слышим 70% громкости оригинального звука и 30% измененного. По-умолчанию значение равно 50.&lt;br /&gt;kDelayParam_DelayTime — длинна задержки звука в секундах. Диапазон от 0 до 2 секунд. По-умолчанию значение 1. В таком случае мы будем слышать повторяющийся звук через секунду.&lt;br /&gt;kDelayParam_Feedback — определяет насколько быстро будет затухать звук. Значения от -100 до 100. Чем больше значение (по моим наблюдениям, по модулю) тем медленнее будет затухать звук. Похоже, что на 100 совсем не затухает.&lt;br /&gt;kDelayParam_LopassCutoff — самый нераскрытый для меня параметр. Но, очевидно, это — фильтр, который пропускает все частоты ниже заданных. Диапазон от 10 до половины частоты дискретизации звука. По-умолчанию равен 15000 Hz.&lt;br /&gt;Установим, например, длинну задержки звука:&lt;br /&gt;AudioUnit delayUnit = ...;&lt;br /&gt;AudioUnitParameterValue value = 0.5; // длинна задержки - пол секунды&lt;br /&gt;CA(AudioUnitSetParameter(delayUnit, kDelayParam_DelayTime, kAudioUnitScope_Global, 0, value, 0));&lt;br /&gt;Функция принимает сам юнит, параметр, scope для которого применяется, шина для которой применяется (первый ноль), значение параметра. Последний аргумент должен быть равен нулю.&lt;br /&gt;Заключение&lt;/p&gt;
						&lt;p&gt;Как видим, добавить вершину с юнитом в граф — не такая уж и тяжелая задача. Исходный код проекта вы можете загрузить по этой ссылке. Код оснащен комментариями, чтобы было проще разобраться. Рекомендую обратить внимание на метод setupDelayUnit, изменить некоторые параметры и посмотреть как они влияют на звук.&lt;/p&gt;</description>
			<author>mybb@mybb.ru (Gates)</author>
			<pubDate>Wed, 14 Aug 2013 12:01:55 +0400</pubDate>
			<guid>http://programir.mybb.ru/viewtopic.php?pid=36#p36</guid>
		</item>
		<item>
			<title>Android Emulator не принимает ввод с клавиатуры</title>
			<link>http://programir.mybb.ru/viewtopic.php?pid=35#p35</link>
			<description>&lt;p&gt;Во время обновления SDK tools до ревизии 20 и выше, можно столкнуться с проблемой, когда во время покадания фокуса ввода на эмуляторе скажем в EditView появляется Soft Keyboard ( как это и должно быть), ввод с коророй работат, а вот ввод данных с клавиатуры – нет. Не очень удобно мышкой кликать по первой и набирать таким образом текст на эмуляторе, согласитесь?&lt;/p&gt;
						&lt;p&gt;Причиной тому является следующее: повидимому в этой ревизии SDK флаг keyboard установлен в false. Посему просто стоит сделать следующее: добавить&lt;/p&gt;
						&lt;p&gt;hw.keyboard=yes&lt;/p&gt;
						&lt;p&gt;в файле config.ini, который находится в папке образа эмулятора. Обычно это папка&lt;/p&gt;
						&lt;p&gt;~/.android/avd/.avd/&lt;/p&gt;
						&lt;p&gt;Если вы работаете под линуксом, то это довольно легко сделать для всех эмуляторов при помощи команды в терминале:&lt;/p&gt;
						&lt;p&gt;for f in ~/.android/avd/*.avd/config.ini; do echo ‘hw.keyboard=yes’ &amp;gt;&amp;gt; «$f»; done&lt;/p&gt;
						&lt;p&gt;Для этого ни в каких правах суперпользователя нет надобности.&lt;/p&gt;
						&lt;p&gt;Второй способ – сделать это непосредственно с помощью ADT плагина в эклипс:&lt;/p&gt;
						&lt;p&gt;1. Идем в AVD Mananger.&lt;br /&gt;&lt;/p&gt;
						&lt;p&gt;2. Выбираем эмулятор, жмем Edit.&lt;br /&gt;&lt;/p&gt;
						&lt;p&gt;3. В секции Hardware добавляем новое свойство Keyboard support.&lt;br /&gt;&lt;/p&gt;
						&lt;p&gt;(Кстати, таким образом можно доабвлять для эмуляторов поддержку еще много чего, смотри весь список доступных параметров)&lt;/p&gt;
						&lt;p&gt;Действия 1-3 какраз и добавят требуемую строку в ini-файл.&lt;/p&gt;</description>
			<author>mybb@mybb.ru (Gates)</author>
			<pubDate>Wed, 14 Aug 2013 11:57:47 +0400</pubDate>
			<guid>http://programir.mybb.ru/viewtopic.php?pid=35#p35</guid>
		</item>
		<item>
			<title>Cloud для своего мобильного приложения</title>
			<link>http://programir.mybb.ru/viewtopic.php?pid=34#p34</link>
			<description>&lt;p&gt;Допустим, вы решили написать свое небольшое (или большое) мобильное приложение, которое может требовать хранения данных на отдельном сервере. А вот писать отдельный сервер, покупать домен, выкладывать его где-то в интернете и тд нет возможности или времени. Почему бы не воспользоваться уже готовыми решениями. Одним из таких решений может быть parse.com. Вот как они себя описывают:&lt;/p&gt;
						&lt;p&gt;The complete mobile app platform&lt;br /&gt;Parse lets you focus on creating unique &amp;amp; engaging apps. We take care of the rest.&lt;/p&gt;
						&lt;p&gt;Cloud Code&lt;br /&gt;Run custom app code in the Parse Cloud.&lt;/p&gt;
						&lt;p&gt;Parse Push&lt;br /&gt;Increase user engagement with push notifications&lt;br /&gt;- Easily integrate push into your new or existing app?&lt;br /&gt;- Compatible with iOS and Android&lt;br /&gt;- Powerful push management console&lt;/p&gt;
						&lt;p&gt;Если коротко – сервис предлагает хранить ваи данные у себя в клауде, при этом предоставляя вам простой и прозрачный доступ к ним. Вы можете:&lt;br /&gt;- создавать и редактировать таблицы на сервере буквально в несколько кликов,&lt;br /&gt;- существует поддержка аутентификации через email а также с помощью Facebook и Twitter аккаунтов.&lt;br /&gt;- возможность отсылки пуш-уведомлений на устройства (поддерживаются как Android так и iOS девайсы).&lt;/p&gt;
						&lt;p&gt;Сервис предоставляет библиотеку, которую вы можете легко интегрировать в свое приложение и вызывать сервисы в клауде.&lt;br /&gt;Бесплатно вы можете хранить 1 Гб данных и использовать 1,000,000 API запросов и 1,000,000 пуш уведомлений в месяц.&lt;br /&gt;Если Ваше приложение требует бОльших ресурсов – можно легко докупить их согласно прейскуранту. Сразу скажу, что за 50 баксов в месяц можно поддерживать довольно таки больших объемов приложение.&lt;/p&gt;
						&lt;p&gt;А теперь коротко о этих преимуществах, по большей части в картинках.&lt;/p&gt;
						&lt;p&gt;1. Регистрируемся на сайте parse.com&lt;/p&gt;
						&lt;p&gt;2. Создаем приложение&lt;br /&gt;&lt;/p&gt;
						&lt;p&gt;3. После чего мы можем с легкостью создавать таблицы, которые будут хранить данные (да, прямо как sql таблицы). Таблицы могут быть как кастомного типа, тоесть отражать ваши сущности, так и предопределенного типа (Installation, User и т.д) в которых будет храниться информация касательно инсталляций вашего приложения пользователями (грубо говоря, если на клиенте при первом запуске рпиложения вызвать соответствующий метод, то в эту таблицу будут трекаться данные, касающиеся времени инсталляции, версии программы и тд), в таблице Users будут храниться аккаунты пользователей, если вы захотите внедрить возможность аутентификации в вашем приложении и т.д.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
						&lt;p&gt;4. На вкладке Push Notifications будет отражаться статистика по уведомлениям, отосланным в зарегистрированные «каналы». ( о чем можно почитать в документации)&lt;br /&gt;&lt;/p&gt;
						&lt;p&gt;5. Для того, чтобы иметь возможность аутентифицироваться через Facebook или Twitter, вам необходимо будет зарегистрировать на соответствующих сервисах свои приложения и ввесть в форме их id.&lt;br /&gt;&lt;/p&gt;
						&lt;p&gt;Также можно смотреть статистику в виде графиков, сколько вашими приложениями было сделать апи-запросов и им отослано пуш-уведомлений.&lt;br /&gt;&lt;/p&gt;
						&lt;p&gt;Вобщем, еще много чего можно говорить, но данный пост подразумевате только ознакомление с сервисом и тчо такое уже есть.&lt;/p&gt;
						&lt;p&gt;Так что если вы надумали сделать свое клиент-верверное приложение и данное решение вам подходит – пользуйтесь на здоровье! У них довольно хорошо все документировано, есть Java doc по SDK и т.д.&lt;/p&gt;
						&lt;p&gt;P.S. Закончу небольшой ложкой дёгтя. Втечение последней недели сервис пару раз ненадолго (несколько минут) падал и сайт не работал. Надеюсь, это временные неудобства, так как контора у них довольно большая. Если же в серьез задумаетесь становиться их клиентами – то стоит возможно изначально порыскать на форумах на эту тематику.&lt;/p&gt;</description>
			<author>mybb@mybb.ru (Gates)</author>
			<pubDate>Wed, 14 Aug 2013 11:57:08 +0400</pubDate>
			<guid>http://programir.mybb.ru/viewtopic.php?pid=34#p34</guid>
		</item>
		<item>
			<title>Получение номера телефона в Android</title>
			<link>http://programir.mybb.ru/viewtopic.php?pid=33#p33</link>
			<description>&lt;p&gt;Когда-то, помнится, я упоминал, как можно получить номер телефона (симки) буквально двумя строчками кода:&lt;/p&gt;
						&lt;p&gt;1&lt;br /&gt;2&lt;br /&gt;3&lt;br /&gt;TelephonyManager tMgr =&lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160;(TelephonyManager)mAppContext.getSystemService(Context.TELEPHONY_SERVICE);&lt;br /&gt;&amp;#160; mPhoneNumber = tMgr.getLine1Number();&lt;br /&gt;Но тут есть одно но. Этот способ убдет работать далеко не всегда, а точнее, далеко не со всеми SIM-картами. Дело в том, что физически номер хранится не на всех SIM-картах. Аутентифицируются они своими способами в сети, что позволяет сменить номер телефона без смены SIM. Поэтому, если у вас в приложении очень многое завязано на номере телефона, то лучше подстраховаться и с нужный момент попросить пользователя ввести номер вручную (естественно, снабдив это все верификациями и тд) или же есть еще один способ, который может стоить 1 SMS. Примерно такой: если вышеописанный код таки возвразает null – можно сгенерировать какой-то код, отослать его в SMS-сообщении на свой номер ( номер таки прийдется спросить у пользователя) и повесить ресивер на SMS, в котором проанализоировать текст сообщения и если он совпадает со сгенеренным – то взять ноемр, с которого оно, сообщение, пришло, и использовать уже его. Причем тут также стоит потрудиться и привести его к полному формату +ххх-хх-хх-хххх , тоесть с кодом страны и т.д., так как замечено, что, скажем, в штатах, у которых код +1, SMS может таки приходить от номера без кода. Далее – удалить сообщение. Недостаток – все сообщения будут проверяться этим кодом.&lt;/p&gt;</description>
			<author>mybb@mybb.ru (Gates)</author>
			<pubDate>Wed, 14 Aug 2013 11:56:45 +0400</pubDate>
			<guid>http://programir.mybb.ru/viewtopic.php?pid=33#p33</guid>
		</item>
		<item>
			<title>Ubuntu не видит Android телефон</title>
			<link>http://programir.mybb.ru/viewtopic.php?pid=32#p32</link>
			<description>&lt;p&gt;Во время разработки для Android под Ubuntu может сложиться ситуация, когда подключаете телефон к сомпьютеру через USB, включили в настройках «USB Debugging» , а adb телефон всеравно не видит.&lt;br /&gt;Для того, чтобы Linux корректноопределил устройство, необходимо добавить udev rules файл , который содержит USB конфигурацию для производителя вашего устройства. В нем каждый производитель идентифицируется уникальным ID, который определяется свойством ATTR{idVendor}.&lt;/p&gt;
						&lt;p&gt;Шаги следующие:&lt;/p&gt;
						&lt;p&gt;1. Открываем терминал, создаем файл /etc/udev/rules.d/51-android.rules.&lt;/p&gt;
						&lt;p&gt;$sudo gedit /etc/udev/rules.d/51-android.rules&lt;/p&gt;
						&lt;p&gt;2. Добавить в файл следующие свойства (на примере HTC):&lt;/p&gt;
						&lt;p&gt;SUBSYSTEM==»usb», ATTR{idVendor}==»0bb4&amp;#8243;, MODE=»0666&amp;#8243;, GROUP=»plugdev»&lt;/p&gt;
						&lt;p&gt;тут «0bb4&amp;#8243; – это ID тайваньского производителя.&lt;/p&gt;
						&lt;p&gt;3. Выполнить команду&lt;/p&gt;
						&lt;p&gt;$sudo chmod a+r /etc/udev/rules.d/51-android.rules&lt;/p&gt;
						&lt;p&gt;После этого можем проверить доступность для adb нашего устройства. В папке с /platform-tools запускаем:&lt;/p&gt;
						&lt;p&gt;$./adb devices&lt;/p&gt;
						&lt;p&gt;Команда выведет список подключенных к компьютеру устройств.&lt;/p&gt;
						&lt;p&gt;Полный список идентификаторов производителей:&lt;/p&gt;
						&lt;p&gt;Company	USB Vendor ID&lt;br /&gt;Acer	0502&lt;br /&gt;ASUS	0b05&lt;br /&gt;Dell	413c&lt;br /&gt;Foxconn	0489&lt;br /&gt;Fujitsu	04c5&lt;br /&gt;Fujitsu Toshiba	04c5&lt;br /&gt;Garmin-Asus	091e&lt;br /&gt;Google	18d1&lt;br /&gt;Hisense	109b&lt;br /&gt;HTC	0bb4&lt;br /&gt;Huawei	12d1&lt;br /&gt;K-Touch	24e3&lt;br /&gt;KT Tech	2116&lt;br /&gt;Kyocera	0482&lt;br /&gt;Lenovo	17ef&lt;br /&gt;LG	1004&lt;br /&gt;Motorola	22b8&lt;br /&gt;NEC	0409&lt;br /&gt;Nook	2080&lt;br /&gt;Nvidia	0955&lt;br /&gt;OTGV	2257&lt;br /&gt;Pantech	10a9&lt;br /&gt;Pegatron	1d4d&lt;br /&gt;Philips	0471&lt;br /&gt;PMC-Sierra	04da&lt;br /&gt;Qualcomm	 05c6&lt;br /&gt;SK Telesys	1f53&lt;br /&gt;Samsung	04e8&lt;br /&gt;Sharp	04dd&lt;br /&gt;Sony	054c&lt;br /&gt;Sony Ericsson	0fce&lt;br /&gt;Teleepoch	2340&lt;br /&gt;Toshiba	0930&lt;br /&gt;ZTE	19d2&lt;/p&gt;</description>
			<author>mybb@mybb.ru (Gates)</author>
			<pubDate>Wed, 14 Aug 2013 11:56:25 +0400</pubDate>
			<guid>http://programir.mybb.ru/viewtopic.php?pid=32#p32</guid>
		</item>
		<item>
			<title>Android Log Collector</title>
			<link>http://programir.mybb.ru/viewtopic.php?pid=31#p31</link>
			<description>&lt;p&gt;Есть замечательная программа Android Log Collectror, позволяющая отсылать системные логи девайса к примеру на e-mail.&lt;br /&gt;Она может быть использована так отдельное stand alone приложение, так и позволяет запустить себя при помощи интента с вашего приложения. Данным приложением удобно пользоваться, если, к примеру, ваши заказчики не особо технически подкованы, вы им отсылаете билды приложения а оно у них крашится по непонятным для них причинам. Последовательность действий они конечно могут вам сообщить, модель телефона и т.д., но сами понимаете, что может быть полезнее самого лога!)&lt;/p&gt;
						&lt;p&gt;Последовательность действий довольно проста: устанавливается apk файл, на рабочем столе появляется обычная иконка приложения. После запускается тестируемая программа и «гоняется» до краша к примеру. После чего запускается LogCollector, который из буфера считывает последние логи и предложит их выслать одним из доступных способов, к примеру по email.&lt;/p&gt;</description>
			<author>mybb@mybb.ru (Gates)</author>
			<pubDate>Wed, 14 Aug 2013 11:56:03 +0400</pubDate>
			<guid>http://programir.mybb.ru/viewtopic.php?pid=31#p31</guid>
		</item>
		<item>
			<title>5 лучших клиентов Twitter для Android</title>
			<link>http://programir.mybb.ru/viewtopic.php?pid=30#p30</link>
			<description>&lt;p&gt;При растущей популярности ОС Android для смартфонов, она стала претендовать на внимание со стороны iPhone фирмы Apple. С подъёмом Android–а пришло то, вокруг чего он вращается — открытое программное обеспечение. Кроме того, что она вращается вокруг бытия открытым ПО, ОС Android также пустила глубокие корни в социальные службы. Произошел выброс разных Twitter–ных приложений для Android–ных устройств, у каждого были свои взлёты и падения, они предназначены для разных типов пользователей — от известных в обществе людей до опытных пользователей Twitter–а. Те приложения включают 5 верхних клиентов Twitter–а для Android–а (используют телефон с ОС Android 2.1 – &amp;#201;clair, но большинство из них совместимо с ОС 1.5 и 1.6, а если их не будет в наличии для всех версий ОС, это будет указано):&lt;/p&gt;
						&lt;p&gt;Twitter for Android (Бесплатно)&lt;/p&gt;
						&lt;p&gt;Twitter for Android&lt;/p&gt;
						&lt;p&gt;Это официальное приложение для доступа к Twitter с вашего Android–ного мобильного устройства. Это приложение, разработанное самими работниками Twitter–а, самое разностороннее из присутствующих на рынке, учитывая то, что оно было создано творцами Twitter–а. Оно имеет всё, что когда–либо понадобится среднему светскому человеку, а также функции, которые будут сильно хотеть опытные пользователи. Вот некоторые из за и против:&lt;/p&gt;
						&lt;p&gt;За:&lt;br /&gt;Интерфейс:&lt;br /&gt;Интерфейс Twitter–а для Android–а очень хороший и краткий, позволяет попадать туда, куда вы хотите, быстро и умело. При начале работы с приложением вам представляют варианты того, что вы можете делать — такие как просмотр сообщений, списков, упоминаний, пересланных записей, прямых посланий и профиля. Кажется, разработчики Twitter–а для Android–а взяли страницу из руководства Google, так как время загрузок изумительно быстрое. Панель инструментов вверху приложения содержит возможность размещать обновления на своей странице и находить определённые записи людей на основе ключевых слов и/или места пребывания. Также, маркер вашего нахождения на карте в углу, который позволит находить записи людей, которые ближе других к вашему месту. Также хорошо отметить, что логотип Twitter–а вверху слева на любом экране направит вас назад к главному меню.&lt;/p&gt;
						&lt;p&gt;Скорость:&lt;br /&gt;Время загрузок, как упомянуто выше, вполне быстрое. Это прекрасно для пользователя на ходу, который не хочет ждать загрузки целого приложения прежде, чем сделать запись. Оно может быть дольше у пользователей с меньшей оперативной памятью, но не намного.&lt;/p&gt;
						&lt;p&gt;Темы тенденций:&lt;br /&gt;При заходе на сайт твиттеров появляется много тем тенденций. Принести эту функцию к мобильному обществу и сделать так, чтобы она хорошо выглядела, требует некоторых ухищрений. Twitter для Android–а может делать это вполне умело — та голубая птичка внизу главного меню чирикает вокруг себя три меняющиеся темы тенденций. Это новаторство само по себе, потому что прямо сейчас пользовательских интерфейсов (UI) для приложений Android–а весьма мало, но это новаторство покажет, что если вы действительно потрудитесь над Android–ом, вы можете получить пользовательский интерфейс того же качества, которое имеет iPhone в плане приложений.&lt;/p&gt;
						&lt;p&gt;Против:&lt;br /&gt;Доступность:&lt;br /&gt;Twitter для Android–а доступен только для пользователей с 2.1 OS (&amp;#201;clair), что, к сожалению, оставит без внимания фрагментированные версии Cupcake (v1.5 OS) и Donut (v1.6 OS). Это разочаровывает большинство пользователей, ибо пользователей тех версий больше (до выпуска обновлений).&lt;/p&gt;
						&lt;p&gt;HootSuite (платный и бесплатный)&lt;/p&gt;
						&lt;p&gt;Цена: 2,99 $ США&lt;/p&gt;
						&lt;p&gt;HootSuite&lt;/p&gt;
						&lt;p&gt;Это следующая наилучшая вещь для пользователей, не заинтересованных в официальном приложении для Twitter–а. Hootsuite позволяет пользователям управлять множественными аккаунтами в Twitter–е, а также может быть панелью управления для многих социальных служб. Таких как Facebook, MySpace, LinkedIn и Foursquare. Но Hootsuite может быть использован для менее используемых сетей или узлов связи — таких как Ping.fm и WordPress (только что добавили). Так как Hootsuite создан для Android–а, у него сокращённый интерфейс, и теперь пользователь способен лишь размещать записи. Этот клиент доступен для всех версий Android–а, включительно с последней сборкой FroYo. Также заметьте, что в бесплатной версии нет статистики и она ограничена 3 аккаунтами. Вот взгляд на хорошие и плохие стороны:&lt;/p&gt;
						&lt;p&gt;За:&lt;br /&gt;Расписание:&lt;br /&gt;Способность размещать записи в определённые моменты на протяжении дня без надобности каждый раз входить в Twitter очень желательна. Если пользователь знает, что он будет делать в определённые моменты дня, составление расписания обновлений в те моменты — великолепный способ не отвлекать его от дел. Всё, что надо делать, это клацнуть по верхней кнопке справа, а потом на кнопке календаря и назначить день и время.&lt;/p&gt;
						&lt;p&gt;Слежение за статистикой:&lt;br /&gt;Наилучшая функция в HootSuite — слежение за статистикой, тесно интегрированное с клиентом. Находясь на главной странице, нажмите на кнопку меню и клацните на «Stats» — вы увидите количество клацаний на размещённой вами записи (при условии, что вы разместили ссылку в вашем посте/теме), а также рейтинг вашей записи на основе количества клацаний на ней. Очень полезно следить за тем, сколько клацаний получает ваша запись и какие ключевые слова были использованы для получения такого количества клацаний на посте.&lt;/p&gt;
						&lt;p&gt;Против:&lt;br /&gt;Очень болтлив:&lt;br /&gt;При загрузке этот конкретный клиент загрузит все ваши аккаунты и списки и попытается обновить их всех сразу. Это может привести к очень медленному началу ваших записей в течение дня. Также отмечу, что если у вас есть менеджер задач, лучше не выходить полностью из HootSuite, чтобы избежать долгого запуска при каждом нажатии на иконку. Наилучший способ для этого — использовать средства многозадачности Android–а для запуска приложения, потом делать что–то другое на телефоне, пока оно загружается.&lt;/p&gt;
						&lt;p&gt;Twidroyd (платный и бесплатный)&lt;/p&gt;
						&lt;p&gt;Цена: 3,99 $ США&lt;/p&gt;
						&lt;p&gt;Twidroyd&lt;/p&gt;
						&lt;p&gt;При том, что Twitroid называют “industries standard twitter client” («клиентом–стандартом Twitter–а»), этому приложению надо много чего оправдать. Такие большие надежды вызваны широкой базой функций, включающей, но не ограниченной, способность добавлять плагины (расширения), редактировать списки, хорошей поддержкой bit.ly и tweet outbox на тот случай, если вы отключены от сети. Эти функции будут доступны лишь в платной версии, но цена действительно будет стоить их только ради возможности использовать плагины. Похоже, что у этого клиента много чего предложить. Потому давайте посмотрим на хорошие и плохие стороны.&lt;/p&gt;
						&lt;p&gt;За:&lt;br /&gt;Схема:&lt;br /&gt;Twidroyd/Twitroid имеет упрощённый интерфейс, и легко научиться работе с ним. Кнопки легко узнаваемы, они не вынуждают вас гадать, куда же вы идёте. Это тот тип приложения, который станет использовать и опытный пользователь, и рядовой светский человек. Его сильнейший набор легко адаптируется к любому пользователю — если обычному пользователю не нужны все дополнения, ему не нужно использовать их, но они есть на тот случай, если понадобятся опытному пользователю.&lt;/p&gt;
						&lt;p&gt;Плагины:&lt;br /&gt;Хотите расширить свой twitter–клиент, чтобы мочь видеть своё положение на карте либо чтобы он читал вам уведомления? Twidroyd/Twitroid будет делать это! Это изумительная функция, потому что она оставляет платформу открытой для новаторства, которое, если я точно помню, является концепцией Android–а у Google.&lt;/p&gt;
						&lt;p&gt;Против:&lt;br /&gt;Бесплатные функции:&lt;br /&gt;Независимо от того, каким мерилом вы будете мерить эту программу, она замечательна. Но нет бесплатных функций. Поддержка bit.ly была бы очень полезна, так как она позволяет следить за статистикой клацаний на ваших постах, но, к сожалению, она не будет доступна в бесплатной версии. Возможно, в будущих версиях, когда они добавят ещё функции, они добавят несколько и в бесплатную версию, чтобы сказать, что они думали о вас!&lt;/p&gt;
						&lt;p&gt;Seesmic (Бесплатный)&lt;/p&gt;
						&lt;p&gt;Seesmic&lt;/p&gt;
						&lt;p&gt;Seesmic сосредоточил свои усилия на всём наборе инструментов, которые могут вытягивать информацию со многих сайтов социальных служб и представлять её вам в легко читаемом формате. Эти инструменты имеют в ОС Android в виде более минималистического, но чётко очерченного интерфейса. В отличие от версии для iPhone, клиент для Android–а может лишь добавлять имена пользователей Twitter–а, отмечать профили Facebook и делать другие подобные вещи. Всё представлено прямым образом вверху экрана, с возможностью переключаться между шкалой времени, ответами, посланиями и вашим профилем. При нажатии на красную полоску вверху страницы, когда вы где–угодно на странице кроме верха, экран передвинется к начальной позиции.&lt;/p&gt;
						&lt;p&gt;За:&lt;br /&gt;Функциональность:&lt;br /&gt;В Seesmic возможность создать пост/тему такая же лёгкая, как нажатие кнопки меню. При создании записи возможность прикрепить картинку либо видео и загрузить её на tweetphoto.com — вебсайт обмена фалами в социальных службах в реальном времени — такая же простая, как снятие снимка, остальное будет сделано за вас. Прикрепление своего местонахождения стало таким простым — надо нажать иконку местонахождения и выбрать из прикрепления своего местонахождения, прикрепления ссылки на карту местонахождения либо адреса местонахождения. Последнее, но не наименее важное — автоматический сократитель URL, который очень удобен для создания множественных ссылок на одну запись (кому–нибудь поспамить?). Возможность делать всё это с композиционного экрана — потрясающая функция, и её быстро внедряют в клиенты, но Seesmic был среди первых отважившихся.&lt;/p&gt;
						&lt;p&gt;Против:&lt;br /&gt;Поддержка плагинов:&lt;br /&gt;При том, что клиенты начинают использовать плагины для своих изделий, этот клиент их не использует. Seesmic тоже стоит посмотреть на то, что делают такие клиенты как Twidroyd, и строить, улучшать то, что захотят, и придумать новые способы использования плагинов. Кто знает, возможно в конце концов мы увидим интеграцию Foursquare, TripIt, Glympse, Waze, Tungle.me и Yelp, как это предложил Роберт Скоубл в своём посте Location 2012: Death Of Information Silos («Местоположение 2012: Смерть информационных бункеров») [TechCrunch].&lt;/p&gt;
						&lt;p&gt;Touiteur (платный и бесплатный)&lt;/p&gt;
						&lt;p&gt;Цена: 1,99 евро&lt;/p&gt;
						&lt;p&gt;Touiteur &lt;/p&gt;
						&lt;p&gt;Пусть вас не сводит с толку французское название — сегодня этот клиент среди лучших клиентов Twitter–а. Его разработала LevelUp Studio. Он имеет чистый, красивый интерфейс, который может соперничать с классическим пользовательским интерфейсом (UI) iPhone. Это приложение выпущено в бесплатной и платной версиях. Потому надо иметь в виду, что вы хотите от этого клиента. Платная версия будет открывать/разблокировать? множественные аккаунты, 3 (бесплатные, а не три?) виджета для домашнего экрана, сокращение URL, производить настройку по желанию пользователя и просматривать вебстраницы и комплексные представления информации внутри программы.&lt;/p&gt;
						&lt;p&gt;За:&lt;br /&gt;Пользовательский интерфейс:&lt;br /&gt;Наверное, у Touiteur–а лучший интерфейс из интерфейсов Android–а, которые мы видели на сегодня. Полная простота и стиль вызывают в памяти пользовательский интерфейс iPhone. Если вы любитель интерфейса iPhone, этот клиент для вас. Всё выложено внизу экрана, а скорость зависит от количества аккаунтов, которые вы имеете.&lt;/p&gt;
						&lt;p&gt;Мастерство:&lt;br /&gt;При нажатии на записи страница расширяется и у вас появляется выбор ряда опций, включающих ответы, ссылки, пересылку, послания и другие. Эту функцию наверняка стоит упомянуть, потому что она не уводит вас от записей под рукой, но всё же позволяет делать то, что вы хотите со той записью, в отличие от других клиентов, в которых вам надо просмотреть запись отдельно прежде, чем произвести какое–то действие.&lt;/p&gt;
						&lt;p&gt;Против:&lt;br /&gt;Бесплатные виджеты:&lt;br /&gt;Отсутствие бесплатных виджетов для свободных пользователей разочаровывает. Должен быть хотя бы один для пользователя, не желающего тратить 1,99 евро на приложение для Twitter–а.&lt;/p&gt;
						&lt;p&gt;Вывод:&lt;br /&gt;Twitter становится неотъёмлемой частью набора социальных сетей, и как таковой должен иметь несколько бесплатных (мы надеемся), первоклассных клиентов для Android–а. Android поддерживает виджеты, большинство приложений предоставляют вам виджеты для упрощения способа доступа к информации и получения её. У некоторых из этих клиентов уже есть такая способность, а потому интегрируют её позже (мы надеемся!). эти клиенты объединяют среднего светского человека с опытными пользователями Twitter–а, и как таковых их признали многие. Многие люди отметят, что у некоторых клиентов лишь одно либо два за и против. Причина этого то, что большинство функций общие в любом клиенте для Twitter–а. Эти же сосредоточены на том, что выделяет их. Эти пять, которые описаны здесь, не единственные хорошие. Наверняка, появятся новые. Но эти — сливки из имеющихся теперь.&lt;/p&gt;</description>
			<author>mybb@mybb.ru (Gates)</author>
			<pubDate>Wed, 14 Aug 2013 11:52:12 +0400</pubDate>
			<guid>http://programir.mybb.ru/viewtopic.php?pid=30#p30</guid>
		</item>
		<item>
			<title>Мое JS1K демо: шаг за шагом</title>
			<link>http://programir.mybb.ru/viewtopic.php?pid=29#p29</link>
			<description>&lt;p&gt;Если вы еще не в курсе, посетите сайт соревнования по JS1K демо. В конкурсе победит разработчик, создавший самое лучшее демо, вместив его в 1 килобайт JavaScript кода. Конечно же, я не смог устоять против соблазна попробовать сделать нечто подобное самому, так что я достал свой набор хитростей с проекта по визуализации музыки в Winamp, и начал писать код. И я остался очень доволен результатом. И нет, эта вещь не будет работать на Internet Explorer.&lt;/p&gt;
						&lt;p&gt;Исходная версия&lt;/p&gt;
						&lt;p&gt;ОстановитьИсходный код&lt;/p&gt;
						&lt;p&gt;Я только переписал код демо, добавил объемные лучи света, и по-прежнему вложился в 1К кода:&lt;/p&gt;
						&lt;p&gt;Новая улучшенная версия&lt;/p&gt;
						&lt;p&gt;ОстановитьИсходный код&lt;br /&gt;(музыка не является частью демки, но так смотреть приятнее)&lt;/p&gt;
						&lt;p&gt;Итак, когда размер имеет значение, лучший способ сделать программу маленькой – генерировать необходимые данные с помощью процедур. Такой способ позволяет значительно экономить на объеме кода. С первого взгляда задача кажется сложной и почти невозможной, однако на деле все сводится к умному использованию математики (из высшей школы). И, как это часто бывает, самые лучшие фокусы – самые простые, поскольку описываются минимальным количеством кода.&lt;/p&gt;
						&lt;p&gt;Чтобы продемонстрировать все вышесказанное на деле, я разложу по полочкам мое демо и покажу все главные моменты и хитрости, которые я задействовал в нем. В отличие от исходного кода моего однокилобайтного демо, представленные здесь фрагменты будут отформатированы более свободно, имена переменных будут отражать их назначение.&lt;/p&gt;
						&lt;p&gt;Инициализация&lt;/p&gt;
						&lt;p&gt;Правила JS1K позволяют использовать тег Canvas, так что первый фрагмент кода инициализирует его и производит заливку окна.&lt;/p&gt;
						&lt;p&gt;Далее просто формируются фреймы демо. Этот процесс состоит из четырех основных частей:&lt;/p&gt;
						&lt;p&gt;Анимация проводков&lt;br /&gt;Вращение проводков и проекция согласно положению камеры&lt;br /&gt;Закраска проводков&lt;br /&gt;Движение камеры&lt;br /&gt;Все эти операции повторяются 30 раз в секунду, с помощью таймера&lt;/p&gt;
						&lt;p&gt;view plaincopy to clipboardprint?&lt;br /&gt;setInterval:setInterval(function () { ... }, 33);&amp;#160; &lt;br /&gt;Рисуем проводки&lt;/p&gt;
						&lt;p&gt;Самый заметный фокус из используемых здесь состоит в том, что все демо рисуется одним единственным примитивом с использованием двух переменных: отрезок прямой переменного цвета и ширина штриха. Такой способ сводит весь процесс рисования к двум компактным, вложенным циклам. Каждая внутренняя итерация рисует новый отрезок проводка с того места, где кончился предыдущий, а внешняя итерация отвечает за последовательную дорисовку разных проводков.&lt;/p&gt;
						&lt;p&gt;Вдобавок линии переплетаются друг с другом с помощью встроенного режима наложения &#039;lighten&#039;, который позволяет рисовать линии проводков в любом направлении. Такой режим позволяет избежать ручной сортировки порядка изображения проводков.&lt;/p&gt;
						&lt;p&gt;Чтобы упростить преобразования связанные с перспективой, я использую систему координат, в которой точка (0, 0) помещена в центр картинки и колеблется от -1 до 1 в обоих направлениях. Это компактный и удобный способ решения проблемы разных размеров окна без лишнего кода:&lt;/p&gt;
						&lt;p&gt;view plaincopy to clipboardprint?&lt;br /&gt;with (graphics) {&amp;#160; &lt;br /&gt;&amp;#160; ratio = width / height;&amp;#160; &lt;br /&gt;&amp;#160; globalCompositeOperation = &#039;lighter&#039;;&amp;#160; &lt;br /&gt;&amp;#160; scale(width / 2 / ratio, height / 2);&amp;#160; &lt;br /&gt;&amp;#160; translate(ratio, 1);&amp;#160; &lt;br /&gt;&amp;#160; lineWidthFactor = 45 / height;&amp;#160; &lt;br /&gt;&amp;#160; ...&amp;#160; &lt;br /&gt;Я также добавляю корректировку пропорций (ratio) для неквадратных окон и вычисляю ширину оси координат с помощью lineWidthFactor на потом.Далее идет два вложенных цикла for: один проходится по всем проводам, второй – отвечает за построение каждого отдельного проводка. Псевдо-код этих циклов имеет вид:&lt;/p&gt;
						&lt;p&gt;view plaincopy to clipboardprint?&lt;br /&gt;For (12 wires =&amp;gt; wireIndex) {&amp;#160; &lt;br /&gt;&amp;#160; Начинаем новый проводок&amp;#160; &lt;br /&gt;&amp;#160; For (45 точек вдоль каждого проводка =&amp;gt; pointIndex) {&amp;#160; &lt;br /&gt;&amp;#160; &amp;#160; Вычисляем путь точки в пространстве: (x,y,z)&amp;#160; &lt;br /&gt;&amp;#160; &amp;#160; Двигаем виток наружу: (x,y,z)&amp;#160; &lt;br /&gt;&amp;#160; &amp;#160; Двигаем/Вращаем согласно углу камеры: (x2,y2,z2) - (x3,y3,z3)&amp;#160; &lt;br /&gt;&amp;#160; &amp;#160; Проектируем в 2D: (x,y)&amp;#160; &lt;br /&gt;&amp;#160; &amp;#160; Определяем цвет, ширину и яркость этого сегмента: (r,g,b)&amp;#160; &lt;br /&gt;&amp;#160; &amp;#160; If (эта точка находится перед камерой) {&amp;#160; &lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; If (последняя точка была видна){&amp;#160; &lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; Нарисовать отрезок прямой, начиная с последней точки и до (x,y)&amp;#160; &lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; }&amp;#160; &lt;br /&gt;&amp;#160; &amp;#160; }&amp;#160; &lt;br /&gt;&amp;#160; &amp;#160; else {&amp;#160; &lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; Пометить эту точку как невидимую&amp;#160; &lt;br /&gt;&amp;#160; &amp;#160; }&amp;#160; &lt;br /&gt;&amp;#160; &amp;#160; Зафиксировать начало нового отрезка на (x,y)&amp;#160; &lt;br /&gt;&amp;#160; }}&amp;#160; &lt;br /&gt; &lt;/p&gt;
						&lt;p&gt;Математические извороты&lt;/p&gt;
						&lt;p&gt;Для генерации проводков, я начну с формулы, которая формирует извилистую линию на сфере, с помощью широты/долготы (latitude/longitude). Выглядит это так:&lt;/p&gt;
						&lt;p&gt;view plaincopy to clipboardprint?&lt;br /&gt;offset = time - pointIndex * 0.03 - wireIndex * 3;&amp;#160; &lt;br /&gt;longitude = cos(offset + sin(offset * 0.31)) * 2&amp;#160; &lt;br /&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; + sin(offset * 0.83) * 3 + offset * 0.02;&amp;#160; &lt;br /&gt;latitude = sin(offset * 0.7) - cos(3 + offset * 0.23) * 3;&amp;#160; &lt;br /&gt;Это классика процедурного кодинга в своем лучшем виде: берем зависимую от времени переменную offset и произвольно изменяем ее с помощью таких общедоступных функций, как косинус и синус. Экспериментируйте с ней, пока не получите то, что надо. Это очень простой способ создания интересных, выглядящих естественно образов.&lt;/p&gt;
						&lt;p&gt;Но, так как это определенно больше искусство, чем наука, предлагаю пару способов украсить подобные конструкции, сделать их более привлекательными.&lt;/p&gt;
						&lt;p&gt;Во-первых, всегда включайте в расчеты какие-то нелинейные комбинации переменных, таких как sin() внутри cos(). Этот простой прием экспоненциально увеличивает сложность каждого компонента изображения. В нашем случае, с помощью него регулярные колебания превращаются в зависящие от времени частоты.&lt;/p&gt;
						&lt;p&gt;Во-вторых, всегда масштабируйте периоды колебаний, используя простые числа. Так как простые числа не имеют общих множителей, их использование предотвращает возникновение точного повторения всех отдельных периодов. Математически, наименьшее общее кратное выбранных периодов просто огромно (414253 пунктов ~ 4.8 часа). Начертив график долготы/широты для offset = 0..600, вы получите:&lt;br /&gt;&lt;/p&gt;
						&lt;p&gt;График выглядит как произвольно спутанная кривая, без видимой структуры, что создает видимость никогда не повторяющихся движений. Однако если вы уменьшите каждую константу до&amp;#160; одной значащей цифры (к примеру, 0.31 -&amp;gt; 0.3, 0.83 -&amp;gt; 0.8), случайные повторения уже будут выглядеть не такими случайными:&lt;br /&gt;&lt;/p&gt;
						&lt;p&gt;Это потому, что наименьшее общее кратное уменьшилось до 84 пунктов ~ 3.5 секунды. Учтите, что обе формулы одинаковы по сложности кода, но дают совершенно разные результаты.&lt;/p&gt;
						&lt;p&gt;Экструзия&lt;/p&gt;
						&lt;p&gt;Имея заданные концы каждого из проводков, я могу сгенерировать оставшуюся часть проводка, просто размахивая его хвостом позади него, с задержкой во времени. Вот почему pointIndex стоит со знаком «-» в формуле, приведенной выше. В то же время я двигаю точки к наружному краю, чтобы сформировать длинные хвосты проводков.&lt;/p&gt;
						&lt;p&gt;Мне также надо перейти от широты/долготы к стандартному трехмерному пространству XYZ, что реализуется с помощью преобразований в сферических координатах:&lt;br /&gt;&lt;/p&gt;
						&lt;p&gt;view plaincopy to clipboardprint?&lt;br /&gt;distance = f.sqrt(pointIndex+.2);&amp;#160; &lt;br /&gt;x = cos(longitude) * cos(latitude) * distance;&amp;#160; &lt;br /&gt;y = sin(longitude) * cos(latitude) * distance;&amp;#160; &lt;br /&gt;z = sin(latitude) * distance;&amp;#160; &lt;br /&gt;Вы наверняка заметили, что вместо того, чтобы сделать distance линейной функцией от&amp;#160; pointIndex, я применил квадратный корень. Это еще один простой процедурный трюк, выполняющий важную визуальную функцию. Вот как выглядит квадратный корень (сплошная прямая):&lt;br /&gt;&lt;/p&gt;
						&lt;p&gt;Пунктирная прямая отображает производную от квадратного корня, то есть определяет наклон сплошной кривой. Поскольку наклон уменьшается с увеличением расстояния, этот прием уменьшает движение проводка наружу: чем дальше заходит его конец, тем медленнее движется сам проводок. На практике этот эффект работает так: проводки более подвижны в центре и более вялые снаружи. Так визуальный эффект становится более привлекательным.&lt;/p&gt;
						&lt;p&gt;Вращение и проекция&lt;/p&gt;
						&lt;p&gt;Когда у меня появились абсолютные трехмерные координаты для точки проводка, мне нужно интерпретировать ее в соответствии с положением камеры. Это реализуется путем перемещения начала в точку положения камеры (X,Y,Z), и применением двух ротаций: одна по вертикали (yaw) и вторая по горизонтали (pitch):&lt;/p&gt;
						&lt;p&gt;view plaincopy to clipboardprint?&lt;br /&gt;x -= X; y -= Y; z -= Z;&amp;#160; &lt;br /&gt;&amp;#160; &lt;br /&gt;x2 = x * cos(yaw) + z * sin(yaw);&amp;#160; &lt;br /&gt;y2 = y;&amp;#160; &lt;br /&gt;z2 = z * cos(yaw) - x * sin(yaw);&amp;#160; &lt;br /&gt;&amp;#160; &lt;br /&gt;x3 = x2;&amp;#160; &lt;br /&gt;y3 = y2 * cos(pitch) + z2 * sin(pitch);&amp;#160; &lt;br /&gt;z3 = z2 * cos(pitch) - y2 * sin(pitch);&amp;#160; &lt;br /&gt;Координаты относительно камеры проектируются в перспективе путем деления на Z — чем дальше объект, тем он меньше. Отрезки линий с отрицательной координатой Z расположены за камерой и не должны прорисовываться. Ширина линий также масштабируется пропорционально расстоянию от камеры, и первый отрезок каждого проводка рисуются толще. Выглядит все это следующим образом:&lt;/p&gt;
						&lt;p&gt;view plaincopy to clipboardprint?&lt;br /&gt;plug = !pointIndex;&amp;#160; &lt;br /&gt;lineWidth = lineWidthFactor * (2 + plug) / z3;&amp;#160; &lt;br /&gt;x = x3 / z3;&amp;#160; &lt;br /&gt;y = y3 / z3;&amp;#160; &lt;br /&gt;&amp;#160; &lt;br /&gt;lineTo(x, y);&amp;#160; &lt;br /&gt;if (z3 &amp;gt; 0.1) {&amp;#160; &lt;br /&gt;&amp;#160; if (lastPointVisible) {&amp;#160; &lt;br /&gt;&amp;#160; &amp;#160; stroke();&amp;#160; &lt;br /&gt;&amp;#160; }&amp;#160; &lt;br /&gt;&amp;#160; else {&amp;#160; &lt;br /&gt;&amp;#160; &amp;#160; lastPointVisible = true;&amp;#160; &lt;br /&gt;&amp;#160; }&amp;#160; &lt;br /&gt;}&amp;#160; &lt;br /&gt;else {&amp;#160; &lt;br /&gt;&amp;#160; lastPointVisible = false;&amp;#160; &lt;br /&gt;}&amp;#160; &lt;br /&gt;beginPath();&amp;#160; &lt;br /&gt;moveTo(x, y);&amp;#160; &lt;br /&gt;Окрашивание&lt;/p&gt;
						&lt;p&gt;Каждый отрезок проводков требует соответствующего окраса.&amp;#160; Методом проб и ошибок я таки отыскал простую формулу, которая делает то, что нам нужно. Они использует синусоидальную волну для плавной смены яркости R G B каналов попеременно (то в большую, то в меньшую стороны), а также медленно меняет R-компоненту с течением времени. В результате получается приятная, не перенасыщенная палитра.&lt;/p&gt;
						&lt;p&gt;view plaincopy to clipboardprint?&lt;br /&gt;pulse = max(0, sin(time * 6 - pointIndex / 8) - 0.95) * 70;&amp;#160; &lt;br /&gt;luminance = round(45 - pointIndex) * (1 + plug + pulse);&amp;#160; &lt;br /&gt;strokeStyle=&#039;rgb(&#039; +&amp;#160; &amp;#160;&lt;br /&gt;&amp;#160; round(luminance * (sin(plug + wireIndex + time * 0.15) + 1)) + &#039;,&#039; +&amp;#160; &amp;#160;&lt;br /&gt;&amp;#160; round(luminance * (plug + sin(wireIndex - 1) + 1)) + &#039;,&#039; +&amp;#160; &lt;br /&gt;&amp;#160; round(luminance * (plug + sin(wireIndex - 1.3) + 1)) +&amp;#160; &lt;br /&gt;&amp;#160; &#039;)&#039;;&amp;#160; &lt;br /&gt;В данном случае pulse вызывает яркие пульсации вдоль проводков. Я начал со стандартной синусоидальной волны по всей длине проводка, но&amp;#160; отсек все, кроме 5% каждой верхушки, чтобы добиться разреженной последовательности импульсов:&lt;br /&gt;&lt;/p&gt;
						&lt;p&gt;Движение камеры&lt;/p&gt;
						&lt;p&gt;Когда основная часть визуального образа уже сделана, мой запас кода практически иссяк, осталось только немного места для камеры. Мне нужен простой способ создать видимость движения координат камеры X, Y и Z. Я прибег к простейшему техническому приему, используя для этой цели повторную интерполяцию. Вот как она выглядит:&lt;/p&gt;
						&lt;p&gt;view plaincopy to clipboardprint?&lt;br /&gt;sample += (target - sample) * fraction&amp;#160; &lt;br /&gt;target задается произвольно. Далее, с каждым новым фреймом, sample плавно приближается к значению target благодаря умножению на дробь (к примеру, на 0.1). Данная операция превращает&amp;#160; sample в сглаженную версию target. Технически, это однополюсный фильтр нижних частот.&lt;/p&gt;
						&lt;p&gt;Будет лучше, если дважды повторить эту операцию, так чтобы и промежуточное значение (intermediate) тоже интерполировалось:&lt;/p&gt;
						&lt;p&gt;view plaincopy to clipboardprint?&lt;br /&gt;intermediate += (target - intermediate) * fraction&amp;#160; &lt;br /&gt;sample += (intermediate - sample) * fraction&amp;#160; &lt;br /&gt;Вот как кривые выглядят на графике:&lt;br /&gt;&lt;/p&gt;
						&lt;p&gt;Вы видите, что с каждой повторной интерполяцией разрывность между кривыми уменьшается. Сначала, скачки преобразуются в загибы. Затем уже загибы превращаются в мягкие выпуклости.&lt;/p&gt;
						&lt;p&gt;В моем демо этот принцип применяется отдельно для трех координат камеры. Каждые ~2.5 секунды выбирается новое целевое положение (target):&lt;/p&gt;
						&lt;p&gt;view plaincopy to clipboardprint?&lt;br /&gt;if (frames++ &amp;gt; 70) {&amp;#160; &lt;br /&gt;&amp;#160; Xt = random() * 18 - 9;&amp;#160; &lt;br /&gt;&amp;#160; Yt = random() * 18 - 9;&amp;#160; &lt;br /&gt;&amp;#160; Zt = random() * 18 - 9;&amp;#160; &lt;br /&gt;&amp;#160; frames = 0;&amp;#160; &lt;br /&gt;}&amp;#160; &lt;br /&gt;&amp;#160; &lt;br /&gt;function interpolate(a,b) {&amp;#160; &lt;br /&gt;&amp;#160; return a + (b-a) * 0.04;&amp;#160; &lt;br /&gt;}&amp;#160; &lt;br /&gt;&amp;#160; &lt;br /&gt;Xi = interpolate(Xi, Xt);&amp;#160; &lt;br /&gt;Yi = interpolate(Yi, Yt);&amp;#160; &lt;br /&gt;Zi = interpolate(Zi, Zt);&amp;#160; &lt;br /&gt;&amp;#160; &lt;br /&gt;X&amp;#160; = interpolate(X,&amp;#160; Xi);&amp;#160; &lt;br /&gt;Y&amp;#160; = interpolate(Y,&amp;#160; Yi);&amp;#160; &lt;br /&gt;Z&amp;#160; = interpolate(Z,&amp;#160; Zi);&amp;#160; &lt;br /&gt;Результирующая траектория становится очень плавной и выглядит довольно динамично.&lt;/p&gt;
						&lt;p&gt;Вращение камеры&lt;/p&gt;
						&lt;p&gt;Последний этап – правильное ориентирование камеры. Проще всего будет поместить камеру прямо в центр объекта и вычислить значения вертикальной (yaw) и горизонтальной (pitch) ротаций непосредственно из позиции камеры (X,Y,Z):&lt;/p&gt;
						&lt;p&gt;view plaincopy to clipboardprint?&lt;br /&gt;yaw&amp;#160; &amp;#160;= atan2(Z, -X);&amp;#160; &lt;br /&gt;pitch = atan2(Y, sqrt(X * X + Z * Z));&amp;#160; &lt;br /&gt;В таком случае демо получается чересчур статичным, выглядит очень неестественно. Лучше всего оставить камере немного свободного пространства&amp;#160; для движения вокруг заданной позиции.&lt;/p&gt;
						&lt;p&gt;К сожалению, лимит в 1 килобайт слишком мал, и у меня не хватило места для парочки «волшебных» формул или интерполяций. Поэтому, я просто заменил вышеуказанные формулы на вот эти:&lt;/p&gt;
						&lt;p&gt;view plaincopy to clipboardprint?&lt;br /&gt;yaw&amp;#160; &amp;#160;= atan2(Z, -X * 2);&amp;#160; &lt;br /&gt;pitch = atan2(Y * 2, sqrt(X * X + Z * Z));&amp;#160; &lt;br /&gt;С умнощением переменных на 2, формула становится «неправильно», выскакивают ошибки при значении в 45 градусов. Однако это ограничение не портит общей картины, а даже наоборот. В итоге, я заставил камеру двигаться более плавно и медленно, достигнув отличной динамики всего за 4 лишних байта!&lt;/p&gt;
						&lt;p&gt;Заключение&lt;/p&gt;
						&lt;p&gt;Я уже давно являюсь поклонником визуальных демо, с теплотой вспоминаю мое знакомство с этим жанром: в 1993 году я посмотрел легендарный ролик&amp;#160; Second Reality, с этого все и началось. Именно математику я считаю тем инструментом, который обязательно необходимо освоить и активно использовать в создании визуальных образов.&lt;/p&gt;
						&lt;p&gt;Этим постом я надеялся вдохновить вас на поиски простых математических решений, которые дают удивительные результаты.&lt;/p&gt;</description>
			<author>mybb@mybb.ru (Gates)</author>
			<pubDate>Wed, 14 Aug 2013 11:50:27 +0400</pubDate>
			<guid>http://programir.mybb.ru/viewtopic.php?pid=29#p29</guid>
		</item>
		<item>
			<title>Разработка под Android – Часть 1: Hello World</title>
			<link>http://programir.mybb.ru/viewtopic.php?pid=28#p28</link>
			<description>&lt;p&gt;Эта статья посвящена разработке простой программы языке Java для ОС Android, которая выводит на экран слова «hello world». Совсем недавно Google представил новую среду программирования App Inventor, предназначенную для новичков. Я, к сожалению, не успел ее опробовать, поэтому мы воспользуемся старой проверенной средой Eclipse IDE, с которой вы уже познакомились в одном из предыдущих постов. В создании этого проекта с текстовым интерфейсом есть два важных момента. Процесс будет включать в себя разработку XML-файлов (1) и Java-файла (2), а также подготовку проекта к запуску. Требования к проекту просты: знание основ XML (если вы новичок и не знаете основ XML, ничего страшного, обучитесь в процессе), а также знание языка Java (самый базис, вы улучшите свои знания в ходе разработки нашего проекта, так как нам придется повозиться с фрагментами Java-кода).&lt;/p&gt;
						&lt;p&gt;Eclipse&lt;/p&gt;
						&lt;p&gt;Начнем с запуска Eclipse, кликаем меню File, New и затем Project. Выбираем папку Android, в ней выбираем Android Project, и идем дальше (кликаем next). Если вы корректно обновили свой Android SDK, на экране вы увидите что-то вроде этого. В этом появившемся окне от нас требуется ввести корректные данные в графах Project Name (название проекта), Build Target (версия билда), Application Name (название приложения), Package Name(название пакета), поставить галочку и заполнить поле Create Activity, и указать Min SDK Version (минимально допустимая версия SDK).Эти важные термины нужно знать, потому что они определяют будущий вид и возможности вашего приложения. Вот краткие описания этих граф:&lt;/p&gt;
						&lt;p&gt;Project Name: Здесь нужно указать название нового проекта, какое бы вам хотелось дать. В нашем случае мы введем простое и понятное «HelloWorld» (в принципе, без разницы как вы его назовете у себя)&lt;br /&gt;Build Target: В этом поле указывается, какой API будет использоваться в вашем проекте. К выбору надо подойти с умом,&amp;#160; иначе можно потерять большое количество потенциальных пользователей будущего приложения. Всегда полезно продумать, что придется использовать в программе и проверять, поддерживает ли нужные функции билд более низкой версии, перед тем как сделать выбор в пользу более поздней. Для этой программы мы выберем Android 1.5.&lt;br /&gt;Application Name: Ваша программа будет называться так, как вы укажете в этом поле. Название можно потом поменять, а пока мы назовем ее Hello World.&lt;br /&gt;Package name: Название пакета будет состоять из стандартного названия вашей компании, или, как в моем случае, я использовал название моего частного предприятия - com.gregrjacobs.helloworld. Вообще package name определяет уникальное название вашего проекта среди всех программ для ОС Android. Убедитесь, что вы вводите информацию в нижнем регистре, это хороший тон в программировании.&lt;br /&gt;Create Activity: Вы должны убедиться, что перед Create Activity стоит галочка, так как эта опция делает основную часть работы, вам нужно только дать ей название. ПРИМЕЧАНИЕ: не называйте ее «Activity»,&amp;#160; так как ваш проект не будет работать и придется заново проходить через заполнение или исправление всех полей. Я ввел HelloMain, а вы можете выбрать что-то другое.&lt;br /&gt;Min SDK Version: Введенное в этой графе число определяет, для какой версии платформы вы создаете свою программу и в каких устройствах оно будет поддерживаться, если доберется до Android Marketplace. После того, как вы указали Android 1.5 в Build Target , в поле Min SDK Version&amp;#160; будет стоять цифра 3. Это и есть наша минимальная версия SDK. После этого можно нажать кнопку Finish.&lt;br /&gt;Новый проект Android&lt;/p&gt;
						&lt;p&gt;Тем самым мы создадим ядро нашего проекта, начиная с Android 1.5 JAR файла и заканчивая файлами XML. Самые важные объекты сейчас – это файлы директории src/com.gregjacobs.helloworld/ и res/. Для начала просмотрим файл main.xml, который находится в res/layout/. Этот файл будет содержать макет вашего приложения, включая текстовые окна, метки, элементы dateTimePicker и так далее. В файле main.xml можно увидеть, что мы используем LinearLayout (линейный макет), который формирует элементы в одну сплошную строку или колонку. Иногда это может быть довольно удобный способ (но чаще не очень), и для этого примера он вполне подходит, так что его можно оставить без изменений, потому что при простом выводе текста особой разницы нет. Далее идет файл strings.xml, который содержит все строковые массивы, используемые в приложении. Это очень удобный способ держать весь символьный беспорядок вне файлов Java и main.xml. Нам нужно изменить строку под названием “hello” с Hello World, HelloMain! на Hello Android, Hello World! Строка под названием app_name&amp;#160; может остаться без изменений.&lt;/p&gt;
						&lt;p&gt;HelloWorld&lt;/p&gt;
						&lt;p&gt;После этого, используя навигационное меню слева, мы открываем вкладку с названием пакета (com.gregrjacobs.helloworld) и открываем файл HelloMain.java, в котором содержится код запуска XML-файлов, которые мы только что просматривали. Первая строка файла содержит название нашего пакета, относящееся к java файлу. Далее (см. картинку выше), мы импортировали комплект ОС и функцию, которая будет запускать приложение. Когда мы освоим добавление текстовых окон и меток, мы займемся импортом виджетов в наши файлы. Следующим в коде указывается наш класс (HelloMan), и макет пользовательского интерфейса, определенный с помощью аргумента R.layout.main. Можно создавать&amp;#160; множество разных макетов интерфейса, в зависимости от самого приложения. Для нашей программы вывода «hello world» достаточно этого одного макета, но вы можете легко поэкспериментировать с этой опцией.&lt;/p&gt;
						&lt;p&gt;Когда мы покончили с настройкой всего необходимого, наконец можно перейти к компоновке самого приложения и запуске его с помощью эмулятора, создание которого описано в предыдущем посте. Для этого вверху окна Eclipse находим кнопку с изображением&amp;#160; и нажимаем ее. Далее надо указать какой тип проекта мы собираемся запускать. Выбираем Android Application и жмем «OK».&lt;/p&gt;
						&lt;p&gt;Эмулятор загрузится всплывающим окном. Время первого запуска эмулятора будет довольно долгим, но потом с этим не будет никаких проблем. Когда эмулятор полностью запустится, он может попросить вас нажать Menu для разблокировки. Для этого нажмите на кнопку меню под экраном аппарата. Не надо ничего делать для запуска вашего приложения, все запустится автоматически. В конечном счете, вы получите что-то вроде этого:&lt;/p&gt;
						&lt;p&gt;Эмулятор Android&lt;/p&gt;
						&lt;p&gt;С использованием таких инструментов как Eclipse IDE и AndroidSDK создание простых текстовых программ не представляет никакой трудности. В следующем посте мы будем дорабатывать и усложнять нашу программку «Hello World». Если в сегодняшнем проекте у вас возникли трудности с выполнением тех или иных шагов, оставьте комментарий, и мы постараемся выяснить, что было не так. Если вам не терпится продвинуться вперед в разработке интерфейса под Android-приложение, или если вы хотите влезть в базы данных, вот две отличные ссылки для самообразования: Хранение данных и Пользовательский интерфейс. До следующего поста!&lt;/p&gt;</description>
			<author>mybb@mybb.ru (Gates)</author>
			<pubDate>Wed, 14 Aug 2013 11:49:53 +0400</pubDate>
			<guid>http://programir.mybb.ru/viewtopic.php?pid=28#p28</guid>
		</item>
		<item>
			<title>Разработка под Android - Часть 2: Графические элементы</title>
			<link>http://programir.mybb.ru/viewtopic.php?pid=27#p27</link>
			<description>&lt;p&gt;В этом обучающем посте мы продолжим работать над программой «hello world» с того места, где мы остановились в прошлый раз. На этот раз нам предстоит разработать графический пользовательский интерфейс (GUI) и toast. GUI будет состоять из кнопки, текстового окна и текстовой метки (указателя). &amp;quot;Toast&amp;quot; будет всплывать на экран при нажатии кнопки.&lt;/p&gt;
						&lt;p&gt;Многие спросят, что такое toast. Если говорить языком не-программистов, toast - это текстовое оповещение, которое в основном служит для отображения ошибки на экране (Я большой любитель использовать toast-ы вместо звуковых сигналов, так как они не такие назойливые). В этой статье мы будем использовать toast для отображения сообщения «Hello Greg» в нижней части экрана. После изучения этой статьи вы с легкостью сможете использовать toast-ы в различных командах, редактировать внешний вид нашей программы «hello world» и считывать текст из текстового поля для использования в toast-ах.&lt;/p&gt;
						&lt;p&gt;Начнем с копирования нашей текущей версии проекта Hello World, так чтобы мы могли экспериментировать с оригиналом, но иметь два отдельных проекта, чтобы видеть разницу. Для этого кликнем правой кнопкой мыши на корневой папке нашего проекта HelloWorld в правой панели (Navigation Explorer), выберем функцию копирования (не Copy Qualified Name) и нажмем на нее. Далее нужно правой кнопкой мышки кликнуть на пустом месте Navigation Explorer и нажать «Paste», т.е. вставить. Вас попросят дать новое имя для этого проекта, а также разрешить использовать местоположение по умолчанию. Мы назовем новый проект ImprovedHelloWorld, и оставим галочку напротив графы «use default location» (использовать местоположение по умолчанию). Нажмите OK, и новый проект будет создан из старого.&lt;/p&gt;
						&lt;p&gt;strings.xml&lt;/p&gt;
						&lt;p&gt;Первым нашим заданием будет изменение файла strings.xml, а именно добавление элемента под app_name.&amp;#160; Это производится простым копированием строки стринговой константы и ее вставки прямо под последним элементом &amp;lt;/string&amp;gt;. Изменим название нашего стрингового элемента на «press» и дадим ему значение Press Me!. Далее мы изменим содержание константы hello, поменяв текст на Enter Your Name Here: (введите ваще имя здесь:) вместо Hello Android, Hello World!. Все нужные изменения внесены, пора переходить к дизайну GUI (графического пользовательского интерфейса).&lt;/p&gt;
						&lt;p&gt;main.xml&lt;/p&gt;
						&lt;p&gt;Для этого перейдем к файлу main.xml и внесем необходимые изменения. Самый первым элементом в файле идет LinearLayout, который по сути создает пространство для добавления таких объектов, как текстовые окна, кнопки и т.д., и служит для форматирования вывода программы. Проще говоря, LinearLayout поставит все элементы друг за другом строго в одну колонку или в одну строку. Далее у нас идет метка TextView, пройдемся по всем ее параметрам. Android:layout_width и android:layout_height определяют, как будет вести себя объект, используемый в макете. Эти параметры имеют значения fill_parent или wrap_content. Fill_parent растягивает размер объекта так, чтобы он полностью заполнил экран по горизонтали или по вертикали. Wrap_content выполняет форматирование размера элемента по его содержимому, сжимает или наоборот, растягивает. Обе эти переменные могут использоваться во множестве различных объектов, таких как Layouts, Text Views, Text Boxes, Buttons и др. Android:text используется в определенных объектах (TextViews и TextBoxes) для отображения текста пользователю. На данный момент мы также отображаем пользователю текст, но вызываем его из файла strings.xml вместо того, чтобы вписывать его непосредственно в код программы. Чтобы сослаться на файл strings.xml, нужно только в скобках ввести @string/press, где press - это название переменной, содержащий нужный текст.&lt;/p&gt;
						&lt;p&gt;С терминами мы уже определились, поэтому перейдем к программированию метки, текстового окна и кнопки. Для этого просто добавим текстовое поле и кнопку по тому же принципу, как это сделали с меткой в string.xml. Чтобы добавить текстовое поле (Textbox), добавим новую строку под концом элемента &amp;lt;TextView /&amp;gt;. Для большей точности, я напечатаю сам код в строку и потом объясню, что он означает. Вот сам код: &amp;lt;EditText android:id=”@+id/helloName” android:layout_width=”fill_parent” android:layout_height=”wrap_content” /&amp;gt;.&amp;#160; EditText в данном случае и есть наше текстовое поле. Добавление @+id/ перед названием переменной даст возможность потом привязаться к .java файлу. Далее добавим код: &amp;lt;Button android:id=”@+id/go” android:layout_width=”fill_parent” android:layout_height=”wrap_content” android:text=”@string/press” /&amp;gt; прямо под окончанием описания EditText. Обратите внимание, что мы опять ссылаемся на string.xml и вызываем переменную, которая говорит «Press Me!» - этот текст будет на нашей кнопке. Мы создали первую часть макета, которая позволяет вводить текст в текстовое поле.&lt;/p&gt;
						&lt;p&gt;Следующий раздел будет изобиловать кодом, я приведу как можно больше скриншотов, чтобы помочь вам его понять. Нужно уловить главный принцип: когда нам нужно ссылаться на объект макета, его необходимо импортировать. Нам нужно добавить импорты для кнопки и текстового поля. Мы можем реализовать это простым добавлением этих строк в верхушку кода:&lt;/p&gt;
						&lt;p&gt;view plaincopy to clipboardprint?&lt;br /&gt;&amp;#160; &amp;#160;&lt;br /&gt;import android.widget.Button;&amp;#160; &lt;br /&gt;import android.widget.EditText;&amp;#160; &lt;br /&gt;После этого нам понадобится добавить еще несколько импортов. Первый нужен для добавления к кнопке события Listen, второй для нашего toast-а, третий - для возможности просмотра контекста приложения и последний - для управления макетом. Эти импорты нужно вставить в код после предыдущих, вот их код:&lt;/p&gt;
						&lt;p&gt;view plaincopy to clipboardprint?&lt;br /&gt;&amp;#160; &amp;#160;&lt;br /&gt;import android.view.View.OnClickListener;&amp;#160; &lt;br /&gt;import android.widget.Toast;&amp;#160; &lt;br /&gt;import android.content.Context;&amp;#160; &lt;br /&gt;import android.view.View;&amp;#160; &lt;br /&gt;Далее приступаем к программированию функций обработчика событий для нашей кнопки и onCreate, которые вызываются при запуске программы. Для простоты и лучшего понимания я приведу остаток кода и поясню его предназначение.&lt;/p&gt;
						&lt;p&gt;view plaincopy to clipboardprint?&lt;br /&gt;&amp;#160; &amp;#160;&lt;br /&gt;public class HelloMain extends Activity {&amp;#160; &lt;br /&gt;EditText helloName;&amp;#160; &lt;br /&gt;Мы создаем ссылку на наше текстовое поле перед всеми функциями, так чтобы объявить его один раз, но по необходимости вызывать несколько раз.&lt;/p&gt;
						&lt;p&gt;view plaincopy to clipboardprint?&lt;br /&gt;&amp;#160; &amp;#160;&lt;br /&gt;/** Вызывается при первом создании операции. */&amp;#160; &lt;br /&gt;@Override&amp;#160; &lt;br /&gt;public void onCreate(Bundle savedInstanceState)&amp;#160; &lt;br /&gt;{&amp;#160; &lt;br /&gt;super.onCreate(savedInstanceState);&amp;#160; &lt;br /&gt;setContentView(R.layout.main);&amp;#160; &lt;br /&gt;// Фиксирует кнопку из макета&amp;#160; &lt;br /&gt;Button button = (Button)findViewById(R.id.go);&amp;#160; &lt;br /&gt;// Регистрирует onClick (считыватель нажатий)&amp;#160; &lt;br /&gt;button.setOnClickListener(mAddListener);&amp;#160; &lt;br /&gt;}&amp;#160; &lt;br /&gt;Этот код фиксирует кнопку с макета, используя переменную. Мы также назначим ей обработчик событий onClick, который объявлен в десятой строке кода вверху. Ниже приведен код самого обработчика событий (Event Handler). После создания этой функции мы сможем считывать текст из текстового поля и отображать его вместе со статическим текстом.&lt;/p&gt;
						&lt;p&gt;view plaincopy to clipboardprint?&lt;br /&gt;&amp;#160; &amp;#160;&lt;br /&gt;// добавляем анонимную имплементацию класса OnClickListener&amp;#160; &lt;br /&gt;private OnClickListener mAddListener = new OnClickListener()&amp;#160; &lt;br /&gt;{&amp;#160; &lt;br /&gt;public void onClick(View v)&amp;#160; &lt;br /&gt;{&amp;#160; &lt;br /&gt;long id = 0;&amp;#160; &lt;br /&gt;// делает что-либо, когда нажимается кнопка&amp;#160; &lt;br /&gt;try&amp;#160; &lt;br /&gt;{&amp;#160; &lt;br /&gt;helloName = (EditText)findViewById(R.id.helloName);&amp;#160; &lt;br /&gt;Здесь мы описываем элемент текстового поля (TextBox), который мы объявили ранее, и включаем текстовое поле в макет поиском по идентификатору ID, который ему присвоили раньше.&lt;/p&gt;
						&lt;p&gt;view plaincopy to clipboardprint?&lt;br /&gt;&amp;#160; &amp;#160;&lt;br /&gt;Context context = getApplicationContext();&amp;#160; &lt;br /&gt;CharSequence text = &amp;quot;Hello &amp;quot; + helloName.getText() + &amp;quot;!&amp;quot;;&amp;#160; &lt;br /&gt;int duration = Toast.LENGTH_LONG;&amp;#160; &lt;br /&gt;Toast toast = Toast.makeText(context, text, duration);&amp;#160; &lt;br /&gt;toast.show();&amp;#160; &lt;br /&gt;Вышеуказанный фрагмент кода берет содержание контекста (Context) и добавляет его к нашему Toast-у вместе с динамическим текстом CharSequence и длиной, в которой он появится на экране. В нашем случае длина должна быть побольше. В целом Toast-ы более эффективны, чем всплывающие текстовые блоки, так как они меньше отвлекают пользования от основного процесса.&lt;/p&gt;
						&lt;p&gt;view plaincopy to clipboardprint?&lt;br /&gt;&amp;#160; &amp;#160;&lt;br /&gt;}&amp;#160; &lt;br /&gt;catch (Exception ex)&amp;#160; &lt;br /&gt;{&amp;#160; &lt;br /&gt;Context context = getApplicationContext();&amp;#160; &lt;br /&gt;CharSequence text = ex.toString() + &amp;quot;ID = &amp;quot; + id;&amp;#160; &lt;br /&gt;int duration = Toast.LENGTH_LONG;&amp;#160; &lt;br /&gt;Toast toast = Toast.makeText(context, text, duration);&amp;#160; &lt;br /&gt;toast.show();&amp;#160; &lt;br /&gt;}&amp;#160; &lt;br /&gt;}&amp;#160; &lt;br /&gt;};&amp;#160; &lt;br /&gt;}&amp;#160; &lt;br /&gt;Последнее, что мы делаем для этой функции - проверка на ошибки. Программа проверит наш код, и если в нем обнаружится ошибка, она будет показана вместе с описанием. В таких функциях, как эти, очень важно проверить все, чтобы не исправить ошибки до запуска программы. Обработчик ошибок словит все ошибки в коде и в удобной для пользователя форме их обозначит.&lt;/p&gt;
						&lt;p&gt;Верхняя половина кода:&lt;br /&gt;Код раз&lt;br /&gt;Нижняя половина кода, продолжение предыдущего фрагмента:&lt;br /&gt;Код два&lt;/p&gt;
						&lt;p&gt;После того, как мы прописали код для нашего файла .java, можем запустить программу и посмотреть на наш улучшенный вариант программы «Hello World». Обратите внимание, когда в текстовом поле нет текста, и вы нажимаете кнопку, программа все равно работает корректно. Пользователя не засыпает текстовыми сообщениями об ошибках. Конечный продукт наших трудов выглядит примерно так:&lt;/p&gt;
						&lt;p&gt;Результат&lt;/p&gt;
						&lt;p&gt;На этом программирование нашего примера Hello World заканчивается, но до конца изучения темы разработок под Android еще далеко. В следующем посте мы будем изучать базы данных, некоторые простые запросы, а также построение БД с нуля. Как всегда, если у вас возникнут проблемы с кодом, оставляйте свои комментарии, общими силами мы решим эти проблемы.&lt;/p&gt;</description>
			<author>mybb@mybb.ru (Gates)</author>
			<pubDate>Wed, 14 Aug 2013 11:48:57 +0400</pubDate>
			<guid>http://programir.mybb.ru/viewtopic.php?pid=27#p27</guid>
		</item>
	</channel>
</rss>
