<?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>ultrajoke</title>
	<atom:link href="http://blog.spacemanlabs.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.spacemanlabs.com</link>
	<description>faking it &#039;til we make it</description>
	<lastBuildDate>Thu, 18 Apr 2013 01:18:40 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<item>
		<title>WWDC Blast</title>
		<link>http://blog.spacemanlabs.com/2013/03/wwdc-blast/?&#038;owa_medium=feed&#038;owa_sid=</link>
		<comments>http://blog.spacemanlabs.com/2013/03/wwdc-blast/#comments</comments>
		<pubDate>Fri, 22 Mar 2013 19:03:38 +0000</pubDate>
		<dc:creator>Joel Kraut</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[Alex C. Schaefer]]></category>
		<category><![CDATA[Heroku]]></category>
		<category><![CDATA[Twilio]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[web app]]></category>
		<category><![CDATA[web application]]></category>
		<category><![CDATA[WWDC]]></category>
		<category><![CDATA[WWDCBlast]]></category>

		<guid isPermaLink="false">http://blog.spacemanlabs.com/?p=675</guid>
		<description><![CDATA[Early this morning we and our friend Alex C. Schaefer launched a new project of special interest to iOS and Mac developers. WWDC Blast is a free service to inform you via SMS the very moment that WWDC tickets go &#8230; <a href="http://blog.spacemanlabs.com/2013/03/wwdc-blast/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Early this morning we and our friend <a href="http://twitter.com/alexcschaefer">Alex C. Schaefer</a> launched a new project of special interest to iOS and Mac developers. <a title="WWDC Blast" href="https://secure.wwdcblast.com">WWDC Blast</a> is a free service to inform you via SMS the very moment that <a title="WWDC" href="http://developer.apple.com/wwdc">WWDC</a> tickets go on sale. With tickets having sold out last year in under two hours, there&#8217;s no better time to automate this. We won&#8217;t spam you, we won&#8217;t sell your information, we just like the idea of keeping people as informed as possible. <a href="https://secure.wwdcblast.com">Check it out</a>.</p>
<p>Technically, this project was a fun one. It&#8217;s the first web app we&#8217;ve launched, and while the guts aren&#8217;t incredibly complicated, we&#8217;re integrating lots of cool services and frameworks to make some magic happen. We&#8217;re using <a title="Heroku" href="http://www.heroku.com">Heroku</a>, <a title="Twilio" href="http://www.twilio.com">Twilio</a>, <a href="http://aws.amazon.com/s3/">S3</a>, <a title="Bootstrap" href="http://twitter.github.com/bootstrap/">Twitter Bootstrap</a>, <a title="MixPanel" href="http://www.mixpanel.com">MixPanel</a>, and some other cool stuff that isn&#8217;t even user-visible yet. And the list goes on.</p>
<p>Most fun of all is that we put together the whole thing, soup to nuts, concept to launch, in under four days. That&#8217;s right: the initial idea was broached Monday morning. We launched early early Friday.</p>
<p>Not bad.</p>
<a href="http://twitter.com/share" class="twitter-share-button" data-url="http://blog.spacemanlabs.com/2013/03/wwdc-blast/" data-text="WWDC Blast" data-count="horizontal">Tweet</a>]]></content:encoded>
			<wfw:commentRss>http://blog.spacemanlabs.com/2013/03/wwdc-blast/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Spatter and Spark</title>
		<link>http://blog.spacemanlabs.com/2013/03/spatter-and-spark/?&#038;owa_medium=feed&#038;owa_sid=</link>
		<comments>http://blog.spacemanlabs.com/2013/03/spatter-and-spark/#comments</comments>
		<pubDate>Mon, 04 Mar 2013 18:30:26 +0000</pubDate>
		<dc:creator>Joel Kraut</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[animation]]></category>
		<category><![CDATA[core animation]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[projects]]></category>
		<category><![CDATA[Spatter and Spark]]></category>

		<guid isPermaLink="false">http://www.ultrajoke.net/?p=666</guid>
		<description><![CDATA[Another year-and-a-bit, another successful client project! As of a few weeks ago, Polk Street Press shipped a brand-new storybook, Spatter and Spark. It&#8217;s a genuinely endearing tale of two best friends solving problems together in a way that five-year-olds will &#8230; <a href="http://blog.spacemanlabs.com/2013/03/spatter-and-spark/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p><img class="alignleft size-full wp-image-667" alt="icon_150" src="http://www.blog.spacemanlabs.com/wp-content/uploads/2013/03/icon_150.png" width="150" height="150" /></p>
<p>Another year-and-a-bit, another successful client project! As of a few weeks ago, <a href="http://polkstreetpress.com">Polk Street Press</a> shipped a brand-new storybook, <a href="http://www.polkstreetpress.com/apps-books/spatter-spark"><strong>Spatter and Spark</strong></a>. It&#8217;s a genuinely endearing tale of two best friends solving problems together in a way that five-year-olds will find endlessly entertaining. We do too. The story is engaging, the character design and illustration is phenomenal, and there&#8217;s just a ton of incidental interactivity to keep the experience fresh and engaging read after read. There are also IAP games and activities designed to teach a preschool skill set – they happen to be pretty fun, too. In a very cool innovation, the activities, and some in-story data, are optionally parsed on PSP&#8217;s backend to give parents an auto-updating <a title="sample parental dashboard" href="https://learn.polkstreetpress.com/sample">report</a> on their child&#8217;s educational progress.</p>
<p>Most importantly from the perspective of this blog, <a href="http://spacemanlabs.com">Spaceman Labs</a> is responsible for all the code in the iOS app. We built on all the work we&#8217;d done for <a title="Goodnight Safari" href="http://www.blog.spacemanlabs.com/2012/01/goodnight-safari/">Goodnight Safari</a>, and were able to make use of a lot of clever stuff we&#8217;d done, like <a title="Cancel dispatch_after" href="http://www.blog.spacemanlabs.com/2011/12/cancel-dispatch_after/">cancelable blocks</a>, audio-synced text highlighting, and animated character encapsulation. But we also developed the animation framework significantly. As it&#8217;s now Polk Street Press&#8217;s IP we can&#8217;t tell you too much about it, but we can say it delivers greatly improved fidelity, performance, and memory utilization (yes, more than just fixing leaks), all of which enables much more detailed, expressive, and long-running animations in the same app size and on the same hardware. We also did a lot of work on moving animation specifications out of code and into artist-maintainable XML, which allowed for rapid iteration without our involvement.</p>
<p>We&#8217;re really proud of this project. We love the work we&#8217;ve done, and we love the finished product. So far reviews on iTunes seem to agree. Check it out (on the <a href="https://itunes.apple.com/us/app/spatter-spark/id551418119?mt=8&amp;partnerId=30&amp;siteId=p2VtJiMMlt8">App Store</a> and at Polk Street Press&#8217;s <a href="http://www.polkstreetpress.com/apps-books/spatter-spark">website</a>) and let us know what you think.</p>
<a href="http://twitter.com/share" class="twitter-share-button" data-url="http://blog.spacemanlabs.com/2013/03/spatter-and-spark/" data-text="Spatter and Spark" data-count="horizontal">Tweet</a>]]></content:encoded>
			<wfw:commentRss>http://blog.spacemanlabs.com/2013/03/spatter-and-spark/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SMPageControl, Meet UIAccessibility</title>
		<link>http://blog.spacemanlabs.com/2013/01/smpagecontrol-meet-uiaccessibility/?&#038;owa_medium=feed&#038;owa_sid=</link>
		<comments>http://blog.spacemanlabs.com/2013/01/smpagecontrol-meet-uiaccessibility/#comments</comments>
		<pubDate>Wed, 30 Jan 2013 17:45:57 +0000</pubDate>
		<dc:creator>Jerry Jones</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[objective-c]]></category>
		<category><![CDATA[Open Source]]></category>
		<category><![CDATA[UI]]></category>
		<category><![CDATA[UIPageControl]]></category>

		<guid isPermaLink="false">http://www.ultrajoke.net/?p=649</guid>
		<description><![CDATA[A while back we introduced SMPageControl, our drop in replacement for UIPageControl with a slew of extra bells and whistles. It was surprisingly well received, and is by far the most popular bit of open source code we&#8217;ve released to &#8230; <a href="http://blog.spacemanlabs.com/2013/01/smpagecontrol-meet-uiaccessibility/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>A while back we <a href="http://www.blog.spacemanlabs.com/2012/10/smpagecontrol/">introduced</a> <a href="https://github.com/Spaceman-Labs/SMPageControl">SMPageControl</a>, our drop in replacement for UIPageControl with a slew of extra bells and whistles. It was surprisingly well received, and is by far the most popular bit of open source code we&#8217;ve released to date. (Thanks for starring the repo on GitHub!)</p>
<p>Unfortunately, it seems that deeming it a &#8220;drop in replacement&#8221; wasn&#8217;t 100% accurate; while SMPageControl provided a pixel perfect representation of UIPageControl, it lacked UIAccessibility support all together. I hate to admit it, but until recently I haven&#8217;t paid much attention to accessibility in the iOS apps I&#8217;ve worked on. That has changed &#8211; for a variety of reasons that I plan to cover in a future blog post &#8211; and SMPageControl now really is a drop in replacement, providing the exact same UIAccessibility behaviors as UIPageControl.</p>
<h3>But Wait, There&#8217;s More!</h3>
<p>In sticking with the theme of being <em>UIPageControl’s Fancy One-Upping Cousin</em>, SMPageControl extends UIPageControl&#8217;s accessibility functions by letting you give each page its own name. The default behavior is to set the accessibility value to <code>"page [current page + 1] of [number of pages]"</code>. e.g. <code>"page 1 of 10"</code>. If a page has been named, the provided name is prepended to the default accessibility value. e.g <code>"Search - page 1 of 10"</code>. This is extremely useful when using per page indicator images, where one or more page is likely to have a specific usage that is identifiable to the user.</p>
<h3>Example</h3>
<pre class="brush: objc; title: ; notranslate">
SMPageControl *pageControl = [[SMPageControl alloc] init];
[pageControl setImage:[UIImage imageNamed:@&quot;searchDot&quot;] forPage:0];
[pageControl setCurrentImage:[UIImage imageNamed:@&quot;currentSearchDot&quot;] forPage:0];
[pageControl setImage:[UIImage imageNamed:@&quot;appleDot&quot;] forPage:1];
[pageControl setCurrentImage:[UIImage imageNamed:@&quot;currentAppleDot&quot;] forPage:1];
[pageControl setName:@&quot;Search&quot; forPage:0];
[pageControl setName:@&quot;Apple&quot; forPage:1];
</pre>
<p><a href="http://www.blog.spacemanlabs.com/wp-content/uploads/2013/01/SMPageControl-3.png"><img src="http://www.blog.spacemanlabs.com/wp-content/uploads/2013/01/SMPageControl-3-300x201.png" alt="SMPageControl-3" width="300" height="201" class="alignnone size-medium wp-image-651" /></a></p>
<h3>Version 1.0 and CocoaPods</h3>
<p>With the addition of accessibility support, I feel comfortable calling SMPageControl a 1.0; bugs may still surface, and features may be added, but the goal of creating a fully featured page control has been achieved. Having reached this point, I&#8217;ve created a 1.0 version tag in the git repo and added it to CocoaPods.</p>
<p>As always, feedback is welcome. We accept pull requests for features and bug fixes at the GitHub repo, so if you&#8217;d like to contribute, by all means, please do.</p>
<a href="http://twitter.com/share" class="twitter-share-button" data-url="http://blog.spacemanlabs.com/2013/01/smpagecontrol-meet-uiaccessibility/" data-text="SMPageControl, Meet UIAccessibility" data-count="horizontal">Tweet</a>]]></content:encoded>
			<wfw:commentRss>http://blog.spacemanlabs.com/2013/01/smpagecontrol-meet-uiaccessibility/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Integrating Asana and Git</title>
		<link>http://blog.spacemanlabs.com/2013/01/integrating-asana-and-git/?&#038;owa_medium=feed&#038;owa_sid=</link>
		<comments>http://blog.spacemanlabs.com/2013/01/integrating-asana-and-git/#comments</comments>
		<pubDate>Mon, 28 Jan 2013 18:30:24 +0000</pubDate>
		<dc:creator>Joel Kraut</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Asana]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[post-commit]]></category>
		<category><![CDATA[scripting]]></category>
		<category><![CDATA[source control]]></category>

		<guid isPermaLink="false">http://www.ultrajoke.net/?p=635</guid>
		<description><![CDATA[Recently we at Spaceman Labs have been testing out different tools and workflows to manage our various projects. After trying a few different packages, we&#8217;ve more or less settled on Asana as our issue tracking solution. It&#8217;s got a lot of advantages: &#8230; <a href="http://blog.spacemanlabs.com/2013/01/integrating-asana-and-git/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Recently we at <a href="http://spacemanlabs.com">Spaceman Labs</a> have been testing out different tools and workflows to manage our various projects. After trying a few different packages, we&#8217;ve more or less settled on <a href="http://asana.com">Asana</a> as our issue tracking solution. It&#8217;s got a lot of advantages: it&#8217;s free, we can have as many projects as we want, and it&#8217;s low-friction enough that we&#8217;re likely to use it regularly.</p>
<p>That last one is actually the biggest feature – just as the best camera is the one you have with you, the best issue tracker is the one you&#8217;ll actually use. All the bells and whistles in the world don&#8217;t matter if you&#8217;re scared away from the core functionality. But there&#8217;s one way Asana could be even easier to use. It doesn&#8217;t have a standard way to integrate with our Git workflow. We&#8217;d like to be able to commit some code with a comment like &#8220;fixes #123&#8243; and have that fix reflected in Asana.</p>
<p>Fortunately, all of the groundwork has been done. Asana has a really awesome, and beautifully well-documented, <a title="Asana API documentation" href="http://developer.asana.com/documentation/">API</a>. And other developers, most notably for our purposes <a title="Git Hook for Asana" href="http://brunohq.com/journal/speed-project-git-hook-for-asana/">Bruno Costa</a>, have taken that API and put together useful scripts.</p>
<p>The only thing missing from Bruno&#8217;s post-commit hook (and go back and read his <a title="Git Hook for Asana" href="http://brunohq.com/journal/speed-project-git-hook-for-asana/">blog post</a> if you haven&#8217;t, it&#8217;s great) is the ability to run it non-interactively. Our Git workflow is built around <a href="http://git-tower.com">Tower</a>; it&#8217;s an awesome tool, but it doesn&#8217;t know how to simulate reading from <code>/dev/tty</code> in a hook.</p>
<p>So we needed to make the script non-interactive. This means it loses some potential flexibility, but on the other hand, it&#8217;s faster and has a somewhat higher &#8220;just works&#8221; quotient. Once you set up your git config variables, anyway. While we were at it, we also made it handle multiple ticket numbers per checkin, and differentiate between referencing and closing a ticket. That means you can write a commit message like <strong><code><br />
<h3>"Tweaked the widget; fixed #1, #2, and #3; references #4 and #5; oh yeah, and closes #6"</h3>
<p></code></strong> and have the right thing happen. Neat, right?</p>
<p>You can find the script at our <a href="https://github.com/Spaceman-Labs/asana-post-commit">GitHub page</a>; please modify and open pull requests if you&#8217;d like to see it do more. (For instance, re-opening a closed ticket would be trivial.) V1 of the script is duplicated below. To run it, save it to your local repo&#8217;s <code>.git/hooks/post-commit</code>, and <code>chmod 755</code> that mother. Run the <code>git config</code> line at the top of the script with the appropriate values, and you&#8217;re good to go.</p>
<pre class="brush: bash; title: ; notranslate">#!/bin/bash

# modified from http://brunohq.com/journal/speed-project-git-hook-for-asana/

# -----------------------
# necessary configuration:
# git config --global user.asana-key &quot;MY_ASANA_API_KEY&quot; (http://app.asana.com/-/account_api)
# -----------------------

apikey=$(git config user.asana-key)
if [ $apikey == '' ] ; then exit 0; fi

# hold the closed ticket numbers
declare -a closed
# hold the ticket numbers that are not closed, just touched
declare -a referenced
# track whether we're currently closing tickets or just referencing them
closes='NO'

# regex pattern to recognize a story number
taskid_pattern='#([0-9]*)'
# regex pattern to recognize a &quot;closing this ticket&quot; word
closes_pattern='([Ff]ix|[Cc]lose|[Cc]losing)'
# regex pattern to recognize an &quot;and&quot; word (eg &quot;fixes #1, #2, and #3&quot;)
and_pattern='([Aa]nd|&amp;)'

# get the checkin comment for parsing
comment=$(git log --pretty=oneline -n1)

# break the commit comment down into words
IFS=' ' read -a words &lt;&lt;&lt; &quot;$comment&quot;

for element in &quot;${words[@]}&quot;
do
    # if we have a task id, save it to the appropriate array
    if [[ $element =~ $taskid_pattern ]]; then
	if [ &quot;${closes}&quot; == &quot;YES&quot; ]; then
	    closed=(&quot;${closed[@]}&quot; &quot;${BASH_REMATCH[1]}&quot;)
	fi
	referenced=(&quot;${referenced[@]}&quot; &quot;${BASH_REMATCH[1]}&quot;)
    # or else if we have a &quot;closes&quot; word, set the tracking bool accordingly
    elif [[ $element =~ $closes_pattern ]]; then
	closes='YES'
    # and if we don't, set us back to referencing
    # (if we're an &quot;and&quot;, don't change any state)
    elif [[ ! $element =~ $and_pattern ]]; then
	closes='NO'
    fi
done

# touch the stories we've referenced
for element in &quot;${referenced[@]}&quot;
do
    curl -u ${apikey}: https://app.asana.com/api/1.0/tasks/${element}/stories \
         -d &quot;text=just committed ${comment/ / with message:%0A}&quot; &gt; /dev/null 2&gt;&amp;1
done

# close the tasks we've fixed
for element in &quot;${closed[@]}&quot;
do
    curl --request PUT -u ${apikey}: https://app.asana.com/api/1.0/tasks/${element} \
         -d &quot;completed=true&quot; &gt; /dev/null 2&gt;&amp;1
done</pre>
<a href="http://twitter.com/share" class="twitter-share-button" data-url="http://blog.spacemanlabs.com/2013/01/integrating-asana-and-git/" data-text="Integrating Asana and Git" data-count="horizontal">Tweet</a>]]></content:encoded>
			<wfw:commentRss>http://blog.spacemanlabs.com/2013/01/integrating-asana-and-git/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Mistakes Were Made: Audio and ARC</title>
		<link>http://blog.spacemanlabs.com/2012/11/mistakes-were-made-audio-and-arc/?&#038;owa_medium=feed&#038;owa_sid=</link>
		<comments>http://blog.spacemanlabs.com/2012/11/mistakes-were-made-audio-and-arc/#comments</comments>
		<pubDate>Mon, 05 Nov 2012 18:30:34 +0000</pubDate>
		<dc:creator>Joel Kraut</dc:creator>
				<category><![CDATA[Explanation]]></category>
		<category><![CDATA[ARC]]></category>
		<category><![CDATA[automatic reference counting]]></category>
		<category><![CDATA[Beginners]]></category>
		<category><![CDATA[cocoa]]></category>
		<category><![CDATA[code snippet]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[mistakes were made]]></category>
		<category><![CDATA[Mistkaes]]></category>

		<guid isPermaLink="false">http://www.ultrajoke.net/?p=600</guid>
		<description><![CDATA[There&#8217;s a mistake I have, to my own great embarrassment, made twice, so I think it&#8217;s worth writing up for posterity. This one&#8217;s brief: you can laugh at me and move on. Under ARC, AVAudioPlayers don&#8217;t retain themselves while playing. &#8230; <a href="http://blog.spacemanlabs.com/2012/11/mistakes-were-made-audio-and-arc/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>There&#8217;s a mistake I have, to my own great embarrassment, made twice, so I think it&#8217;s worth writing up for posterity. This one&#8217;s brief: you can laugh at me and move on.</p>
<p>Under ARC, <code>AVAudioPlayer</code>s don&#8217;t retain themselves while playing.</p>
<p>A brief recap: ARC takes memory management off the minds of developers by automating the insertion of retains and releases according to a simple set of rules. This is much to the delight of new developers, and equally to the chagrin of grizzled oldsters. (I admit to having felt more than a bit of the latter at the outset.)</p>
<p><code>AVAudioPlayer</code> is the simplest API to get an audio file from your Resources directory to your user&#8217;s ears.</p>
<pre class="brush: objc; title: ; notranslate">
AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:audioPath error:nil];
[player play];
</pre>
<p>And you&#8217;re set! Under manual memory management, anyway.</p>
<p>What happens with ARC?  The newly created player is retained. As soon as the method ends, however, there is no longer anything referring to it. Even though it&#8217;s still doing stuff — the audio file you&#8217;re playing will presumably last longer than a millisecond — it&#8217;s released, and happily cuts off the audio early.</p>
<p>Having reasoned through it, the solution is of course trivial. Make the <code>AVAudioPlayer</code> an instance variable of whatever class is creating it, and it won&#8217;t be released until the controlling object is. Audio bliss.</p>
<p>Of course, I feel that the mistake is perhaps not <em>entirely</em> mine. AVAudioPlayer really should retain itself while it&#8217;s playing audio, and release itself when it finishes. To that end, I&#8217;ve filed <a href="rdar://12635205">rdar://12635205</a>; feel free to dupe it if you agree. And for general interest, here&#8217;s the sample code I included to demonstrate the problem:</p>
<pre class="brush: objc; title: ; notranslate">
#define IVAR_PLAYER 0

#import &quot;SMViewController.h&quot;
#import &lt;AVFoundation/AVFoundation.h&gt;

@interface SMViewController ()
{
#if IVAR_PLAYER
	AVAudioPlayer *player;
#endif
}

@end

@implementation SMViewController

- (IBAction)playSound:(id)sender {
#if !IVAR_PLAYER
	AVAudioPlayer *player;
#endif
	NSURL *audioPath = [NSURL fileURLWithPath:[[NSBundle mainBundle]
											   pathForResource:@&quot;audio&quot; ofType:@&quot;mp3&quot;]];
	player = [[AVAudioPlayer alloc] initWithContentsOfURL:audioPath
													error:nil];
	[player play];
	
	// delay a bit to keep the run loop alive long enough to let the audio start playing
	// don't do this in production code, obviously! It is a bad way to do basically anything.
	for (int i = 0; i &lt; 1000; i++)
		NSLog(@&quot;%d&quot;, i);

@end
</pre>
<a href="http://twitter.com/share" class="twitter-share-button" data-url="http://blog.spacemanlabs.com/2012/11/mistakes-were-made-audio-and-arc/" data-text="Mistakes Were Made: Audio and ARC" data-count="horizontal">Tweet</a>]]></content:encoded>
			<wfw:commentRss>http://blog.spacemanlabs.com/2012/11/mistakes-were-made-audio-and-arc/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SMPageControl: UIPageControl&#8217;s Fancy One-Upping Cousin</title>
		<link>http://blog.spacemanlabs.com/2012/10/smpagecontrol/?&#038;owa_medium=feed&#038;owa_sid=</link>
		<comments>http://blog.spacemanlabs.com/2012/10/smpagecontrol/#comments</comments>
		<pubDate>Mon, 15 Oct 2012 17:00:23 +0000</pubDate>
		<dc:creator>Jerry Jones</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[cocoa]]></category>
		<category><![CDATA[images]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[iphone]]></category>
		<category><![CDATA[Open Source]]></category>
		<category><![CDATA[UIPageControl]]></category>

		<guid isPermaLink="false">http://www.ultrajoke.net/?p=555</guid>
		<description><![CDATA[If you&#8217;ve ever spent any time at Dribbble, you know how much designers love to customize UIPageControl, normally in the form of custom spacing or fancy inset looking page dots. As a developer, you&#8217;re probably also keenly aware of the &#8230; <a href="http://blog.spacemanlabs.com/2012/10/smpagecontrol/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>If you&#8217;ve ever spent any time at Dribbble, you know how much designers <strong>love</strong> to customize <code>UIPageControl</code>, normally in the form of custom spacing or fancy inset looking page dots. As a developer, you&#8217;re probably also keenly aware of the lack of customization that Apple&#8217;s built in class provides. Hell, it wasn&#8217;t even until the introduction of iOS 6 that you could change the tint color, and even then, that&#8217;s where the flexibility ends.</p>
<p>Given my blogging absence in recent months, and the fact that I have a somewhat immediate need for a highly customizable page control, it seemed like a fun opportunity to spend an afternoon making something I could share. Admittedly, this isn&#8217;t a terribly hard problem to solve &#8211; <code>UIPageControl</code> is one of the least sophisticated bit of UI in iOS. Despite this being an open-source softball, I was excited by the idea of writing something simple, yet robust enough that I wouldn&#8217;t have to tackle this issue ever again.</p>
<p><code>SMPageControl</code> provides all the functionality of <code>UIPageControl</code>, but also adds the ability to change indicator diameters, margins and alignment. It also provides support for using images as the inactive and active page indicator. For greater control, it is also possible to provide index specific indicator images.</p>
<p>If you&#8217;re not interested in the fanfare and hot air about to follow, go ahead and just <a href="https://github.com/Spaceman-Labs/SMPageControl">grab the code</a> from GitHub.</p>
<p><strong>Update: </strong>Two new features have been added. A masking mode that will allow indicators to use the tint coloring, along with an image as a clipping mask. And a convenience method for updating the current page by passing in a scrollview.</p>
<p><a href="http://www.blog.spacemanlabs.com/wp-content/uploads/2012/10/Screen-Shot-2012-10-17-at-9.03.52-AM.png"><img src="http://www.blog.spacemanlabs.com/wp-content/uploads/2012/10/Screen-Shot-2012-10-17-at-9.03.52-AM-210x300.png" alt="" title="Screen Shot 2012-10-17 at 9.03.52 AM" width="210" height="300" class="alignnone size-medium wp-image-595" /></a></p>
<h3>Find and Replace</h3>
<p>Using this class is as simple as they come, you can quite literally just change your class names from <code>UIPageControl</code> to <code>SMPageControl</code>, and <code>SMPageControl</code> will seamlessly provide all of the bland, out of the box functionality you are used to. It also provides the same two (and more) <code>UIAppearance</code> properties, <code>pageIndicatorTintColor</code> and <code>pageIndicatorTintColor</code>. I&#8217;ve made an effort to precisely mirror the default functionality, so that <code>UIPageControl</code> can be used as normal, while being able to make a quick shift to <code>SMPageControl</code> as soon as extra flexibility is required.</p>
<p>Example:</p>
<pre class="brush: objc; title: ; notranslate">
// Positioning code suppressed
UIPageControl *pageControl = [[UIPageControl alloc] init];
pageControl.numberOfPages = 10;
[pageControl sizeToFit];
[self.view addSubview:pageControl];

SMPageControl *pageControl2 = [[SMPageControl alloc] init];
pageControl2.numberOfPages = 10;
[pageControl2 sizeToFit];
[self.view addSubview:pageControl2];
</pre>
<p><a href="http://www.blog.spacemanlabs.com/wp-content/uploads/2012/10/Screen-Shot-2012-10-13-at-9.57.49-PM.png"><img src="http://www.blog.spacemanlabs.com/wp-content/uploads/2012/10/Screen-Shot-2012-10-13-at-9.57.49-PM-148x300.png" alt="" title="Screen Shot 2012-10-13 at 9.57.49 PM" width="148" height="300" class="alignnone size-medium wp-image-566" /></a></p>
<h3>A Little Bit Retro, a Little Bit Rock and roll</h3>
<p><code>SMPageControl</code> allows for the tried and true page control appearance, with some slightly adjusted layout. Using the fully <code>UIAppearance</code> compatible properties, <code>indicatorDiameter</code> and <code>indicatorMargin</code>, it&#8217;s easy to make <code>SMPageControl</code> appear a bit more modern.</p>
<pre class="brush: objc; title: ; notranslate">
// All instances of SMPageControl across the app will inherit this style
SMPageControl *appearance = [SMPageControl appearance];
appearance.indicatorDiameter = 10.0f;
appearance.indicatorMargin = 20.0f;
</pre>
<p><a href="http://www.blog.spacemanlabs.com/wp-content/uploads/2012/10/Screen-Shot-2012-10-13-at-10.09.48-PM.png"><img src="http://www.blog.spacemanlabs.com/wp-content/uploads/2012/10/Screen-Shot-2012-10-13-at-10.09.48-PM-148x300.png" alt="" title="Screen Shot 2012-10-13 at 10.09.48 PM" width="148" height="300" class="alignnone size-medium wp-image-570" /></a></p>
<p>Alright, so bigger, more whitespace; that&#8217;s hardly Dribbble worthy, right? Well fortunately, SMPageControl has two more powerful properties &#8211; Also <code>UIAppearance</code> compatible &#8211; <code>pageIndicatorImage</code> and <code>currentPageIndicatorImage</code>. These are probably my favorite two properties, as they allow for a wholesale replacement of the active and inactive &#8220;dots&#8221;, with two quick and easy lines of code.</p>
<pre class="brush: objc; title: ; notranslate">
// All instances of SMPageControl across the app will inherit this style
SMPageControl *appearance = [SMPageControl appearance];
appearance.pageIndicatorImage = [UIImage imageNamed:@&quot;pageDot&quot;];
appearance.currentPageIndicatorImage = [UIImage imageNamed:@&quot;currentPageDot&quot;];
</pre>
<p><a href="http://www.blog.spacemanlabs.com/wp-content/uploads/2012/10/Screen-Shot-2012-10-13-at-10.25.29-PM.png"><img src="http://www.blog.spacemanlabs.com/wp-content/uploads/2012/10/Screen-Shot-2012-10-13-at-10.25.29-PM-148x300.png" alt="" title="Screen Shot 2012-10-13 at 10.25.29 PM" width="148" height="300" class="alignnone size-medium wp-image-575" /></a></p>
<h3>Stand Out In A Crowd</h3>
<p>The final bit of customization provided by <code>SMPageControl</code> is its support for per-indicator images, a function that really is the icing of its flexibility. The easiest example of this to point out is the all too familiar Springboard. Ever since Spotlight was added in iPhone OS 3.0, the springboard page control has included a little search icon along with those classic circles. <code>SMPageControl</code> makes this sort of customization trivial. Because of the case by case nature of how I expect this feature to be implemented, per-indicator images are not compatible with <code>UIAppearance</code> &#8211; but adding them is <em>just</em> as quick and easy.</p>
<pre class="brush: objc; title: ; notranslate">
SMPageControl *pageControl = [[SMPageControl alloc] init];
pageControl.numberOfPages = 10;
[pageControl setImage:[UIImage imageNamed:@&quot;searchDot&quot;] forPage:0];
[pageControl setCurrentImage:[UIImage imageNamed:@&quot;currentSearchDot&quot;] forPage:0];
[pageControl setImage:[UIImage imageNamed:@&quot;appleDot&quot;] forPage:3];
[pageControl setCurrentImage:[UIImage imageNamed:@&quot;currentAppleDot&quot;] forPage:3];
[pageControl sizeToFit];
</pre>
<p><a href="http://www.blog.spacemanlabs.com/wp-content/uploads/2012/10/Screen-Shot-2012-10-13-at-10.45.13-PM.png"><img src="http://www.blog.spacemanlabs.com/wp-content/uploads/2012/10/Screen-Shot-2012-10-13-at-10.45.13-PM-148x300.png" alt="" title="Screen Shot 2012-10-13 at 10.45.13 PM" width="148" height="300" class="alignnone size-medium wp-image-579" /></a></p>
<h3>Feedback and Suggestions Welcome</h3>
<p>I suspect that for a problem with such a narrow set of requirements, there isn&#8217;t much left that this little project doesn&#8217;t cover. That seems to be further supported by the similar feature set of this project and the others I&#8217;ve found. (P.S. It&#8217;s generally best to do the diligent Googling <em>before</em> writing your own new code to solve solved problems &#8211; whoops). However, I already mentioned that I wanted this to be robust enough that I wouldn&#8217;t have to chase a similar solution ever again. If there&#8217;s something I&#8217;ve missed, fill out the comment box just below.</p>
<p>If you&#8217;ve read this far, I&#8217;m impressed &#8211; and it&#8217;s probably time you just went and <a href="https://github.com/Spaceman-Labs/SMPageControl">grabbed the code</a> for a bit of tinkering.</p>
<a href="http://twitter.com/share" class="twitter-share-button" data-url="http://blog.spacemanlabs.com/2012/10/smpagecontrol/" data-text="SMPageControl: UIPageControl\'s Fancy One-Upping Cousin" data-count="horizontal">Tweet</a>]]></content:encoded>
			<wfw:commentRss>http://blog.spacemanlabs.com/2012/10/smpagecontrol/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Premature Completion: An Embarrassing Problem</title>
		<link>http://blog.spacemanlabs.com/2012/08/premature-completion-an-embarrassing-problem/?&#038;owa_medium=feed&#038;owa_sid=</link>
		<comments>http://blog.spacemanlabs.com/2012/08/premature-completion-an-embarrassing-problem/#comments</comments>
		<pubDate>Mon, 27 Aug 2012 17:30:56 +0000</pubDate>
		<dc:creator>Joel Kraut</dc:creator>
				<category><![CDATA[Explanation]]></category>
		<category><![CDATA[animation]]></category>
		<category><![CDATA[bugs]]></category>
		<category><![CDATA[cocoa]]></category>
		<category><![CDATA[code snippet]]></category>
		<category><![CDATA[core animation]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[mistakes were made]]></category>
		<category><![CDATA[Mistkaes]]></category>
		<category><![CDATA[sample code]]></category>

		<guid isPermaLink="false">http://www.ultrajoke.net/?p=541</guid>
		<description><![CDATA[Working on a project recently, Jerry and I came across an odd bug. We have a two-level UI that allows the user to navigate between several different scroll views. For the sake of keeping things pretty, we want to reset &#8230; <a href="http://blog.spacemanlabs.com/2012/08/premature-completion-an-embarrassing-problem/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Working on a project recently, Jerry and I came across an odd bug. We have a two-level UI that allows the user to navigate between several different scroll views. For the sake of keeping things pretty, we want to reset a given scroll view before going back to the navigation interface paradigm. No problem, right? Something like this should do the trick:</p>
<pre class="brush: objc; title: ; notranslate">
[UIView animateWithDuration:2.f animations:^{
		[scrollView scrollRectToVisible:CGRectMake(0, 0, 10, 10) animated:YES];
	}completion:^(BOOL finished){
		[self.delegate resumeNavigation];
	}];
</pre>
<p>Turns out, nope! Somehow this runs the completion block in parallel with the animation block. The solution? Simply changing the <code>YES</code> to a <code>NO</code> in the <code>scrollRectToVisible</code> call.</p>
<h3>But Why?</h3>
<p>It seems that Core Animation (and therefore UIView animation) does some unexpected, not entirely welcome magic behind the scenes. If there is nothing to do in the animation block, the framework shrugs and runs the completion block immediately, rather than waiting the specified duration.</p>
<p>My guess, and this is just a guess, is that internally Core Animation depends on the <code><a href="https://developer.apple.com/library/mac/documentation/graphicsimaging/reference/CAAnimation_class/Introduction/Introduction.html#//apple_ref/occ/instm/NSObject/animationDidStop:finished:">animationDidStop:finished:</a></code> delegate callback from a <code><a href="https://developer.apple.com/library/mac/#documentation/graphicsimaging/reference/CAAnimationGroup_class/Introduction/Introduction.html">CAAnimationGroup</a></code> or similar it sets up to handle everything that happens inside an animation block. When that callback fires, CA knows it&#8217;s time to kick off the completion block. If there are no animations created, there is nothing to send a callback. Rather than set a timer to wait for nothing to happen, the completion block runs right away, because why not?</p>
<p>This is seductive reasoning, and has the advantage of being easy to code. Unfortunately, it&#8217;s not always what the user of the API expects. (I would venture to say never!) In our case, it means the naive code fails because (again, guessing) asking the scroll view to animate its scrolling sets up another animation context. Asking it not to animate, by contrast, allows Core Animation to create an <a href="http://www.blog.spacemanlabs.com/2011/08/calayers-parallel-universe/" title="CALayer’s Parallel Universe">implicit animation</a> and work its magic.</p>
<h3>What&#8217;s the Solution?</h3>
<p>Unfortunately, for us API clients there really isn&#8217;t one. This is more of an informational post than anything: once you&#8217;re aware this can be a problem, you may save yourself hours you otherwise would&#8217;ve spent in fruitless debugging. Believe me, I&#8217;ve been there on this issue.</p>
<p>The best thing you can do is play around and see where exactly unexpected things happen.  As long as you keep the &#8220;are there any animations created?&#8221; question in mind, whatever behavior you see will be easy to explain. But reasoning <em>a priori</em> and figuring out what to expect is impossible without inside knowledge of the implementation of UIKit. Which brings us to…</p>
<h3>Toys</h3>
<p>To see just what was going on, I put together a tiny sample project. You can find it <a title="premature completion sample project" href="https://github.com/Spaceman-Labs/PrematureCompletion">here</a>. It has a <code>UIProgressView</code> and a <code>UITextView</code>, both set up to perform some transition either animated or not. The important part of the code looks like this:</p>
<pre class="brush: objc; title: ; notranslate">
- (void)party:(BOOL)animated
{
	[UIView animateWithDuration:2.f animations:^{
		[self.partyProgress setProgress:1 animated:animated];
	}completion:^(BOOL finished){
		self.partyLabel.text = @&quot;PARTY TIME!&quot;;
	}];
}

- (void)study:(BOOL)animated
{
	[UIView animateWithDuration:2.f animations:^{
		[self.studyView scrollRectToVisible:CGRectMake(0, 0, 10, 10) animated:animated];
	}completion:^(BOOL finished){
		self.studyLabel.text = @&quot;STUDY TIME!&quot;;
	}];
}
</pre>
<p>Try scrolling the text view all the way to the bottom, then compare the difference between animating and not animating inside of an animation block. Then do the same with the progress view. Why would they be different?</p>
<p>There are plenty of other classes in UIKit with methods that take an <code>animated:</code> argument. I encourage you to plug them into this test app to see just what happens. Protect yourself from being surprised next time. Take any precaution you can against the embarrassment and social shunning that inevitably accompany premature completion.</p>
<a href="http://twitter.com/share" class="twitter-share-button" data-url="http://blog.spacemanlabs.com/2012/08/premature-completion-an-embarrassing-problem/" data-text="Premature Completion: An Embarrassing Problem" data-count="horizontal">Tweet</a>]]></content:encoded>
			<wfw:commentRss>http://blog.spacemanlabs.com/2012/08/premature-completion-an-embarrassing-problem/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Things I Learned at Siggraph II (2012)</title>
		<link>http://blog.spacemanlabs.com/2012/08/things-i-learned-at-siggraph-ii-2012/?&#038;owa_medium=feed&#038;owa_sid=</link>
		<comments>http://blog.spacemanlabs.com/2012/08/things-i-learned-at-siggraph-ii-2012/#comments</comments>
		<pubDate>Tue, 14 Aug 2012 17:30:31 +0000</pubDate>
		<dc:creator>Joel Kraut</dc:creator>
				<category><![CDATA[Philosophy]]></category>
		<category><![CDATA[GLSL]]></category>
		<category><![CDATA[GPU]]></category>
		<category><![CDATA[optimization]]></category>
		<category><![CDATA[SIGGRAPH]]></category>

		<guid isPermaLink="false">http://www.ultrajoke.net/?p=530</guid>
		<description><![CDATA[In the interests of becoming a more well-rounded individual in the extremely narrow field of computer graphics, I spent last week in sunny LA, attending Siggraph. What follow are my notes and a little bit of synthesis from various sessions &#8230; <a href="http://blog.spacemanlabs.com/2012/08/things-i-learned-at-siggraph-ii-2012/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>In the interests of becoming a more well-rounded individual in the extremely narrow field of computer graphics, I spent last week in sunny LA, attending <a href="http://s2012.siggraph.org">Siggraph</a>. What follow are my notes and a little bit of synthesis from various sessions that I thought might be of some interest to our audience. I jotted these notes down during the presentations, and it&#8217;s very likely I either misunderstood or mis-transcribed things. All errors are my own.</p>
<h3>Computer Aesthetics</h3>
<p>Turns out, there&#8217;s no reliable way for a computer to understand human aesthetics. We like what we like as a result of a huge mess of ad hoc built up junk in our evolutionary history; this is difficult to model. Attempts have been made in the past: the most famous of these, the <a href="http://en.wikipedia.org/wiki/Golden_ratio">Golden Ratio</a>, is actually not as historically important as we&#8217;ve been told. It doesn&#8217;t really appear in all the great works of art and culture people like to cite, although modern artists, aware of its reputation, have consciously used it in a sort of self-fulfilling prophecy.</p>
<p>Interestingly, computers <em>can</em>, through the use of evolutionary algorithms, develop their own sense of aesthetics. They&#8217;re just not going to like what we like. We can&#8217;t use an evolutionary algorithm to bridge this gap because the fitness test can&#8217;t be automated – deciding which of two choices is more aesthetically pleasing is of course the problem we&#8217;re trying to solve. Thus evolutionary algorithms are bottlenecked by humans making that choice (and are subject to the taste of those particular humans).</p>
<h3>Mobile GPU</h3>
<p>Big news: at Siggraph, OpenGL ES 3 was announced. It&#8217;ll be a while before we have mobile devices running it, but now we can start planning ahead.</p>
<p>The important thing on mobile, as ever, is energy efficiency. This year I learned something new: mobile GPUs are never going to have more power. Power means heat, and if they get much more than the ~1watt they currently consume, the chips will melt. Well ok, not the chips, but the solder dots holding the chips in place. So, increases in mobile performance have to come from more energy-efficient hardware and software; we can&#8217;t just throw power at the problem like we do on the desktop.</p>
<p>Of course, we as software engineers don&#8217;t have much say over the chips. But we can write our code in a way that consumes less power. As I said <a title="Things I Learned at Siggraph" href="http://www.blog.spacemanlabs.com/2011/08/things-i-learned-at-siggraph/">last time</a>, this mostly means writing more efficient code, so the hardware has a chance to power down. It also means common-sense things like throttling down the frame rate on static or slow content like menus.</p>
<p>There are some things I didn&#8217;t talk about last time, because I didn&#8217;t know them. Reducing bandwidth is of course important; communication between CPU and GPU takes time and power. One simple way to do this is to mirror symmetrical textures with <code>GL_MIRRORED_REPEAT</code>. Hey, half the bandwidth!</p>
<p>You can also reduce the amount of drawing the GPU has to do. Drawing models front to back, rather than vice versa, means the GPU can reject covered pixels and potentially save you a lot of drawing time. Yes, that means the skybox is drawn last, not first.</p>
<p>Changing GPU state takes a ton of time. That means bouncing between buffers for multi-pass effects is expensive. Rather than drawing to the framebuffer, then to an FBO, then compositing that back into the framebuffer, do the FBO first, and change states just once. Furthermore, you should try to draw all your other FBOs and assorted render targets before rendering to the default framebuffer.</p>
<p>Lastly on stuff I should already have known: FBO contents are saved to main memory. Clearing them before drawing into them on a new frame saves you from having to restore that memory back to the GPU. That&#8217;s a huge win for one line of code.</p>
<h3>New Stuff</h3>
<p>The new ES spec gives us a couple additional options. To reduce bandwidth, it provides two standard texture compression formats that look quite nice compared to the current non-standards (<a href="http://developer.apple.com/library/ios/#documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/TextureTool/TextureTool.html#//apple_ref/doc/uid/TP40008793-CH108-SW1">PVRTC</a> on iOS) and tend to compress smaller.</p>
<p>Also, <code>glFramebufferInvalidate</code> does the same job as <code>glClear</code> for informing the GPU that you don&#8217;t need the contents of a framebuffer, without having to keep track of which bits need to be cleared. There&#8217;s the added bonus of having a name that indicates what it does.</p>
<p>That&#8217;s all, folks!</p>
<a href="http://twitter.com/share" class="twitter-share-button" data-url="http://blog.spacemanlabs.com/2012/08/things-i-learned-at-siggraph-ii-2012/" data-text="Things I Learned at Siggraph II (2012)" data-count="horizontal">Tweet</a>]]></content:encoded>
			<wfw:commentRss>http://blog.spacemanlabs.com/2012/08/things-i-learned-at-siggraph-ii-2012/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SMShadowedLayer</title>
		<link>http://blog.spacemanlabs.com/2012/03/smshadowedlayer/?&#038;owa_medium=feed&#038;owa_sid=</link>
		<comments>http://blog.spacemanlabs.com/2012/03/smshadowedlayer/#comments</comments>
		<pubDate>Fri, 16 Mar 2012 18:08:15 +0000</pubDate>
		<dc:creator>Joel Kraut</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[animation]]></category>
		<category><![CDATA[CALayer]]></category>
		<category><![CDATA[cocoa]]></category>
		<category><![CDATA[core animation]]></category>
		<category><![CDATA[highlights]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[Lambert]]></category>
		<category><![CDATA[OS X]]></category>
		<category><![CDATA[shading]]></category>
		<category><![CDATA[shadows]]></category>
		<category><![CDATA[specular]]></category>

		<guid isPermaLink="false">http://www.ultrajoke.net/?p=480</guid>
		<description><![CDATA[Impetus For a recent project, we needed to &#8220;simulate&#8221; or &#8220;fake&#8221; the look of pieces of paper in a physical environment. We didn&#8217;t need fancy physical modeling or curves and curls and folds, just perspective and shadowing during an animation. &#8230; <a href="http://blog.spacemanlabs.com/2012/03/smshadowedlayer/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<h3>Impetus</h3>
<p>For a recent project, we needed to &#8220;simulate&#8221; or &#8220;fake&#8221; the look of pieces of paper in a physical environment. We didn&#8217;t need fancy physical modeling or curves and curls and folds, just perspective and shadowing during an animation. With OpenGL, perspective and shadowing are dead simple, but animation is hard. With Core Animation, perspective and animation are dead simple, but shadowing is hard. Being loathe to work with a lower-level framework when a high-level one will do, we put together a little CALayer subclass that knows how to render sufficiently accurate shadows on its surface. Note that it doesn&#8217;t do inter-object shadows, just shading based on its angle in space. Since the math is essentially the same, we decided to throw in specular highlights free of charge.</p>
<h3>How Does It Work?</h3>
<p>The math more or less uses the Lambert illumination model. The key concept is that the illumination of a surface is related to the angle between the incoming light ray and the normal of the surface at that point. (Since layers are by definition planar, this is the normal of the layer.) All we do is calculate the dot product of the incoming light with the normal of the layer, and scale the shadow&#8217;s strength by that value. (For simplicity, we model the light as a directional source, shooting into the screen.) This means that the closer the layer is to facing you full-on, the less visible the shadow is. The specular highlight works the same way, except it has a sharper falloff: exponential rather than linear.</p>
<p>I say &#8220;more or less&#8221; because in the true illumination model, this math would change the color of the surface. We can&#8217;t do that for a number of reasons — primarily because you can set an image or other content in a CALayer, and we don&#8217;t want to wipe it out — so instead we put a shadow layer and a highlight layer on top of the base layer, and change their opacity.</p>
<h3>Animation</h3>
<p>To get the shadow and highlight to do the right thing during an implicit animation, we had to find a way to recalculate their opacities during each frame. If we had followed the naive approach, we would have missed changes that occur when, for instance, the layer rotates from facing left to facing right by going through facing center. In that case the shadow should go from dark to light to dark, but an implicit animation would simply go from dark to dark. Instead, we define our own internal animatable property, tell the layer it needs to re-display when that property changes, and then recalculate the shadow math rather than drawing anything in the <code>display</code> method.</p>
<p>We also put in a method to allow users to bypass this extra math, if they know their transform animation won&#8217;t cause this kind of nonlinear change in the shading.</p>
<h3>Enough Talk, Let&#8217;s See It</h3>
<p><code>
	<!-- Begin Video.js -->
	<video id="example_video_id_1066802780" class="video-js vjs-default-skin" width="640" height="471" controls preload="none" data-setup="{}">
		<source src="http://www.blog.spacemanlabs.com/wp-content/uploads/2012/03/shadowedlayer.mov" type='video/mp4' />
		
		
	</video>
	<!-- End Video.js -->
</code></p>
<p>(Why is there a slight stutter in that video? Because I did not take the time to do <a title="CALayer’s Parallel Universe" href="http://www.blog.spacemanlabs.com/2011/08/calayers-parallel-universe/">this</a>.)</p>
<h3>Where Can I Get It?</h3>
<p><a href="http://github.com/Spaceman-Labs/ShadowedLayer">Here</a>. You may want to follow the <a href="https://github.com/Spaceman-Labs">Spaceman Labs Github account</a>, as it will surely have more interesting stuff in the future. It&#8217;s even got some interesting stuff now.</p>
<a href="http://twitter.com/share" class="twitter-share-button" data-url="http://blog.spacemanlabs.com/2012/03/smshadowedlayer/" data-text="SMShadowedLayer" data-count="horizontal">Tweet</a>]]></content:encoded>
			<wfw:commentRss>http://blog.spacemanlabs.com/2012/03/smshadowedlayer/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
<enclosure url="http://www.blog.spacemanlabs.com/wp-content/uploads/2012/03/shadowedlayer.mov" length="1883272" type="video/quicktime" />
		</item>
		<item>
		<title>Countries of the World in an NSArray</title>
		<link>http://blog.spacemanlabs.com/2012/03/countries-of-the-world-in-an-nsarray/?&#038;owa_medium=feed&#038;owa_sid=</link>
		<comments>http://blog.spacemanlabs.com/2012/03/countries-of-the-world-in-an-nsarray/#comments</comments>
		<pubDate>Tue, 06 Mar 2012 23:54:42 +0000</pubDate>
		<dc:creator>Joel Kraut</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[cocoa]]></category>
		<category><![CDATA[code snippet]]></category>
		<category><![CDATA[countries]]></category>
		<category><![CDATA[list]]></category>
		<category><![CDATA[lorem ipsum]]></category>
		<category><![CDATA[NSArray]]></category>
		<category><![CDATA[objective-c]]></category>
		<category><![CDATA[sed]]></category>

		<guid isPermaLink="false">http://www.ultrajoke.net/?p=473</guid>
		<description><![CDATA[Continuing our popular series of lists we&#8217;ve typed in so you don&#8217;t have to (although this one is thanks to sed). Caveats: scraped from a random source on the internet; may not be accurate; provided for entertainment/lorem ipsum purposes only. &#8230; <a href="http://blog.spacemanlabs.com/2012/03/countries-of-the-world-in-an-nsarray/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Continuing our popular series of lists we&#8217;ve typed in so you don&#8217;t have to (although this one is thanks to <code>sed</code>). Caveats: scraped from a random source on the internet; may not be accurate; provided for entertainment/lorem ipsum purposes only.</p>
<p><code>NSArray *countries = [NSArray arrayWithObjects:@"Afghanistan", @"Akrotiri", @"Albania", @"Algeria", @"American Samoa", @"Andorra", @"Angola", @"Anguilla", @"Antarctica", @"Antigua and Barbuda", @"Argentina", @"Armenia", @"Aruba", @"Ashmore and Cartier Islands", @"Australia", @"Austria", @"Azerbaijan", @"The Bahamas", @"Bahrain", @"Bangladesh", @"Barbados", @"Bassas da India", @"Belarus", @"Belgium", @"Belize", @"Benin", @"Bermuda", @"Bhutan", @"Bolivia", @"Bosnia and Herzegovina", @"Botswana", @"Bouvet Island", @"Brazil", @"British Indian Ocean Territory", @"British Virgin Islands", @"Brunei", @"Bulgaria", @"Burkina Faso", @"Burma", @"Burundi", @"Cambodia", @"Cameroon", @"Canada", @"Cape Verde", @"Cayman Islands", @"Central African Republic", @"Chad", @"Chile", @"China", @"Christmas Island", @"Clipperton Island", @"Cocos (Keeling) Islands", @"Colombia", @"Comoros", @"Democratic Republic of the Congo", @"Republic of the Congo", @"Cook Islands", @"Coral Sea Islands", @"Costa Rica", @"Cote d'Ivoire", @"Croatia", @"Cuba", @"Cyprus", @"Czech Republic", @"Denmark", @"Dhekelia", @"Djibouti", @"Dominica", @"Dominican Republic", @"Ecuador", @"Egypt", @"El Salvador", @"Equatorial Guinea", @"Eritrea", @"Estonia", @"Ethiopia", @"Europa Island", @"Falkland Islands (Islas Malvinas)", @"Faroe Islands", @"Fiji", @"Finland", @"France", @"French Guiana", @"French Polynesia", @"French Southern and Antarctic Lands", @"Gabon", @"The Gambia", @"Gaza Strip", @"Georgia", @"Germany", @"Ghana", @"Gibraltar", @"Glorioso Islands", @"Greece", @"Greenland", @"Grenada", @"Guadeloupe", @"Guam", @"Guatemala", @"Guernsey", @"Guinea", @"Guinea-Bissau", @"Guyana", @"Haiti", @"Heard Island and McDonald Islands", @"Holy See (Vatican City)", @"Honduras", @"Hong Kong", @"Hungary", @"Iceland", @"India", @"Indonesia", @"Iran", @"Iraq", @"Ireland", @"Isle of Man", @"Israel", @"Italy", @"Jamaica", @"Jan Mayen", @"Japan", @"Jersey", @"Jordan", @"Juan de Nova Island", @"Kazakhstan", @"Kenya", @"Kiribati", @"North Korea", @"South Korea", @"Kuwait", @"Kyrgyzstan", @"Laos", @"Latvia", @"Lebanon", @"Lesotho", @"Liberia", @"Libya", @"Liechtenstein", @"Lithuania", @"Luxembourg", @"Macau", @"Macedonia", @"Madagascar", @"Malawi", @"Malaysia", @"Maldives", @"Mali", @"Malta", @"Marshall Islands", @"Martinique", @"Mauritania", @"Mauritius", @"Mayotte", @"Mexico", @"Federated States of Micronesia", @"Moldova", @"Monaco", @"Mongolia", @"Montserrat", @"Morocco", @"Mozambique", @"Namibia", @"Nauru", @"Navassa Island", @"Nepal", @"Netherlands", @"Netherlands Antilles", @"New Caledonia", @"New Zealand", @"Nicaragua", @"Niger", @"Nigeria", @"Niue", @"Norfolk Island", @"Northern Mariana Islands", @"Norway", @"Oman", @"Pakistan", @"Palau", @"Panama", @"Papua New Guinea", @"Paracel Islands", @"Paraguay", @"Peru", @"Philippines", @"Pitcairn Islands", @"Poland", @"Portugal", @"Puerto Rico", @"Qatar", @"Reunion", @"Romania", @"Russia", @"Rwanda", @"Saint Helena", @"Saint Kitts and Nevis", @"Saint Lucia", @"Saint Pierre and Miquelon", @"Saint Vincent and the Grenadines", @"Samoa", @"San Marino", @"Sao Tome and Principe", @"Saudi Arabia", @"Senegal", @"Serbia", @"Montenegro", @"Seychelles", @"Sierra Leone", @"Singapore", @"Slovakia", @"Slovenia", @"Solomon Islands", @"Somalia", @"South Africa", @"South Georgia and the South Sandwich Islands", @"Spain", @"Spratly Islands", @"Sri Lanka", @"Sudan", @"Suriname", @"Svalbard", @"Swaziland", @"Sweden", @"Switzerland", @"Syria", @"Taiwan", @"Tajikistan", @"Tanzania", @"Thailand", @"Tibet", @"Timor-Leste", @"Togo", @"Tokelau", @"Tonga", @"Trinidad and Tobago", @"Tromelin Island", @"Tunisia", @"Turkey", @"Turkmenistan", @"Turks and Caicos Islands", @"Tuvalu", @"Uganda", @"Ukraine", @"United Arab Emirates", @"United Kingdom", @"United States", @"Uruguay", @"Uzbekistan", @"Vanuatu", @"Venezuela", @"Vietnam", @"Virgin Islands", @"Wake Island", @"Wallis and Futuna", @"West Bank", @"Western Sahara", @"Yemen", @"Zambia", @"Zimbabwe", nil];</code></p>
<a href="http://twitter.com/share" class="twitter-share-button" data-url="http://blog.spacemanlabs.com/2012/03/countries-of-the-world-in-an-nsarray/" data-text="Countries of the World in an NSArray" data-count="horizontal">Tweet</a>]]></content:encoded>
			<wfw:commentRss>http://blog.spacemanlabs.com/2012/03/countries-of-the-world-in-an-nsarray/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

<!-- Dynamic page generated in 1.295 seconds. -->
<!-- Cached page generated by WP-Super-Cache on 2013-05-20 00:49:06 -->
