<?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>Fiery Robot! &#187; iPhone</title>
	<atom:link href="http://www.fieryrobot.com/blog/category/iphone/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.fieryrobot.com/blog</link>
	<description>Defender of Corporate Headquarters</description>
	<lastBuildDate>Mon, 26 Oct 2009 05:14:23 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>My Journey To Tweetsville</title>
		<link>http://www.fieryrobot.com/blog/2008/11/15/my-journey-to-tweetsville/</link>
		<comments>http://www.fieryrobot.com/blog/2008/11/15/my-journey-to-tweetsville/#comments</comments>
		<pubDate>Sat, 15 Nov 2008 20:31:11 +0000</pubDate>
		<dc:creator>Ed</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[iPhone]]></category>

		<guid isPermaLink="false">http://www.fieryrobot.com/blog/?p=147</guid>
		<description><![CDATA[Now that [Tweetsville][appstorelink] is out there, I thought I&#8217;d reflect a bit about where it came from and how it came to be.
When the app store opened, I downloaded several Twitter clients. Each was interesting in their own right, but I couldn&#8217;t use any of them because of one reason: the scrolling performance. I didn&#8217;t [...]]]></description>
			<content:encoded><![CDATA[<p>Now that [Tweetsville][appstorelink] is out there, I thought I&#8217;d reflect a bit about where it came from and how it came to be.</p>
<p>When the app store opened, I downloaded several Twitter clients. Each was interesting in their own right, but I couldn&#8217;t use any of them because of one reason: the scrolling performance. I didn&#8217;t get why everyone&#8217;s scrolling was not as smooth as any of the built-in iPhone applications. For me, it made the apps frustrating to use, to the point that I just never used them.</p>
<p>[appstorelink]:http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=294887301&#038;mt=8<br />
<span id="more-147"></span><br />
A Simple Proof<br />
&#8212;</p>
<p>As an experiment, I wanted to prove that I could make something that scrolled as good as the Apple-made stuff. I had already done other samples using the Twitter API as part of Blueprint at Yahoo! as well as in a Widget for Konfabulator I worked on but never finished. So I figured it would be fairly quick to throw something together.</p>
<p>I wrote a simple web fetcher and then did a rudimentary list. Originally, it was completely a navigation based app. You&#8217;d get a list of items such as &#8216;With Friends&#8217;, &#8216;History&#8217;, etc. and click those and get the listing of 20 tweets (the default that you get with the API). It was at this point that I started messing with getting the scrolling fast. In the end, it was smooth as silk. But I also didn&#8217;t have features such as the current balloon look and clickable links and @names.</p>
<p>The clickable links took me quite a while to get to work. I spent much time working on line breaking and making sure it was as efficient as it could be, using Instruments to find and eliminate bottlenecks.</p>
<p>After that, I had something that could line break beautifully and yet it still scrolled incredibly fast. Implementing the balloon look however, did slow it down a bit, but again I spent a lot of time trying to cache and get things to the point that you see it today.</p>
<p>But even after all that, there was still tons to do. I mean, it was really just something to prove to myself I could do this. But at this point, I realized I was onto something real here and could really build something exciting. But there were plenty of competition out there. Why build another Twitter app? I wasn&#8217;t really sure, but I kept going anyway to see where it took me. By the time I got to the end, I knew I had something big on my hands.</p>
<p>Making It Real<br />
&#8212;<br />
For the next few weeks I worked pretty much 24/7 getting things to cache properly, push things off onto threads as much as possible, use a real database, etc. and make sure that things just worked correctly. Despite the simplicity of what Twitter is, making an application to really behave properly and ensure that things are always as you expect is pretty darn hard. Even something as &#8217;simple&#8217; as clicking a tweet and then iterating your tweets with the up and down arrows in the upper right was interesting. Not because of the iteration of the items, but because you now have yet another view that needs to be aware of state changes in the application. For example, if you favorite something, I needed to make sure that if it was showing elsewhere in the UI the change was reflected. Good times.</p>
<p>I also rewrote certain sections more than once to get things right. The database for example, was originally just a plist, but the more complicated the UI became, the more necessary SQLite was. And that had to be threaded for even more fun. I had also originally started with MGTwitterEngine, but ended up writing my own engine for two reasons: </p>
<p>   1. design-wise, MGTwitterEngine didn&#8217;t fit with what I needed to to (it was too centralized, I needed many delegates, not just one).<br />
   2. I decided to use JSON instead of XML to try to keep the data smaller and simpler to parse. XML really is a pain to deal with.</p>
<p>And since I didn&#8217;t find a JSON parser that seemed to be small enough and simple enough to integrate, I wrote one. Admittedly, it&#8217;s been something I&#8217;ve wanted to do for a while. I&#8217;m sure somewhere out there there&#8217;s a JSON parser that would have fit the bill, but I didn&#8217;t find it in my cursory search of the interweb tubes.</p>
<p>Details, Details<br />
&#8212;<br />
You know all those &#8217;standard&#8217; form controls you see in all the Apple applications? Yeah, they ain&#8217;t standard. So a bit of time was put into creating my own version of those constructs and making them look and behave as closely to the Apple stuff as I could.</p>
<p>I also needed to handle image uploads and tweet compositions, loading followers so you can help address messages, doing the first run screen, etc. Lots of little things to make stuff right.</p>
<p>And even after I thought I had everything done, I ended up rewriting the messaging section to what you see today. It originally had inbox and sent like the web site, but one of my beta testers suggested a threaded view. At first I was like &#8220;man, that&#8217;s going to take forever&#8221;, but it only took me a day or two to get right. Most of my time in fact was spent drawing the stupid balloons to try and match the SMS app, only this time in delicious purple!</p>
<p>That was the making of 1.0. Right before I handed it over to Tapulous, I also added:</p>
<p>   * Flickr upload support<br />
   * Re-tweet functionality<br />
   * Ability to quit during writing a tweet and come back to where you left off the next time you launch.</p>
<p>I know there are several other thing that people want. I originally did envision adding things like multiple account support, etc. and I see that people also really want to have it save where you last were and not jump to the top when you load tweets. That one actually came up during development and there seemed to be strong arguments on both sides. So I left it as it was, but perhaps Tapulous can add that as an option.</p>
<p>People also have been asking for the ability to tap the status bar and have it jump to the top, but it already does that (the iPhone frameworks do it for you in fact). Perhaps there&#8217;s a bug in there that stops it from working sometimes.</p>
<p>So that&#8217;s my story, and I&#8217;m sticking to it. The best part for me has been seeing people&#8217;s comments on Twitter which echo exactly those attributes I held dear in its inception:</p>
<p>   * UI speed. It tries its best to not get &#8217;stuck&#8217;. It only now gets stuck when it&#8217;s loading the table for the first time. I hope.<br />
   * Simplicity. It&#8217;s not glossy black. It&#8217;s just iPhone.<br />
   * Power. You can do almost everything the Twitter API allows: follow friends, drill down in a user&#8217;s history or followers, search for your favorite terms, etc.</p>
<p>Thanks<br />
&#8212;<br />
I want to thank everyone that helped me test the application and suggested features, etc. This includes Arlo Rose, Rob Marquardt, Karl Adam and Aaron Hurley. These people had much input into the end design. Arlo suggested the use of the Tab Bar over the nav-style interface. Karl caused me to go to threaded messages, Rob and Aaron both suggested many different enhancements to make things more user-friendly and do what you&#8217;d expect. You can blame Rob in particular for my adding Flickr upload. </p>
<p>Again, thank you all. It was a great project!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fieryrobot.com/blog/2008/11/15/my-journey-to-tweetsville/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Tweetsville Update</title>
		<link>http://www.fieryrobot.com/blog/2008/11/04/tweetsville-update/</link>
		<comments>http://www.fieryrobot.com/blog/2008/11/04/tweetsville-update/#comments</comments>
		<pubDate>Tue, 04 Nov 2008 22:51:54 +0000</pubDate>
		<dc:creator>Ed</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[iPhone]]></category>

		<guid isPermaLink="false">http://www.fieryrobot.com/blog/?p=139</guid>
		<description><![CDATA[Just wanted to post an update as to what was happening with Tweetsville, my iPhone Twitter app.
First, it&#8217;s been approved for sale! But I haven&#8217;t seen it show up and sadly I don&#8217;t have a link to it. Read on for why.
The main reason the app was delayed so long coming out is because I [...]]]></description>
			<content:encoded><![CDATA[<p>Just wanted to post an update as to what was happening with Tweetsville, my iPhone Twitter app.</p>
<p>First, it&#8217;s been approved for sale! But I haven&#8217;t seen it show up and sadly I don&#8217;t have a link to it. Read on for why.</p>
<p>The main reason the app was delayed so long coming out is because I had been been trying to figure out exactly what to do with it. I might be in a situation soon that will actually prevent me from being able to maintain the application, so I wanted to see if I could find a good stewart for my baby. I&#8217;ve chosen to hand the application over to [Tapulous][tap], maker of Tap Tap Revenge, Twinkle, FriendBook, Fortune and Collage, with whom I have a good working relation.</p>
<p>[tap]:http://www.tapulous.com</p>
<p>I think this is going to be great for Tweetsville, as they have better resources at their disposal to help support and grow the application. I also think they have some technology which can help Tweetsville be even better than it is. At the same time, there&#8217;s some stuff I did for my app that I believe will benefit Tapulous.</p>
<p>There is no plan to have Twinkle and Tweetsville merge. They are to be separate applications with separate audiences. Twinkle is about locality and the Tapulous network, and Tweetsville is purely a Twitter application.</p>
<p>One of the advantages here for users is that there will be choice. There will ultimately be two versions of Tweetsville. The first will be a paid version currently slated for $3.99. The second will come a few weeks later and will be a free ad-supported version.</p>
<p>I&#8217;m confident this will move is the best thing for the Tweetsville app as well as users in the long run. You&#8217;ll get more choices and better support than I would have been able to provide on my own. I feel the app is in good hands, as I have a great working relationship with Tapulous (I&#8217;m actually doing a little work on Twinkle 1.3 these days too).</p>
<p>So look for it to appear in the App Store on Monday the 10th of November!</p>
<p>**Update** Actually the date is apparently going to be the 12th.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fieryrobot.com/blog/2008/11/04/tweetsville-update/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>The Simple Beauty of clearColor</title>
		<link>http://www.fieryrobot.com/blog/2008/10/22/the-simple-beauty-of-clearcolor/</link>
		<comments>http://www.fieryrobot.com/blog/2008/10/22/the-simple-beauty-of-clearcolor/#comments</comments>
		<pubDate>Thu, 23 Oct 2008 00:21:07 +0000</pubDate>
		<dc:creator>Ed</dc:creator>
				<category><![CDATA[Apple]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[iPhone]]></category>

		<guid isPermaLink="false">http://www.fieryrobot.com/blog/?p=137</guid>
		<description><![CDATA[I&#8217;ve recently discovered the simple beauty of `[UIColor clearColor]` used for a background color. Previously I had been doing stuff like:
    self.opaque = NO;
    self.backgroundColor = nil;
    self.clearsContextBeforeDrawing = YES;
But all I really needed to do was:
    self.backgroundColor = [UIColor clearColor];
Same transparency, and [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve recently discovered the simple beauty of `[UIColor clearColor]` used for a background color. Previously I had been doing stuff like:</p>
<p>    self.opaque = NO;<br />
    self.backgroundColor = nil;<br />
    self.clearsContextBeforeDrawing = YES;</p>
<p>But all I really needed to do was:</p>
<p>    self.backgroundColor = [UIColor clearColor];</p>
<p>Same transparency, and I&#8217;m guessing (hoping?) that it might be a bit more efficient (unless of course, the innards look for clear color and effectively do what I used to).</p>
<p>Hey, even if it&#8217;s not more efficient, it&#8217;s certainly simpler to type and it seems to have the same effect!</p>
<p>Sadly, this is one thing I didn&#8217;t figure out on my own, but rather spied in some other sources, but I wanted to throw it out there for &#8216;all uh yous&#8217;.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fieryrobot.com/blog/2008/10/22/the-simple-beauty-of-clearcolor/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Tweetsville</title>
		<link>http://www.fieryrobot.com/blog/2008/10/11/tweetsville/</link>
		<comments>http://www.fieryrobot.com/blog/2008/10/11/tweetsville/#comments</comments>
		<pubDate>Sat, 11 Oct 2008 17:56:23 +0000</pubDate>
		<dc:creator>Ed</dc:creator>
				<category><![CDATA[Apple]]></category>
		<category><![CDATA[General]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[iPhone]]></category>

		<guid isPermaLink="false">http://www.fieryrobot.com/blog/?p=131</guid>
		<description><![CDATA[Well, it&#8217;s been about two months since I left Yahoo! and I&#8217;ve been spending pretty much literally all my time working on an iPhone application. It&#8217;s nothing super flashy, mind you, but I feel it is the best app of its kind. I had been really disappointed with all the Twitter apps for the iPhone. [...]]]></description>
			<content:encoded><![CDATA[<p>Well, it&#8217;s been about two months since I left Yahoo! and I&#8217;ve been spending pretty much literally all my time working on an iPhone application. It&#8217;s nothing super flashy, mind you, but I feel it is the best app of its kind. I had been really disappointed with all the Twitter apps for the iPhone. Either they were too slow, or scrolling was poor, or it just didn&#8217;t work the way I did. So I set out to write the version I&#8217;d want to use. I ended up with an application called Tweetsville. I&#8217;ve been putting together a small site for it [here][site], so you can check out the features there. I also have set up a [Twitter user][user] you can follow if you care.</p>
<p>[site]:http://www.tweetsville.com<br />
[user]:http://twitter.com/tweetsville</p>
<p>I had three goals in mind, in this order:</p>
<p>   * Totally responsive, with [glassy scrolling][glassy].<br />
   * Embrace the natural aesthetics of the iPhone user experience.<br />
   * Use the full power of the [Twitter API][twitterapi].</p>
<p>[glassy]:http://www.fieryrobot.com/blog/2008/10/01/glassy-scrolling-with-uitableview/<br />
[twitterapi]:http://apiwiki.twitter.com</p>
<p>It&#8217;s definitely fast and smooth (though admittedly it could be tweaked a bit more), and it looks like a real honest-to-goodness iPhone application. For whatever reason, I thought it would be ideal to do this app using as many standard (or standard-looking) controls as I could. The less custom stuff the better.</p>
<p>But the real beauty is the fact that I cache as much of your Twitter stream as I can get from the API, coupled with the depth of what you can do with this application. You can see your timeline, do direct messaging, see your favorites, history, and replies. You can also see the latest trends and do searches, including advanced searches. It is without a doubt the most powerful Twitter iPhone application I&#8217;ve seen to date. Of course, I&#8217;ve had my head in the sand for the past 8 weeks!</p>
<p>Will this application change the world? Hell, no. But I think it will definitely make a dent in the Twitter space. And it was fun, which was really my whole main point of writing it. It&#8217;s been a long while since coding was fun.</p>
<p>Even with all it does, there is still other things I&#8217;d like to do to it to improve the functionality and ease of use beyond what&#8217;s there, but there are only so many hours in the day, and I&#8217;m burnt out from coding so much. I need a vacation from my vacation!</p>
<p>The real question is: should I even charge for it? I&#8217;m currently thinking no, but man, it might really take off, and then I&#8217;ll be missing out. If I don&#8217;t charge for it, I will likely make the source publicly available. I&#8217;ve already got a subversion repository set up and ready for some sources if I do. Or there&#8217;s always code.google.com.</p>
<p>I&#8217;m hoping this will get submitted very very soon. My last build seems to be pretty stable, and I&#8217;m too paranoid to make any real changes now.</p>
<p>Wow, this would be the first thing I&#8217;ve ever written and released like this since&#8230; Aaron (if anyone remembers that). Again, wow.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fieryrobot.com/blog/2008/10/11/tweetsville/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>A Small Trick for Provisioning Profiles</title>
		<link>http://www.fieryrobot.com/blog/2008/10/10/a-small-trick-for-provisioning-profiles/</link>
		<comments>http://www.fieryrobot.com/blog/2008/10/10/a-small-trick-for-provisioning-profiles/#comments</comments>
		<pubDate>Fri, 10 Oct 2008 19:03:42 +0000</pubDate>
		<dc:creator>Ed</dc:creator>
				<category><![CDATA[Apple]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[iPhone]]></category>

		<guid isPermaLink="false">http://www.fieryrobot.com/blog/?p=123</guid>
		<description><![CDATA[As most iPhone developers know, dealing with provisioning files is a bit of a pain. Even more of a pain can be altering your profile and trying to use the altered one.
Well yesterday, after having to do that two or three times, I finally figured out a bit of a clue. I was having trouble [...]]]></description>
			<content:encoded><![CDATA[<p>As most iPhone developers know, dealing with provisioning files is a bit of a pain. Even more of a pain can be altering your profile and trying to use the altered one.</p>
<p>Well yesterday, after having to do that two or three times, I finally figured out a bit of a clue. I was having trouble making xcode see the profile. Turns out, you need to have the current target configuration set to the config you are modifying in the info panel or else it just won&#8217;t show up. Made all the difference in the world.</p>
<p>And of course I always restart xcode after installing any profiles. Install, restart xcode, then ensure your target is set to the right config, then edit the config. Booyah!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fieryrobot.com/blog/2008/10/10/a-small-trick-for-provisioning-profiles/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>More Glassy Scrolling with UITableView</title>
		<link>http://www.fieryrobot.com/blog/2008/10/08/more-glassy-scrolling-with-uitableview/</link>
		<comments>http://www.fieryrobot.com/blog/2008/10/08/more-glassy-scrolling-with-uitableview/#comments</comments>
		<pubDate>Wed, 08 Oct 2008 22:52:28 +0000</pubDate>
		<dc:creator>Ed</dc:creator>
				<category><![CDATA[Apple]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[iPhone]]></category>

		<guid isPermaLink="false">http://www.fieryrobot.com/blog/?p=111</guid>
		<description><![CDATA[[A few days ago][first] I wrote about how to help make your table views scroll more smoothly. The tips and tricks in there definitely work, but I&#8217;ve just added another layer of smoothness on top of that. It&#8217;s rather heavy-handed, but I thought I&#8217;d throw it out there anyway.
[first]:http://www.fieryrobot.com/blog/2008/10/01/glassy-scrolling-with-uitableview/
It starts much the same, use a [...]]]></description>
			<content:encoded><![CDATA[<p>[A few days ago][first] I wrote about how to help make your table views scroll more smoothly. The tips and tricks in there definitely work, but I&#8217;ve just added another layer of smoothness on top of that. It&#8217;s rather heavy-handed, but I thought I&#8217;d throw it out there anyway.</p>
<p>[first]:http://www.fieryrobot.com/blog/2008/10/01/glassy-scrolling-with-uitableview/</p>
<p>It starts much the same, use a single view for your cell and do all your own drawing. The ultimate solution presented here though is to cache the whole damned view contents. Revolutionary? No. Extreme? Yes. But it&#8217;s the smoothest I&#8217;ve been able to get it for my situation. I&#8217;m not yet 100% sure I&#8217;ll ship with this thing turned on, but I think it&#8217;s a valid approach for you to explore for your own project.</p>
<p>In my case I&#8217;m slowed down a bit by database fetches, text layout, and rounded images with shadows. By only following the techniques in my last blog post on this topic it&#8217;s actually not too bad. But with this addition it&#8217;s a lot better.</p>
<p>It should be noted this should really only be used as a last resort due to the increase in memory required.</p>
<p>In order to use this method, you need to be able to uniquely identify a cell by some sort of ID (in my case it&#8217;s an NSNumber).</p>
<p>    &#8211; (void)drawRect:(CGRect)rect<br />
    {<br />
        if ( !_item )<br />
            return;</p>
<p>        UIImage* cached = [sImageCache objectForKey:_item.identifier];<br />
        if ( cached == nil )<br />
        {<br />
            CGRect    bounds = self.bounds;</p>
<p>            UIGraphicsBeginImageContext(<br />
                 CGSizeMake( bounds.size.width, bounds.size.height ) );</p>
<p>            // DRAW CONTENT HERE</p>
<p>            cached = UIGraphicsGetImageFromCurrentImageContext();<br />
            UIGraphicsEndImageContext();</p>
<p>            if ( sImageCache == nil )<br />
                 sImageCache = [[NSMutableDictionary alloc]<br />
 				initWithCapacity:1];</p>
<p>            [sImageCache setObject:cached forKey:_item.identifier];<br />
        }</p>
<p>        [cached drawAtPoint:CGPointMake(0,0)];<br />
    }</p>
<p>This is actually more expensive to draw in two ways:</p>
<p>   1. It means you are drawing and then blitting, instead of just drawing.<br />
   2. The table view caches the contents of cells itself, so technically we&#8217;re buffering yet again.</p>
<p>But the advantage is that we have the caches around for more than just the visible cells (which is all UITableView cares about). So when things scroll back into view, they&#8217;re absolutely smooth as silk.</p>
<p>This does mean that as views first load in they will be less smooth than the next time they roll into view. If the user typically only scrolls in one direction, you might not see a lot of benefit. So you could decide instead to pre-cache items ahead of where the user is. So perhaps you precache the first 20 items and after that let it cache as they are encountered.</p>
<p>When To Dump the Cache<br />
&#8212;</p>
<p>Obviously, memory-wise this is not cheap. How much should you save? When should you flush it?</p>
<p>There are many ways to deal with this: </p>
<p>   1. Let the system tell you when to flush. If you get a `didReceiveMemoryWarning` call, just flush the image cache completely.<br />
   2. Cap the cache at some number of items and flush it when it gets to that size.<br />
   3. Cap the cache at some number of items and drop the oldest item when a new item needs to be added (Least Recently Used).</p>
<p>I&#8217;m actually trying out #1 believe it or not. I also flush the cache when the view has its `viewWillDisappear` method called. Why keep it around if you&#8217;re not actively flushing.</p>
<p>Another trick is to use a timed cache. Wait for the table view to stop scrolling (possibly by listening for the `scrollViewDidEndScrollingAnimation` delegate method to be called. When called, start a 10 second timer. If the view starts scrolling again, clear the timer. This way, after 10 seconds of real non-interaction, you&#8217;ll get the chance to flush the cache. No point holding them around.</p>
<p>A timed cache was what we used in Mac OS X for menus. One of the old stand-bys for measuring &#8216;performance&#8217; was how fast the menus dropped down as you dragged back and forth in the menu bar. So we used the same basic method: on first appearance, cache the image, and from then on use the cache. And then we&#8217;d set a 20 second timer to flush the buffers so we didn&#8217;t waste memory. Since then I&#8217;ve used this technique a number of times.</p>
<p>Here&#8217;s a quick boilerplate for how you could use a timer:</p>
<p>    &#8211; (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView<br />
    {<br />
        // the user is scrolling/dragging, we WANT the cache, don&#8217;t clear it!<br />
        [self.timer invalidate];<br />
        self.timer = nil;<br />
    }</p>
<p>    &#8211; (void)scrollViewDidEndDragging:(UIScrollView *)scrollView<br />
                      willDecelerate:(BOOL)decelerate;<br />
    {<br />
         // if decelerating, wait until it stops first, else start the timer now.<br />
        if ( !decelerate )<br />
        {<br />
		    self.timer = [NSTimer scheduledTimerWithTimeInterval:10.0<br />
                             target:self<br />
                             selector:@selector(flushCaches:)<br />
                             userInfo:nil<br />
                             repeats:NO];<br />
        }<br />
    }</p>
<p>    &#8211; (void)scrollViewDidEndDecelerating:(UIScrollView*)scrollView<br />
    {<br />
        // We know for sure we&#8217;re done, start the timer.<br />
        self.timer = [NSTimer scheduledTimerWithTimeInterval:10.0<br />
                           target:self<br />
                           selector:@selector(flushCaches:)<br />
                           userInfo:nil repeats:NO];<br />
    }</p>
<p>    &#8211; (void)flushCaches:(NSTimer*)timer<br />
    {<br />
        // FINALLY. Flush it.<br />
        [MyFancyCellView flushCaches];<br />
        self.timer = nil;<br />
    }</p>
<p>Anyway, there you have it. Probably the most extreme method to get ultra-smooth scrolling while still using UITableView. Use it wisely.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fieryrobot.com/blog/2008/10/08/more-glassy-scrolling-with-uitableview/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Dealing With Memory Warnings in View Controllers</title>
		<link>http://www.fieryrobot.com/blog/2008/10/03/dealing-with-memory-warnings-in-view-controllers/</link>
		<comments>http://www.fieryrobot.com/blog/2008/10/03/dealing-with-memory-warnings-in-view-controllers/#comments</comments>
		<pubDate>Fri, 03 Oct 2008 23:47:54 +0000</pubDate>
		<dc:creator>Ed</dc:creator>
				<category><![CDATA[Apple]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[iPhone]]></category>

		<guid isPermaLink="false">http://www.fieryrobot.com/blog/?p=107</guid>
		<description><![CDATA[When the iPhone decides you are consuming too much memory (and I&#8217;m not sure what the deciding factors are), it calls your application and ultimately your view controllers&#8217; `didReceiveMemoryWarning` method. In here you&#8217;re supposed to clean any caches, etc. But there&#8217;s a small wrinkle in how you need to handle this in a view controller.
Is [...]]]></description>
			<content:encoded><![CDATA[<p>When the iPhone decides you are consuming too much memory (and I&#8217;m not sure what the deciding factors are), it calls your application and ultimately your view controllers&#8217; `didReceiveMemoryWarning` method. In here you&#8217;re supposed to clean any caches, etc. But there&#8217;s a small wrinkle in how you need to handle this in a view controller.</p>
<p>Is My View Still There?<br />
&#8212;</p>
<p>Basically, there&#8217;s a comment in the template code that says something like:</p>
<p>    	// Releases the view if it doesn&#8217;t have a superview	</p>
<p>This is on the line to `[super didReceiveMemoryWarning];`. Well, this news about your view being released is good, because it means that you can undo what you might have set up in `viewDidLoad`, your go-to place to know your view is there and ready for use. In fact, I always try to do the minimum in any `init` method and save it all for `viewDidLoad`.</p>
<p>The problem is that you don&#8217;t really know if it threw the view out. You can&#8217;t just test `self.view` as this will recreate the view! So I&#8217;ve been using a little trick to help me out:</p>
<p>    &#8211; (void)didReceiveMemoryWarning<br />
    {<br />
        UIView* superview = self.view.superview;</p>
<p>        // Releases the view if it doesn&#8217;t have a superview<br />
        [super didReceiveMemoryWarning];</p>
<p>	    if ( superview == nil )<br />
	    {<br />
            // OK. NOW we can assume view is gone.<br />
         }</p>
<p>         // Destroy any caches regardless here<br />
    }</p>
<p>Essentially I see if the superview was nil before calling the super method. If superview was nil, the view really does go away (unless you have a reference to it of course, in which case things get much more complicated). If the view didn&#8217;t go away, you still might want to destroy any caches anyway, as shown above.</p>
<p>When we know the view is gone, I usually unregister any observers I might have installed, since they tend to operate on the view, which will just bring it back into being, which is bad. If the view is hidden, why bring it back into existence if no one can see it? Plus, aren&#8217;t we trying to use less memory? It should stay dormant until it&#8217;s brought back into the visible world.</p>
<p>Anyway, that&#8217;s what I&#8217;ve learned to do to ensure that my view is gone and I can tear down virtually everything, as if the controller just got initialized. It sure would be nice if we had a `viewWillUnload` method to catch this for realz instead of what amounts to a best guess.</p>
<p>Other Considerations<br />
&#8212;</p>
<p>Even if the approach above worked like a charm, you might have other reasons to keep some data around. I had a situation where I had a view pushed onto a navigation controller that referenced the view underneath. Basically an iterator of the list below it, ala Mail. So I couldn&#8217;t throw away all my data structures like I otherwise might have when the view disappeared. So I ended up employing this:</p>
<p>    // We can only safely purge the world if we are on top.<br />
    BOOL isOnTop = ([self.navigationController topViewController] == self);</p>
<p>And inside the `if (superview == nil)` condition I also checked `isOnTop`. If not, I can&#8217;t destroy everything. </p>
<p>Keep in mind that isOnTop can be true even the view was not visible. In a tab bar it might be the top controller on the non-selected tab, for example. In any case, if you&#8217;re on top you can safely dispose of all of your data structures, as you know there&#8217;s no controllers above you.</p>
<p>So there&#8217;s two ways I cope with memory warnings. As mentioned, they probably won&#8217;t solve 100% of the needs out there, but it might give you some ideas for you own applications.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fieryrobot.com/blog/2008/10/03/dealing-with-memory-warnings-in-view-controllers/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Glassy Scrolling with UITableView</title>
		<link>http://www.fieryrobot.com/blog/2008/10/01/glassy-scrolling-with-uitableview/</link>
		<comments>http://www.fieryrobot.com/blog/2008/10/01/glassy-scrolling-with-uitableview/#comments</comments>
		<pubDate>Wed, 01 Oct 2008 22:26:19 +0000</pubDate>
		<dc:creator>Ed</dc:creator>
				<category><![CDATA[Apple]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[iPhone]]></category>

		<guid isPermaLink="false">http://www.fieryrobot.com/blog/?p=101</guid>
		<description><![CDATA[Now that the [veil of secrecy has been lifted][nda], I think it&#8217;s time to share at least one bit of information that I&#8217;ve gleaned whilst writing my new iPhone application: how to do fast tables.
[nda]:http://developer.apple.com/iphone/program/
**NOTE:** If you&#8217;re not already familiar with how UITableView works, the rest of this might just sound like jibberish. But I [...]]]></description>
			<content:encoded><![CDATA[<p>Now that the [veil of secrecy has been lifted][nda], I think it&#8217;s time to share at least one bit of information that I&#8217;ve gleaned whilst writing my new iPhone application: how to do fast tables.</p>
<p>[nda]:http://developer.apple.com/iphone/program/</p>
<p>**NOTE:** If you&#8217;re not already familiar with how UITableView works, the rest of this might just sound like jibberish. But I think there&#8217;s still valid life advice in here, so please read on!</p>
<p>In fact, one of the reasons I even started to write my app was to prove that I could write an app that does super-fast tables. And in my app (which, yes, is still not-to-be-named), I even do my own rich text layout for every cell. And every cell is a different height. Even I was skeptical that I&#8217;d be able to pull of fast tables, but it worked out. Phew!</p>
<p>How did I accomplish this? Well, by applying a lot of what I&#8217;ve learned over the years. And learning a bit about how UIView performs.</p>
<p>Use Views Sparingly<br />
&#8212;</p>
<p>I&#8217;m not quite sure what&#8217;s going on in UIView. Maybe it&#8217;s that all views are backed by Core Animation layers, but drawing using a bunch of views is a sure-fire way to slow yourself down for straight on scrolling performance.</p>
<p>For simple table views (settings, or profiles, etc.) you probably can get away with using views with subviews. But we want smoothness as the user is scrolling through hundreds of items. In these cases, if you need to write a custom cell, you should use one view that draws everything directly.</p>
<p>Use Opaque Views<br />
&#8212;</p>
<p>If you have a transparent cell, this can slow you down. If a view is marked opaque, the view system knows it doesn&#8217;t have to render the area behind it and blend your view onto it. This seems to make a marked difference at times. I just ran into this myself and switched to filling the view with the background color of the table and it is noticeably smoother now. </p>
<p>Note that this can work against you if you want to use standard cell highlighting, as your item won&#8217;t turn blue since the standard highlighting relies on transparency. But it does work great for instances where the items aren&#8217;t clickable. In my case, it was a message view modeled after the SMS application.</p>
<p>Be Lazy and Cache Often<br />
&#8212;</p>
<p>Quite simply: don&#8217;t do anything until you don&#8217;t need to do it. And when you do need to do it, try to do it only once.</p>
<p>As I mentioned I render rich text. Sadly, there&#8217;s no built-in way to do this at present, so I have to lay the text out myself. This is not really cheap. So I have a layout object that lays itself out and then I keep that object around all pre-calculated. This is very much the way you&#8217;d use something like ATSUI or NSLayoutManager. You do the hard work once, then store it off for later. Then when I draw the cell, I just run along the lines of text and render them. I also calculate the rectangles of everything I will draw ahead of time. Then rendering is just a quick operation.</p>
<p>If you are drawing images, try to cache them at the size you&#8217;ll render them. If you are resizing them every time you draw them, you&#8217;re in for a world of hurt. In my situation, they weren&#8217;t too much bigger than my target size and they&#8217;re never bigger than a certain size, so I didn&#8217;t even bother. But if the images could be any size, it&#8217;d be really important to resize and cache them someplace.</p>
<p>Another thing I needed to do was cache the heights of the cells. If you have a table with variable-height cells, the table view will run through every item in the table and ask it how high it is before it renders anything. In my situation, with all the layout I do, that is pretty costly (it can actually take up to 2 seconds on the device). So I cache the heights. As new items come in and go out of my list, I only need to calc the new items, so after the first reload, all subsequent reloads are really fast since I already have the values.</p>
<p>Another cache I use is for the background for my cells for one of my &#8216;looks&#8217;. In this case, I use `[UIImage stretchableImageWithLeftCapWidth:topCapHeight:]` to give me an image I can stretch across the background. BTW, this is one of the most useful image functions in there. It basically takes an image and separates into a piece that acts as an end cap and a stretchable part. There&#8217;s effectively a one-pixel band that gets repeated as it stretches. But I digress&#8230;</p>
<p>What I ended up doing was taking these images and, because my cells could only be certain heights, as I knew I was using one font size and the cells would be based on the number of lines, there were only a finite number of heights I&#8217;d need (I believe 5). So as I encountered the heights, I created a cached version of the stretched image using code like:</p>
<p>    UIImage* background = [[self balloonImage] stretchableImageWithLeftCapWidth:0<br />
                                               topCapHeight:35];<br />
    UIGraphicsBeginImageContext( CGSizeMake( rect.size.width, rect.size.height ) );</p>
<p>    rect.origin.x = rect.origin.y = 0;<br />
    [background drawInRect:rect];</p>
<p>    image = UIGraphicsGetImageFromCurrentImageContext();</p>
<p>    UIGraphicsEndImageContext(); </p>
<p>And then I just take that image and store it in the cache. Very simple. I also stored that as the class level so all views can use the cache.</p>
<p>Nothing&#8217;s ever free, of course, so keep in mind that if you cache too much, you might find yourself running out of memory. Be prepared to listen to memory warnings via the `didReceiveMemoryWarning` method and dump your cache if needed.</p>
<p>And finally, also keep in mind that the table view only keeps the visible cells around. Every time a cell comes into view, it asks you for a new cell. You can reuse cells, but even that only will get you so much. So don&#8217;t assume that once your list has completely been scrolled through that everything is loaded. You cannot rely on such behavior. This piece of information caused me to change my strategy a bit until I arrived at what I am describing here.</p>
<p>How Do You Know What To Cache?<br />
&#8212;</p>
<p>Fortunately, I&#8217;ve done this for a while, and I know a lot of the gotchas. But no matter how much you know or think you know, the best way to get real answers is to use a tool like Instruments. It is indispensable for finding bottlenecks in your code. For my app, I just kept sampling my application and finding things I should work on. If you aren&#8217;t using sampler or something similar, you really should.</p>
<p>Again: indispensable.</p>
<p>What&#8217;s Next<br />
&#8212;</p>
<p>Next time I might cover performance in general. How to make your applications fast and not impede the user-experience. I&#8217;ll also talk about how to handle low-memory. This can be a bit tricky at times.</p>
<p>Until then!</p>
<p>**UPDATE:** Since writing this I&#8217;ve sped things up a little bit by going [one step further][update].</p>
<p>[update]:http://www.fieryrobot.com/blog/2008/10/08/more-glassy-scrolling-with-uitableview/</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fieryrobot.com/blog/2008/10/01/glassy-scrolling-with-uitableview/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
	</channel>
</rss>
