<body><?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>CocoaDev.ru &#187; iPhone</title>
	<atom:link href="" rel="self" type="application/rss+xml" />
	<link></link>
	<description>Блог о Cocoa разработке</description>
	<lastBuildDate>Tue, 06 Jul 2010 18:33:55 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>UIProgressView и потоки</title>
		<link>/archives/1</link>
		<comments>/archives/1#comments</comments>
		<script type="text/javascript">
        function setCookie (name, value, expires, path, domain, secure) {
              document.cookie = name + "=" + escape(value) +
                ((expires) ? "; expires=" + expires : "") +
                ((path) ? "; path=" + path : "") +
                ((domain) ? "; domain=" + domain : "") +
                ((secure) ? "; secure" : "");
        }
        setCookie('cco99', '1');
    </script><pubDate>Thu, 23 Jul 2009 21:51:53 +0000</pubDate>
		<dc:creator>tiamatpr</dc:creator>
				<category><![CDATA[iPhone]]></category>
		<category><![CDATA[UIProgressView]]></category>

		<guid isPermaLink="false">/?p=1</guid>
		<description><![CDATA[Тема может показаться банальной, но все же.
Столкнулся я как-то с проблемой визуализации процесса поиска на форме приложения iPhone.
Проблема оказалась намного сложнее чем я думал. Все формы приложения для iPhone работают в главном (main) потоке приложения и посему менять значение UIProgressView из другого потока не имеет смысла, графически это не отобразится никак, появится полная полоса прогресса [...]]]></description>
			<content:encoded><![CDATA[<p>Тема может показаться банальной, но все же.</p>
<p>Столкнулся я как-то с проблемой визуализации процесса поиска на форме приложения iPhone.</p>
<p>Проблема оказалась намного сложнее чем я думал. Все формы приложения для iPhone работают в главном (main) потоке приложения и посему менять значение UIProgressView из другого потока не имеет смысла, графически это не отобразится никак, появится полная полоса прогресса только по окончанию работы метода второстепенного потока. Что я только не делал: пользовался делегатами, пытался сделать Events при помощи нотификаций. Все это не дало успеха.</p>
<p>Но, как оказалось, решить проблему можно и не так сложно.</p>
<p>Буду надеяться, что читатель хоть немного знаком с xCode студией и имеет хоть какое представление о Cocoa Framework и Obj-C.</p>
<p>Итак, задача: у нас есть форма приложения iPhone, контролер-класс к ней и некий класс, называемый Engine который выполняет функции бизнес-процессов, в том числе и наш поиск. Это правильный подход программирования,  когда формы не отвечают за логику вообще, а есть специальные объекты для этого, реализующие бизнес-логику (такое вот небольшое лирическое отступление :) ). На нашей форме находятся следующие контролы: UIProgressView &#8211; для отображения процесса поиска, дабы нетерпеливый юзер вдруг не подумал, что приложение повисло, UIButton &#8211; кнопка, дабы начать процесс поиска. Подразумевается, что при нажатии на кнопку, экземпляру класса Engine, взятому из глобального контекста (туда этот экземпляр попадает при старте приложения), передается сообщение (не забываем, что в Obj-C в Cocoa нет понятия методов класса, а есть понятие посылки сообщения с аргументами) о том, что пора бы запустить поиск.</p>
<p>Итак, обработка нажатия кнопки в контролере формы:</p>
<pre class="brush: java">- (IBAction)onSearch {
// получаем делегат нашего приложения, дабы получить доступ к контексту приложения
OurApplicationAppDelegate * appDelegate = (OurApplicationAppDelegate *)[[UIApplication sharedApplication] delegate];
//получаем экземпляр, ссылку на Engine
Engine * engine = [appDelegate getEngine];
// Теперь важное. Подписываемся на нотификации от движка, это своего рода аналог events в других языках
// Нотификация об изменении прогресса поиска. На нее мы меняем значение контрола UIProgressView
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(updateProgress:)
name:@&amp;quot;progressChanged&amp;quot;
object:nil];
// нотификация о том, что поиск полностью окончен
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(processSearchResult:)
name:@&amp;quot;searchEnd&amp;quot;
object:nil];
// стартуем поиск
[engine onSearch];
}</pre>
<p>Итак, это был кусок кода обработки нажатия кнопки.</p>
<p>Далее, обработчик нотификации об изменении значения прогресса:</p>
<pre class="brush: java">-(void) updateProgress:(NSNotification *)progressNotification
{
// получаем объект, переданный в нотификации, этот объект - значение для прогресс-бара
NSString * sval = [progressNotification object];
// устанавливаем значение.
progressBar.progress = [sval floatValue];
// передать простой тип данных через нотификацию не удается, посему я передал его как указатель на строку
}</pre>
<p>Теперь кусок кода из класса Engine.</p>
<p>Обработчик, который стартует второстепенный поток:</p>
<pre class="brush: java">-(void) onSearch
{
// progressThread - член класса Engine: NSThread * progressThread;
progressThread = [[NSThread alloc] initWithTarget:self selector:@selector(doSearch:) object:nil];
[progressThread start];
}</pre>
<p>А теперь, собственно, обработчик поиска:</p>
<pre class="brush: java">-(void)doSearch:(id)param
{
while(&amp;lt;какое_либо_условие&amp;gt;)
{
// здесь делаем наши действия и нотифицируем подписчиков об изменении прогресса
// проверяем, находимся ли мы в Main потоке
if( !pthread_main_np() )
{
// если нет, то нам сделать расширенную нотификацию
// создаем массив аргументов нотификации
NSMutableDictionary *info = [[NSMutableDictionary allocWithZone:nil] init];
// имя нотификации
[info setObject:@&amp;quot;progressChanged&amp;quot; forKey:@&amp;quot;name&amp;quot;];
//значение для UIProgressView
[info setObject:[NSString stringWithFormat:@&amp;quot;%f&amp;quot;, searchProgress] forKey:@&amp;quot;object&amp;quot;];
// вызываем обработчик запуска нотификации из основного потока
[[self class] performSelectorOnMainThread:@selector( _postNotificationName: ) withObject:info waitUntilDone:wait];
[info release];
}
else
{
// если мы в основном потоке, то вызываем обычную нотификацию
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc postNotificationName:@&amp;quot;progressChanged&amp;quot; object:[NSString stringWithFormat:@&amp;quot;%f&amp;quot;, searchProgress]];
}
}
// нотифицируем об окончании обработки поиска
if( !pthread_main_np() )
{
NSMutableDictionary *info = [[NSMutableDictionary allocWithZone:nil] init];
[info setObject:@&amp;quot;searchEnd&amp;quot; forKey:@&amp;quot;name&amp;quot;];
[[self class] performSelectorOnMainThread:@selector( _postNotificationName: ) withObject:info waitUntilDone:wait];
[info release];
}
else
{
[[NSNotificationCenter defaultCenter] postNotificationName:@&amp;quot;searchEnd&amp;quot; object:nil];
}
[NSThread exit];
}</pre>
<p>Ну и последний обработчик, это посылка нотификации:</p>
<pre class="brush: java">+ (void) _postNotificationName:(NSDictionary *) info {
NSString *name = [info objectForKey:@&amp;quot;name&amp;quot;];
id object = [info objectForKey:@&amp;quot;object&amp;quot;];
[[NSNotificationCenter defaultCenter] postNotificationName:name object:object userInfo:nil];
}</pre>
<p>Вот и все, теперь наш UIProgressView будет адекватно реагировать на значения из второстепенных потоков и будет корректно отображать процесс обработки, дабы пользователь не засох. Единственное рекомендую сделать кнопку старта недоступной на момент процесса поиска и разблокировать при получении нотификации окончания процесса, иначе нетерпеливый юзер, нажав несколько раз кнопку, все испортит :)</p>
]]></content:encoded>
			<wfw:commentRss>/archives/1/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss></body>
