<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;A0QFQn8zeip7ImA9WhRUEEo.&quot;"><id>tag:blogger.com,1999:blog-38820386</id><updated>2012-01-20T12:08:33.182-05:00</updated><category term="walking while working" /><category term="business" /><category term="personal" /><category term="Xcode" /><category term="Subversion" /><category term="vacation" /><category term="iphone sdk" /><category term="random" /><category term="cocoa touch" /><category term="Apple" /><category term="treadmill desk" /><category term="ideas" /><category term="UISearchDisplayController" /><category term="slickshopper" /><category term="cocoa" /><category term="UITableViewDelegate" /><category term="UINib" /><category term="iphone" /><category term="ugs" /><category term="UITableViewDataSource" /><category term="UIViewController" /><category term="enterprise" /><category term="mileage program" /><category term="solidworks" /><category term="Safari" /><category term="colts" /><category term="Purdue" /><category term="around the web" /><category term="UITableView" /><category term="UITableViewCell" /><category term="Interface Builder" /><category term="nx" /><title>Clinging To Ideas</title><subtitle type="html">Stubbornly standing valiant against the coming tide.</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://clingingtoideas.blogspot.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://clingingtoideas.blogspot.com/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>Brian Slick</name><uri>http://www.blogger.com/profile/16637228173640532515</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>112</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/ClingingToIdeas" /><feedburner:info uri="clingingtoideas" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry gd:etag="W/&quot;A0QFQn8zcCp7ImA9WhRUEEo.&quot;"><id>tag:blogger.com,1999:blog-38820386.post-5786621966085423631</id><published>2012-01-20T12:08:00.001-05:00</published><updated>2012-01-20T12:08:33.188-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-01-20T12:08:33.188-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="treadmill desk" /><category scheme="http://www.blogger.com/atom/ns#" term="walking while working" /><title>Treadmill Desk Review</title><content type="html">&lt;p&gt;I have a few days of walking under my belt now, so it's probably a good time to do a preliminary review of my &lt;a href="http://www.amazon.com/gp/product/B006M2PJV0/ref=as_li_qf_sp_asin_tl?ie=UTF8&amp;amp;tag=clitoide-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=B006M2PJV0"&gt;LifeSpan TR1200-DT Treadmill Desk&lt;/a&gt;&lt;/p&gt;
&lt;p style="font-size: 14px;"&gt;&lt;strong&gt;Unboxing &amp;amp; Assembly&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The set arrived in 2 large boxes, 1 for the treadmill base, and one with the desk parts.  I knew that the desk needed to be put together first, and I have limited space available, so I started with the desk box.  I carefully pulled out and set aside the legs, the cross beams, the desktop, and grabbed a bag of miscellaneous hardware.  Manufacturer Mistake #1: this bag contained a handy little note informing me that assembly instructions could be found... in the other box.  Diabolical bastards.  There is absolutely no reason to put the instructions in the other box.  The treadmill doesn't really need instructions; the desk does.  So I throw all of the packing materials into the box and carry it out to the garbage to clear room for the treadmill box, just so that I can get to the instruction manual.&lt;/p&gt;
&lt;p&gt;The desk is not hard to put together.  The cross beams attach to the legs with a few screws.  The desktop then attaches to the legs with a couple of bolts… wait a minute, where are the bolts?  My hardware baggie is empty.  I &lt;em&gt;*gasp*&lt;/em&gt; refer to the manual.  Sure enough, there is a list of parts, with a specific note indicating these items can be found in the hardware bag.  4 bolts, 4 nuts, nowhere to be found.  I can only assume they were still in the original box, perhaps taped to a piece of styrofoam and I simply overlooked them.  So I head down to the garbage, and carefully remove and inspect every item in there.  Styrofoam, cardboard, plastic wrap, tape.  No bolts.  I begin to wonder if they slipped another baggie into the treadmill box.  Back to the den, and inspect the contents as best as I can without actually removing the treadmill since I don't really have room to get it out of the box yet.  No bolts.  Curse words.  Lightning bolts.  The instruction manual provides the specs of those bolts, so I'm moments away from visiting my nearest hardware store, when out of sheer dumb luck I happen to notice something I had missed before.  The bolts were already installed on the legs.  I had to remove them in order to attach the desktop, then reinstall them.  Manufacturer Mistake #2: If the manual says the parts are in the bag, put them in the damn bag.&lt;/p&gt;
&lt;p&gt;For some reason they want the legs at full height in order to attach the desktop.  Not sure why that made a difference, but it did increase the degree of difficulty a bit.  It would have been handy to have the help of a second person, but I was alone at the time, so I did what I could.  It's not the heaviest thing in the world, but it's not light either, and it was awkward to maneuver into position.  As I reattached &lt;em&gt;*grumble*&lt;/em&gt; the bolts, I tried to judge the stability of the surface.  The legs by themselves provide a good, solid base.  There is only the slightest play side-to-side, and virtually none front-to-back.  As I attached the desktop, I noticed a not-insignificant amount of play front-to-back.  There is simply too much play between the bolts and the holes in the desktop.  I wouldn't call it unstable, but neither is it rock solid.  My computer equipment sits towards the back, so most of the time it leans slightly backwards.  When I lean on it to start typing, it rocks slightly forward.  There are a couple of gaps at the connection, and I intend to shove some shims in there which should address the problem, but it's disappointing that I need to.&lt;/p&gt;
&lt;p&gt;After constructing the desk, all that remains is to slide the treadmill into position and connect a couple of cables.  There is no hard connection between the treadmill and the desk, so this theoretically helps to isolate any treadmill vibration from the desk.  I don't have anything to compare to, but it seems to work just fine.&lt;/p&gt;
&lt;p style="font-size: 14px;"&gt;&lt;strong&gt;Treadmill Features&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The control panel is located at the front of the desk.  I would like it more if it was off to one side, but I can't say that it has really interfered with anything I've tried to do.  It has your basic start/stop/mode/speed up/speed down buttons.  The display offers elapsed time, a step counter, a calorie counter, distance, and speed.&lt;/p&gt;
&lt;p&gt;When the unit is first powered on, it wants you to enter your weight.  This is no problem in and of itself, but it starts at the low end of the scale, and, well, I'm heavy.  It takes a while to get it up to the correct weight, even if I hold the button down.  All that does is require less beeping, it doesn't really go that much faster.  What's worse is that there is seemingly no memory.  I power the treadmill down at night, so each morning I turn it back on and have to re-enter my weight again.  Yes, these are 1st-world problems, but it's annoying nonetheless.&lt;/p&gt;
&lt;p&gt;When you hit the Start button to begin walking, there is a 3,2,1 countdown, and then the treadmill slooooooowly begins to move.  There are 2 factors at play here.  One, there is a slow spool-up, probably in the interest of safety.  That's fine, but I'd like a faster curve.  It…….. is…….. quite……… distracting……….. to………. walk……… so………. slowly……….  But it does eventually get up to speed.  The other factor is that &lt;strong&gt;&lt;em&gt;every&lt;/em&gt;&lt;/strong&gt; time you start walking, the speed has been reset to the slowest speed of 0.5.  So your first steps of the day will be at 0.5.  Later, you've been trucking along at 1.3 and need to take a quick break? When you come back: 0.5.  I can understand not keeping the speed value through a shut down, but just after a pause?  Irritating.&lt;/p&gt;
&lt;p&gt;Oh, and stopping the treadmill is the total opposite of starting it.  When you hit the stop button, it stops NOW.  There is no gentle wind-down, boom, the motor is off, you're done.  So you really need to step onto the side rails before telling it to stop.  I actually would like a longer wind-down period here.&lt;/p&gt;
&lt;p&gt;I haven't watched the step counter over lengthy periods of time, but when I watch it for a few steps at a time, it seems accurate enough.  It's a nice feature that I didn't see mentioned on too many other treadmills.  I can't really comment on the accuracy of the distance measurement, but it is at least internally consistent.  If I walk for an hour at 1.1, then by golly the distance will be 1.1.  The calorie counter seems really high to me.  So far it says I'm averaging around 250 cals/hr, and my average speed is around 1.2.  My earlier readings on desk walking lead me to expect around 125 cals/hr, so either those numbers are off, or these numbers are off.  Or maybe I just burn more calories due to my size.  As far as weight loss expectations, I'm mentally keeping the lower number in mind.  If I start dropping pounds in a big way, then I might be inclined to believe the higher numbers more.&lt;/p&gt;
&lt;p&gt;This may have come off a little more negative than I intended.  The controls are user-friendly, and the treadmill does what it is supposed to do just fine.  There are 2 instances where, if it simply stored a value, the annoyance factor would be reduced tremendously.&lt;/p&gt;
&lt;p&gt;The treadmill is reasonably quiet during operation.  I've had no complaints from the apartment neighbors or the wife.  I tend to wear headphones anyway, so I really don't hear it at all.  Even without the headphones, there is not much noise; certainly not enough to be a distraction.  The sound of me walking is more than the treadmill itself makes.&lt;/p&gt;
&lt;p&gt;LIfeSpan advertises a large walking surface, and they aren't kidding.  I haven't accidentally kicked the side rails while walking.  More than enough room front-to-back, too.  They could easily chop a foot of length off, maybe two.  That said, if you consider the space required by my desk chair, the overall footprint of this treadmill isn't much longer than what I used before.&lt;/p&gt;
&lt;p style="font-size: 14px;"&gt;&lt;strong&gt;Desk Features&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The desk surface could use another 6 inches of width of so.  Right now I have a monitor, a docking station for my laptop, iPad stand, iPhone stand, keyboard and mouse, and a large cup of water.  The back corner of the laptop dock is hanging off the edge by a couple of inches.  I could shift everything over a bit, but then my primary monitor wouldn't be centered.  Eventually I would like to purchase a Mac mini and a second monitor, at which point it would probably be a good idea to get one of those swing arms that holds the monitors up in the air.  Doing that would alleviate any space issues I currently have, but alas it's not in the budget for a while.&lt;/p&gt;
&lt;p&gt;The front edge of the desk has a firm cushion.  I had seen a user review video of a woman using this desk, and she was actually leaning on it putting her forearms on this cushion.  To me that seemed like the desk was too high.  I was expecting to basically hold my arms in the air all day over the keyboard.  I also had an issue last year sitting at tables that were hitting lower on my arm closer to my elbow.  This resulted in tingling in my fingers, and I'd rather not go through that again.  But then when I was getting the desk height adjusted, I found that it actually was pretty comfortable to rest my arms on these cushions.  It's not like I'm typing at chin level or anything, but the desk surface is maybe 2 or 3 inches higher than my elbows.  The height of the cushion actually matches pretty well with my keyboard.  If I start noticing tingling in my fingers again, then I will drop the height pretty quickly, but so far so good.&lt;/p&gt;
&lt;p&gt;The desk has a cable pass-thru hole towards the back, but I'm not sure it's really worth it.  There is a tray under the desk that I guess is supposed to be some kind of cable organizer, but for me it was mostly just in the way.  I dropped a plug through expecting it to hit the floor, when instead it clanked on this tray.  So I had to feel around for the plug and fish it out to get it where it needed to go.  I don't know if the intention is to put the power strip there, but mine wouldn't fit anyway.  Either way, the cables are long enough that the ones that don't need to go very far hang off the back and loop back up.  From an organization and appearance standpoint, it's not really doing much.&lt;/p&gt;
&lt;p&gt;Cable lengths are a problem with a desktop this high.  You basically need about 2 more feet that the manufacturers didn't plan for.  I guess if LifeSpan was going to do anything for cable management, what might have been more useful is some kind of tray or something on the back of the highest cross beam for the legs.  This would be a decent spot to place a power strip, and would have most likely eliminated any cable length issues.  In the absence of that, the smaller desk here actually provided an opportunity.  It is over a foot narrower than my regular desk, and the width at the feet is even narrower still.  This left plenty of room for me to slide in one of my &lt;a href="http://www.ikea.com/us/en/catalog/products/70103085/#/50103086"&gt;IKEA EXPEDIT&lt;/a&gt; shelves.  I have it laying on its side, so it is 2 cubes high.  I moved the power strip onto one of the shelves, which allowed all of the power cords to reach just fine.  It's also where I relocated my network drives, as I was a little leery about putting them on the desk.  My laptop has an SSD, so I'm not too terribly worried about movement there, but the NAS's have regular hard drives, so I'd rather have them on an absolutely-not-moving surface.&lt;/p&gt;
&lt;p&gt;&lt;img style="display: block; margin-left: auto; margin-right: auto;" title="treadmill_desk.jpg" src="http://briterideas.com/blog_images/treadmill_desk.jpg" border="0" alt="treadmill_desk.jpg" /&gt;&lt;/p&gt;
&lt;p style="font-size: 14px;"&gt;&lt;strong&gt;Overall&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I do have a few minor complaints, but taken as a whole, there is nothing that I regret about this purchase so far.  Everything seems well made for the price.  I can't yet speak to the long-term durability of either the treadmill or the desk, but I haven't seen any red flags that give me cause for concern.&lt;/p&gt;
&lt;p&gt;As I write this, the purchase price is still $1299 with free shipping.  This is noted as a sale price, with a list price of $1999.  I have no idea how long the sale price will last, or what the regular price will be, but if they can keep it under $1500, then I suspect they will sell a lot of these to those of us without corporate budgets to dip into.  The warranty is listed as lifetime for the frame, 3 years for motor and parts, and 1 year labor.  One thing to note is that they claim a limit of 3 hours/day (manual actually says 4) of usage, though it isn't clear if this applies only to commercial usage.  Either way, this seems to be a ridiculous limitation.  I'm only supposed to walk at my desk for 3 or 4 hours a day?  What am I doing for the rest of the day?  The welcome letter at the front of the manual says that people sit for 11 hours a day.  In my opinion, if there is going to be a daily usage limit, it should be no less than 8 hours.  I guess we'll find out how much an issue that might be if I ever need warranty service.&lt;/p&gt;
&lt;p&gt;Overall, I'm satisfied with the purchase, and would happily recommend it to anyone thinking about a treadmill desk purchase.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38820386-5786621966085423631?l=clingingtoideas.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/AJkPEGvRl7v54dGFMpzxuPHCZ04/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/AJkPEGvRl7v54dGFMpzxuPHCZ04/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/AJkPEGvRl7v54dGFMpzxuPHCZ04/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/AJkPEGvRl7v54dGFMpzxuPHCZ04/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ClingingToIdeas/~4/UXZFyyGSXqA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://clingingtoideas.blogspot.com/feeds/5786621966085423631/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=38820386&amp;postID=5786621966085423631" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/5786621966085423631?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/5786621966085423631?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ClingingToIdeas/~3/UXZFyyGSXqA/treadmill-desk-review.html" title="Treadmill Desk Review" /><author><name>Brian Slick</name><uri>http://www.blogger.com/profile/16637228173640532515</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://clingingtoideas.blogspot.com/2012/01/treadmill-desk-review.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEYGSHo5eyp7ImA9WhRVFks.&quot;"><id>tag:blogger.com,1999:blog-38820386.post-6566868736773060876</id><published>2012-01-15T17:22:00.001-05:00</published><updated>2012-01-15T17:22:09.423-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-01-15T17:22:09.423-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="treadmill desk" /><category scheme="http://www.blogger.com/atom/ns#" term="walking while working" /><title>Treadmill Desk</title><content type="html">&lt;p&gt;The treadmill desk I bought was this one:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.amazon.com/gp/product/B006M2PJV0/ref=as_li_qf_sp_asin_tl?ie=UTF8&amp;amp;tag=clitoide-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=B006M2PJV0"&gt;LifeSpan TR1200-DT Treadmill Desk&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;But before talking specifically about why, I'd like to talk about some of the other options available and why I didn't get them.  I'm going to list them by the key reason I didn't choose a particular desk, though multiple reasons often applied.&lt;/p&gt;
&lt;p style="font-size: 14px;"&gt;&lt;strong&gt;Too Expensive&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Pretty much every ready-made solution out there could fall into this category, even the LifeSpan.  But there are some that are absolutely egregious.&lt;/p&gt;
&lt;p&gt;Steelcase has 2 different models, depending on what you want.  The &lt;a href="http://store.steelcase.com/products/walkstation/"&gt;Walkstation&lt;/a&gt; is strictly for standing/walking, while the awkwardly-named &lt;a href="http://store.steelcase.com/products/sit-to-walkstation/"&gt;Sit-to-Walkstation&lt;/a&gt; is wide enough that you can put a chair next to the treadmill and use it for times when you don't want to stand or walk.  These things start at $4400, with the bigger one adding another $400.  And that's just for the desk and the treadmill.  Their pictures show numerous accessories which can add hundreds, if not thousands, more.  Now don't get me wrong; I absolutely want one of these. They are great looking desks, I expect them to be sturdy, and I love the back panel and accessory system.  But this is simply too much money, particularly for my first treadmill desk.  If I manage to stick with it for a year or two, I will definitely consider upgrading.  It is just too much money to risk at this point.&lt;/p&gt;
&lt;p&gt;The &lt;a href="http://www.treadmilldeskinc.com/treadmilldesks.html"&gt;Signature 9000&lt;/a&gt; desk at ~$2500 is 1/2 the price of the Steelcases, so could be viewed in that light as a bargain.  I really didn't want to go over $1000, and I definitely didn't want to go over $2000.  Bargain though it may be, it was still too pricey.  Another strike against them is that I'm too heavy for their entry-level treadmill, so I would have needed to get the upgraded treadmill, which was another $600 or so.&lt;/p&gt;
&lt;p style="font-size: 14px;"&gt;&lt;strong&gt;Treadmill-Only&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It is possible to buy a treadmill without all of the vertical gadgetry than an average model in a fitness facility would have.  I don't need big fancy arms or a giant display as these would most likely not be viewable anyway due to the desk, and would probably limit my desk choices.  So if you can find a desk that you like, you don't necessarily have to find some place that sells desk+treadmill combinations.  You can buy a treadmill base, usually with a detached control panel, such as the &lt;a href="http://www.treaddesk.com/thetread.html"&gt;The Tread&lt;/a&gt; by TreadDesk.  It is $820, but then you need to add another $100+ in shipping, so it isn't cheap.  But if you can find or already have a cheap desk, then this is one way to save some money.  I did find a competing model, but it was basically the same price.&lt;/p&gt;
&lt;p&gt;I mention these only as didn't-buys, not wouldn't-buys.  If I hadn't found the LifeSpan, I most likely would have purchased The Tread and one of the desks mentioned below.&lt;/p&gt;
&lt;p style="font-size: 14px;"&gt;&lt;strong&gt;Too Cheap&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I'm a big IKEA fan, and one desk that routinely turns up in standing/walking desk searches is the &lt;a href="http://www.ikea.com/us/en/catalog/products/60111123/"&gt;FREDRIK&lt;/a&gt;.  This is advertised as an adjustable-height desk, and while I suppose that's technically true, it looked to me like you'd have to semi-dismantle it in order to reconfigure.  This is $150 dollars, so not horribly expensive at all, and there is a narrower version that is even less expensive.  I would need to raise the primary desk surface higher than they suggest, but I found numerous examples of people having done exactly that without any problems.&lt;/p&gt;
&lt;p&gt;We took a trip to our nearest IKEA to check it out, fully intending to buy that very night.  They had a couple of floor models set up that I could kick the tires on, and in general I felt that it allowed entirely too much motion.  It was quite a bit wobbly, and since I would set my monitor on the highest shelf, that motion would only be amplified.  I was trying to decide if there was any way I could brace it to reduce motion, but the legs are metal.  If they were wood, I'd probably bolt a couple of braces to it, but I didn't think there was much I could do with metal, and I didn't really want to risk it.  I was a bit disappointed, since this was certainly the least expensive option that I had found, but in my opinion it wasn't sturdy enough.  That said, there are numerous examples of people using these just fine, so your mileage may vary.&lt;/p&gt;
&lt;p&gt;&lt;span style="font-size: 14px;"&gt;&lt;strong&gt;Too Short&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;One thing I found frustrating while looking for adjustable-height desks is that they frequently don't account for a tall person standing on a treadmill.  I would be on the upper-end of the available height range as-is, but when you add 4-6 inches of treadmill, I'm beyond the maximum height by several inches.  Could I make them work?  Possibly.  But I don't see the point in spending lots of money on something that I can already assume isn't suitable.  I don't feel like trying to put the desk up on blocks, or buying additional risers just to get the keyboard up to a decent height.  As I type this, my desk is 52 1/2 inches high.  It might be a little high, so I have room to go down if I need to.  But I also have several more inches I could go up.  When most of the desks I found topped out under 50", I was very nervous that they would be too short.&lt;/p&gt;
&lt;p&gt;A popular choice, naturally, is the &lt;a href="http://www.geekdesk.com/default.asp"&gt;GeekDesk&lt;/a&gt;.  The smaller model has a maximum height of 48".  The larger model (+$200) goes another inch.  Each still under 50", and though they come highly recommended, still just short enough that I don't want to risk that they are too short.  After I spend some time with the LifeSpan, and possibly make some height adjustments, I might find that 49" is sufficient.  But until I know that for a fact, I'm going to choose a desk that has a height range that should easily be accommodating.&lt;/p&gt;
&lt;p&gt;There are numerous other desk models in this genre that largely have the same limitation.  If you're tall (and I'm not a giant, at 6'1") and will be on a treadmill, chances are that the majority of standing desks will be too short.  I've been reading about standing/walking at your desk for a good 8 or 9 months now, and in that time I would say that the concept is increasing in popularity.  If I'm correct about that, then I suspect that the standing desk manufacturers will gradually start upgrading their products to add a few more inches of height.  In my case, GeekDesk probably lost a sale, as the combination of GeekDesk+The Tread was cheaper than the Signature 9000.  But the likelihood of the GeekDesk being too short made it a non-starter for me.&lt;/p&gt;
&lt;p style="font-size: 14px;"&gt;&lt;strong&gt;Powered Adjustment&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is basically a sub-set of "Too Expensive".  The vast majority of adjustable-height desks have powered height adjustment.  While this is convenient, it also adds significant cost to the desk.  You can find non-adjustable standing desks, in some cases as low as a couple hundred bucks.  The GeekDesk might have been the least-expensive powered height one I saw at about $750 + S&amp;amp;H.  That's a lot of money for what is otherwise a very spartan desk.  The regular desk I've been using for years has a hutch, file drawers, a compartment for a tower computer, etc.  Most powered desks have none of these things.  They have relatively basic desktops, those desktops move up and down, and that's about it.  So you're paying hundreds, possibly thousands, of dollars for the ability to adjust the height of your desk with the touch of a button.&lt;/p&gt;
&lt;p&gt;That's all fine and good, but how often are you really going to adjust the height of the desk?  If your intention is to both walk and sit, then you will be adjusting the height a lot, so I can see splurging on the mechanicals to do so easily.  But if your intention is solely to stand or to walk, then chances are there won't be much adjusting at all.  You'll have to get it dialed in at first, so let's say you futz with it a few times during the first month or so.  After that, how often would you change height?  Once a year?  Ever again?  Either way, you've now paid significant money for a motor that sits unused 99.999% of the time.  Unless you know in advance the specific height that you need - in which case you could buy a fixed-height desk and probably save lots of money - then you absolutely do want an adjustable-height desk so that you can find your appropriate height.  But are you really going to change the height so often that the ability to do so needs to be powered?  Probably not.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Just Right&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;All of that leads me to why I chose the &lt;a href="http://www.amazon.com/gp/product/B006M2PJV0/ref=as_li_qf_sp_asin_tl?ie=UTF8&amp;amp;tag=clitoide-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=B006M2PJV0"&gt;LifeSpan&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sufficient height range. They claim that 6'8" people can use this desk.  That's more than enough for me on a treadmill.  Their advertised maximum height is 52", though I just realized I'm currently using it a little above that very height, and still have 3 more clicks available, so I think they are being conservative.&lt;/li&gt;
&lt;li&gt;Reasonable price, mostly.  At $1300 (and it was shipped free from Amazon), it is roughly 1/4 the price of the Steelcase, and about 1/2 the Signature.  It was also cheaper than pretty much ever other combination of standing desk + treadmill that I looked at.  About the only way I could have gone cheaper (with new stuff, I mean) is if the Fredrik desk had been sturdy, and even that would have only saved another $100 or so.  It still feels like a big chunk of money, and if they could get this down to $999 or so they'd have a solid winner on their hands, but as-is I consider the price to be reasonable.&lt;/li&gt;
&lt;li&gt;Manual height adjustment, although I can't say I specifically sought that out as a feature.  Would I like powered adjustment?  Absolutely.  Would I like to add another $500 or more to the price of this desk?  No, thank you.&lt;/li&gt;
&lt;li&gt;Positive reviews across the board on Amazon.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you are the DIY type, you can save yourself boatloads of money over what I've done here.  I didn't care to invest the time that would be required to do so, and I've never built anything that I'd feel comfortable putting my computer on.  Bottom line is that this desk supports my livelihood, now physically as well as financially, and I want it to be a good product.  That's worth a couple of bucks to me.&lt;/p&gt;
&lt;p&gt;And let's face it, I'm taking a risk here.  I already had a bad experience with standing at my desk, but at least that experiment didn't cost me a dime.  So this is a new experiment, and if it is ultimately doomed to fail, I'd like to minimize my cash outlay in the process.  But if having nicer (-than DIY) equipment ultimately contributes to the success of this project, then that will be money well spent.  After considering many options and scenarios, I decided that the LifeSpan fell nicely into the sweet spot between risk and reward.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38820386-6566868736773060876?l=clingingtoideas.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/gwD5aOrcaJUnNXpbPQyf_kZF3xk/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/gwD5aOrcaJUnNXpbPQyf_kZF3xk/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/gwD5aOrcaJUnNXpbPQyf_kZF3xk/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/gwD5aOrcaJUnNXpbPQyf_kZF3xk/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ClingingToIdeas/~4/q8nw1eA6Z94" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://clingingtoideas.blogspot.com/feeds/6566868736773060876/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=38820386&amp;postID=6566868736773060876" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/6566868736773060876?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/6566868736773060876?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ClingingToIdeas/~3/q8nw1eA6Z94/treadmill-desk.html" title="Treadmill Desk" /><author><name>Brian Slick</name><uri>http://www.blogger.com/profile/16637228173640532515</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://clingingtoideas.blogspot.com/2012/01/treadmill-desk.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkYFSHg4fSp7ImA9WhRVFk0.&quot;"><id>tag:blogger.com,1999:blog-38820386.post-1137608207098670614</id><published>2012-01-15T00:08:00.001-05:00</published><updated>2012-01-15T00:08:39.635-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-01-15T00:08:39.635-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="treadmill desk" /><category scheme="http://www.blogger.com/atom/ns#" term="walking while working" /><title>Project Walk-At-My-Desk</title><content type="html">&lt;p&gt;If you follow my Twitter account at all, then you may remember that last year I posted a few times about Project Stand-At-My-Desk.  I had stumbled across an article talking about using a treadmill desk - walking while you work.  The concept intrigued me and I searched around for more information.&lt;/p&gt;
&lt;p&gt;If you decide to do your own searching, you'll quickly discover that desks fall into 2 general categories: 1) DIY, 2) Sell-a-kidney expensive.  I don't trust the resulting quality if I DIM, and I rather like my kidneys.  So for the most part, as quickly as I learned about the concept, I just as quickly lost interest.&lt;/p&gt;
&lt;p&gt;For some reason, the thought wouldn't go away.  At the time, I was walking several miles a day around the neighborhood, and for the most part I enjoyed it.  Bad weather and pain or injury would reduce that enjoyment considerably, and after a while it's just boring.  The whole reason I started walking around the neighborhood in the first place is that I find stationary exercise machines just boring beyond words.  But at the end of the day, I drive a desk for a living.  I spend all day sitting.  Even the hour or so of walking I was getting just couldn't compete with that much inactivity, and I was putting on weight.  And I'm not exactly small to begin with.&lt;/p&gt;
&lt;p&gt;Knowing that I needed to do something, I would continue to read and search about treadmill desks.  For the most part, it was pretty universal that it is rough to get started, but after a couple of weeks it gets better, and after a month or so people wouldn't go back to sitting at a desk.  And in most cases, weight loss over the course of a year or so was not-insignificant.  This sounds like the magic bullet.  I can distract myself from the boring exercise by doing work at the time.&lt;/p&gt;
&lt;p&gt;A semi-universal theme is that people recommend that you don't go straight from sitting to walking.  A common recommendation is to stand at your desk first, and then if that holds up, go to walking.  This sounded like a good compromise, so I managed to find some storage containers and elevate my keyboard and monitor.  This was Project Stand-At-My-Desk.&lt;/p&gt;
&lt;p&gt;Cable management becomes a really big deal when you move things ~2 more feet into the air.  The effort of getting everything rearranged was enough that I couldn't just give up after 5 minutes and go back to sitting.  It required every bit of an hour or two to get everything set up.&lt;/p&gt;
&lt;p&gt;In the end, I lasted about 6 weeks.  I toughed it out for the first couple of weeks, armed with the knowledge that it would be hard, and you just need to get through it.  But the pain never went away.  If anything, it got worse.  I tried different shoes, I tried an anti-fatigue mat, I tried regular breaks.  By the time I finally gave up, I was hobbling around like an 80-yr-old man.  I wasn't feeling any healthier, wasn't any lighter, and just generally was miserable.  So, I admitted defeat, and mostly forgot about it.&lt;/p&gt;
&lt;p&gt;And then &lt;a href="http://iphonedevelopment.blogspot.com/2011/12/brilliantly-simple-idea-treadmill-desk.html"&gt;this&lt;/a&gt; happened.&lt;/p&gt;
&lt;p&gt;I respect Jeff, and it hits a little closer to home when someone I know is trying something I've been thinking about.  I returned to my Googling, and finally found a treadmill desk solution at a price that wasn't too ungodly expensive, and it just arrived yesterday.  I'll talk more about it in the next post.  I'm determined to make it work this time, and I've spent enough money that I'm not inclined to give up easily.&lt;/p&gt;
&lt;p&gt;At the time I gave up standing, I felt pretty confident that the walking would actually be easier.  Even when I was mostly hurting, getting out to take my walk usually felt ok.  All I needed was the treadmill and an appropriate desk, so now I have that.  If it works, then at least in my case, standing before walking was not a good idea.  I never quite got around to blogging about standing, so I'll try to be better about updates regarding the walking.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38820386-1137608207098670614?l=clingingtoideas.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/I2nlrPgWfcUglXdgKqwbZgBhXHE/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/I2nlrPgWfcUglXdgKqwbZgBhXHE/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/I2nlrPgWfcUglXdgKqwbZgBhXHE/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/I2nlrPgWfcUglXdgKqwbZgBhXHE/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ClingingToIdeas/~4/r8LvjJcrhm0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://clingingtoideas.blogspot.com/feeds/1137608207098670614/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=38820386&amp;postID=1137608207098670614" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/1137608207098670614?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/1137608207098670614?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ClingingToIdeas/~3/r8LvjJcrhm0/project-walk-at-my-desk.html" title="Project Walk-At-My-Desk" /><author><name>Brian Slick</name><uri>http://www.blogger.com/profile/16637228173640532515</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://clingingtoideas.blogspot.com/2012/01/project-walk-at-my-desk.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkEESXo-fCp7ImA9WhRWEkw.&quot;"><id>tag:blogger.com,1999:blog-38820386.post-4233093464248658811</id><published>2011-12-29T18:57:00.001-05:00</published><updated>2011-12-29T22:10:08.454-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-12-29T22:10:08.454-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="personal" /><category scheme="http://www.blogger.com/atom/ns#" term="cocoa touch" /><category scheme="http://www.blogger.com/atom/ns#" term="iphone sdk" /><category scheme="http://www.blogger.com/atom/ns#" term="cocoa" /><category scheme="http://www.blogger.com/atom/ns#" term="around the web" /><title>Acknowledgements</title><content type="html">&lt;p&gt;As we finish up what is basically my 2nd full year as an iOS developer, I'd like to take a moment to plug some people who have helped me to become the programmer that I am today.&lt;/p&gt;
&lt;p style="font-size: 13px;"&gt;&lt;strong&gt;Big Nerd Ranch&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I attended the &lt;a href="http://www.bignerdranch.com/classes/ios_iphone_ipad_"&gt;Cocoa Bootcamp&lt;/a&gt; several years ago intending to do Mac programming.  Haven't quite followed up on that yet, but what I learned served me quite well on iOS.  Their latest iOS book is worth checking out:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.amazon.com/gp/product/0321773772/ref=as_li_qf_sp_asin_tl?ie=UTF8&amp;amp;tag=clitoide-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=0321773772"&gt;iOS Programming: The Big Nerd Ranch Guide (2nd Edition)&lt;/a&gt;&lt;/p&gt;
&lt;p style="font-size: 13px;"&gt;&lt;strong&gt;Jeff LaMarche and Dave Mark&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I truly don't know how I would have gotten started without Beginning iPhone Development.  I remember downloading SDK betas and being totally clueless, and it really wasn't until this book came out that I was able to get anywhere.  The success of my career is in no small part attributed to their efforts with this book.  They recently released a brand new version:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.amazon.com/gp/product/1430236051/ref=as_li_tf_tl?ie=UTF8&amp;amp;tag=clitoide-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=1430236051"&gt;Beginning iOS 5 Development: Exploring the iOS SDK&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Jeff's blog is &lt;a href="http://iphonedevelopment.blogspot.com/"&gt;iPhone Development&lt;/a&gt;, and Dave's is &lt;a href="http://www.davemark.com/"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p style="font-size: 13px;"&gt;&lt;strong&gt;Rob Napier&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;After I got past the book and started working on real world stuff, the Big Nerd Ranch mailing list turned out to be invaluable, due in no small part to the participation of folks like Rob.  There are a number of practices that I still follow religiously to this day that originated with Rob's advice.  He was instrumental in helping me to get "over the hump" in my Cocoa learning curve.  He has recently released a book that I haven't had time to read yet, but am eagerly looking forward to:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.amazon.com/gp/product/1119961327/ref=as_li_qf_sp_asin_tl?ie=UTF8&amp;amp;tag=clitoide-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=1119961327"&gt;iOS 5 Programming Pushing the Limits: Developing Extraordinary Mobile Apps for Apple iPhone, iPad, and iPod Touch&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Rob's blog is &lt;a href="http://robnapier.net/blog/"&gt;Cocoaphony&lt;/a&gt;.&lt;/p&gt;
&lt;p style="font-size: 13px;"&gt;&lt;strong&gt;Mark Dalrymple&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Mark is another Big Nerd Ranch mailing list participant who was tremendously helpful during my early days.  He gets a two-fer mention here, as his Objective-C book is quite good:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.amazon.com/gp/product/B001NLL7VG/ref=as_li_tf_tl?ie=UTF8&amp;amp;tag=clitoide-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=B001NLL7VG"&gt;Learn Objective–C on the Mac (Learn Series)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And his newest effort:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.amazon.com/gp/product/B005GWG0L0/ref=as_li_tf_tl?ie=UTF8&amp;amp;tag=clitoide-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=B005GWG0L0"&gt;Advanced Mac OS X Programming: The Big Nerd Ranch Guide&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Mark's blog is &lt;a href="http://borkwarellc.wordpress.com/"&gt;Borkware&lt;/a&gt;.&lt;/p&gt;
&lt;p style="font-size: 13px;"&gt;&lt;strong&gt;Thank You&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Training and Googling can only get you so far.  At some point, there will simply be gaps in the available information, and thus roadblocks to accomplishing your goals.  My hat is off to authors like these who can spend the time to learn the new stuff, and then do a great job of conveying it to everyone else.  And I have tremendous gratitude for the folks who participate in public forums providing considerable amounts of support for free.  It is in their honor that I have spent so much time answering questions on &lt;a href="http://www.iphonedevsdk.com/forum/"&gt;iPhone Dev SDK&lt;/a&gt;, and it is due to their efforts that I have the knowledge and skill required to do so.&lt;/p&gt;
&lt;p&gt;So I would like to express my sincerest thanks to all of the trainers, authors, forum posters, bloggers, support engineers, and anyone else who has helped me along on my journey.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38820386-4233093464248658811?l=clingingtoideas.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/qcuFWRu9EJ5MNj6wvY5HqpGiKjU/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/qcuFWRu9EJ5MNj6wvY5HqpGiKjU/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/qcuFWRu9EJ5MNj6wvY5HqpGiKjU/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/qcuFWRu9EJ5MNj6wvY5HqpGiKjU/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ClingingToIdeas/~4/2IEhzLWYf9g" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://clingingtoideas.blogspot.com/feeds/4233093464248658811/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=38820386&amp;postID=4233093464248658811" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/4233093464248658811?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/4233093464248658811?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ClingingToIdeas/~3/2IEhzLWYf9g/acknowledgements.html" title="Acknowledgements" /><author><name>Brian Slick</name><uri>http://www.blogger.com/profile/16637228173640532515</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://clingingtoideas.blogspot.com/2011/12/acknowledgements.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Dk4ERnY5eSp7ImA9WhdXEEk.&quot;"><id>tag:blogger.com,1999:blog-38820386.post-1804536727493741158</id><published>2011-08-22T16:28:00.001-04:00</published><updated>2011-08-22T16:28:27.821-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-08-22T16:28:27.821-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="personal" /><title>No Longer Pending</title><content type="html">&lt;p&gt;So a piece of junk mail that I got today informed me that I have been granted a patent.  And it turns out to be true.  It's a little odd/sad that the way I find out is through junk mail, but hey, as long as I do find out.&lt;/p&gt;
&lt;p&gt;Read it and weep:  (seriously, patents are painfully boring)&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.patentgenius.com/patent/7996939.html"&gt;Electro-hydraulically powered lift ambulance cot&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Note that the filing date is in 2005, so this is a solid 6+ years in the making.  Heck, I haven't even been with the company for 3 years.  And I'm a co-inventor; I don't mean for this to sound like it is exclusively my work.  It was a good team effort.&lt;/p&gt;
&lt;p&gt;The design described in this patent would eventually become the &lt;a href="http://www.ferno.com/powerflexx/"&gt;PowerFlexx&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38820386-1804536727493741158?l=clingingtoideas.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/1u-V0GJLBp3eqVoYWkHR771CxqA/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/1u-V0GJLBp3eqVoYWkHR771CxqA/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/1u-V0GJLBp3eqVoYWkHR771CxqA/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/1u-V0GJLBp3eqVoYWkHR771CxqA/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ClingingToIdeas/~4/QLcg2RAua_Q" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://clingingtoideas.blogspot.com/feeds/1804536727493741158/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=38820386&amp;postID=1804536727493741158" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/1804536727493741158?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/1804536727493741158?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ClingingToIdeas/~3/QLcg2RAua_Q/no-longer-pending.html" title="No Longer Pending" /><author><name>Brian Slick</name><uri>http://www.blogger.com/profile/16637228173640532515</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://clingingtoideas.blogspot.com/2011/08/no-longer-pending.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0cHR3Y6cSp7ImA9Wx9aFEo.&quot;"><id>tag:blogger.com,1999:blog-38820386.post-1955557820121803323</id><published>2011-03-07T00:17:00.001-05:00</published><updated>2011-03-07T00:17:16.819-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-03-07T00:17:16.819-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="UITableViewCell" /><category scheme="http://www.blogger.com/atom/ns#" term="Interface Builder" /><category scheme="http://www.blogger.com/atom/ns#" term="UITableViewDelegate" /><category scheme="http://www.blogger.com/atom/ns#" term="UITableView" /><category scheme="http://www.blogger.com/atom/ns#" term="cocoa touch" /><category scheme="http://www.blogger.com/atom/ns#" term="iphone sdk" /><category scheme="http://www.blogger.com/atom/ns#" term="Xcode" /><category scheme="http://www.blogger.com/atom/ns#" term="cocoa" /><category scheme="http://www.blogger.com/atom/ns#" term="UITableViewDataSource" /><category scheme="http://www.blogger.com/atom/ns#" term="iphone" /><category scheme="http://www.blogger.com/atom/ns#" term="UINib" /><title>UITableView How-To: Part 5 - More About XIB-Based Cells</title><content type="html">&lt;a href="http://clingingtoideas.blogspot.com/2010/01/uitableview-how-to-part-1-view.html"&gt;Part 1&lt;/a&gt; | &lt;a href="http://clingingtoideas.blogspot.com/2010/02/uitableview-how-to-part-2-search.html"&gt;Part 2&lt;/a&gt; | &lt;a href="http://clingingtoideas.blogspot.com/2011/02/uitableview-how-to-part-3-multiple.html"&gt;Part 3&lt;/a&gt; | &lt;a href="http://clingingtoideas.blogspot.com/2011/03/uitableview-how-to-part-4-xib-based.html"&gt;Part 4&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;(This is really the second half of Part 4, so we'll just continue with the same project)&lt;br /&gt;&lt;br /&gt;I've got one more key topic to address, then we'll take a look at some alternate techniques.&lt;br /&gt;&lt;br /&gt;By the end of Part 4, we created a custom view layout using IB, so we've got the visual part down.  That really just leaves one more basic characteristic to talk about: actions.  In addition to customizing the look of your cell, you can customize the behavior of your cell, and this is most easily done by adding controls such as buttons, switches, and so on.  We're just going to do a quick button, but the concept applies to any of the other controls you may wish to use.&lt;br /&gt;&lt;br /&gt;So, open up CustomTableCell.xib and drop a button smack in the middle:&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 325px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part5/add_button.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;With a typical view controller, we'd connect this button to an IBAction.  That's what we want to do here as well, but the question is: where is the IBAction?  There are 2 basic options, in the cell class or in the view controller class.  Where you should put it will depend on the goal you are trying to accomplish.  If you put it in the cell class, then it really isn't any different than wiring it up as you would in a view controller.  Depending on your goal, this approach can either simplify or complicate your ultimate outcome.  But for our purposes here, we're going to put the IBAction in the view controller.  So add this declaration to BasicViewController.h:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;- (IBAction)buttonPressed:(UIButton *)button;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;(you are probably accustomed to seeing (id)sender for these things.  Go ahead and do that if you prefer.  I like the type to be correct, and rarely have a need to mix these methods with multiple types of controls)&lt;br /&gt;&lt;br /&gt;Now we just need to wire it up.  You're going to drag from the button to File's Owner like you normally would.  The only difference is that this is happening in the cell class instead of the view controller class.&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 358px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part5/ibaction_1.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 358px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part5/ibaction_2.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;All that's left is to make the method do something.  You can do a simple stub for now to make sure that this works:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;- (IBAction)buttonPressed:(UIButton *)button&lt;br /&gt;{&lt;br /&gt;   NSLog(@"It worked!");&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Go ahead and run the app, and you should see this message in the console when you hit any button.  And now we reach the fun part.  As you can see, this method fires when you hit &lt;em&gt;any&lt;/em&gt; button.  So, how do you know &lt;em&gt;which&lt;/em&gt; button?&lt;br /&gt;&lt;br /&gt;There are a couple of ways to answer this question and, like so many programming challenges, which way to go will depend on your situation.  You could use the tag property of the button.  This is a common thing to do when you have multiple buttons and would like to distinguish between them.  You would need to declare a property in the cell for the button, and then you could assign the tag value in cellForRow like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;[[cell theButton] setTag:[indexPath row]];&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Then you extract the tag value in the IBAction method:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;NSLog(@"Pressed button at row: %d", [button tag]);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;For many tables, this will be an adequate and simple approach.  However, if you recall some behaviors from Part 3 regarding multi-section tables, then your spidey sense should be warning you that something may be wrong here.  We are using the row parameter to identify the cell, but what happens if there is more than one section?  You could have row 0 in the first section, and row 0 in the second section, and row 0 in the third section...  Remember that row indexes reset in each section.  This is why NSIndexPath provides TWO parameters - section and row - because only one parameter is insufficient to find/describe the location.&lt;br /&gt;&lt;br /&gt;UIButton doesn't have an NSIndexPath property.  If you really wanted to, you could subclass UIButton to add that property.  Then the approach above would be the same, except you would provide/extract the indexPath instead of merely the row.  I personally tend to view subclassing as a last resort, so I've never bothered doing this.  But I suppose I could see some situations where BSTableButton could come in handy.  But that isn't why I've brought you here today.&lt;br /&gt;&lt;br /&gt;No matter what the approach, you have to do some work.  Above we've done a little work in cellForRow, and a little work in the IBAction method.  (And if you subclassed, you've done a lot of work in even more places)  Also think in terms of performance.  You are flicking your way through a table, and you want to get in and out of cellForRow as fast as possible.  Assigning the tag (or indexPath) is just one more thing that has to be done that is preventing you from leaving the method ASAP.  So instead of doing the work for each cell, just do the work when the user actually hits the button.  The question is how.&lt;br /&gt;&lt;br /&gt;In the IBAction, I receive the button itself as a parameter.  From there, I would like to wind up with an indexPath.  This will allow me to know that the 5th button was pressed, so I can go do something to the 5th item in the array.  A quick glance at the UITableView documentation reveals a handful of methods that return an indexPath.  For all visible rows.  For the selected row (before you get excited, this is only for the blue highlighted row, not for tapping on items in the row).  The only one that seems useful in this situation is indexPathForCell:.  If I have a cell, the table can tell me where it is located.&lt;br /&gt;&lt;br /&gt;Ok, so how do I get to the cell from the button.  Ah ha!  Remember that these are all views, and that they have been arranged into an hierarchy.  The button would be a &lt;strong&gt;sub&lt;/strong&gt;view of the cell, so since I'm starting with the button, I would need to walk UP the hierarchy.  You do that with the superview method.&lt;br /&gt;&lt;br /&gt;This part can require some learning and trial-and-error, so let's do it the hard way before we skip to the answer.  Add this to the IBAction:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;NSLog(@"superview is: %@", [[button superview] description]);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Run it and you should see something like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;superview is: &amp;lt;UITableViewCellContentView: 0x4d31390; frame = (0 0; 320 43); layer = &amp;lt;CALayer: 0x4d313f0&amp;gt;&amp;gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Hrm, UITableViewCellContentView.  We're looking for CustomTableCell, so that's not a match.  If you look at the top of the UITableViewCell documentation, you'll find the contentView property mentioned, and then you can go read its description.  Basically, what we added the button to was the contentView, not the cell directly.  So we need to go higher in the hierarchy:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;NSLog(@"superview is: %@", [[button superview] description]);&lt;br /&gt;NSLog(@"superview superview is: %@", [[[button superview] superview] description]);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Try again:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;superview is: &amp;lt;UITableViewCellContentView: 0x4d31390; frame = (0 0; 320 43); layer = &amp;lt;CALayer: 0x4d313f0&amp;gt;&amp;gt;&lt;br /&gt;superview superview is: &amp;lt;CustomTableCell: 0x4b3ad70; baseClass = UITableViewCell; frame = (0 264; 320 44); autoresize = W; layer = &amp;lt;CALayer: 0x4b39e20&amp;gt;&amp;gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Bingo.  So, with this particular hierarchy, to go from the button to the cell, we need to go up 2 levels in the view hierarchy.  This will depend on your actual view hierarchy, which is why I showed the long steps first.  Just keep walking up the hierarchy until you find the cell.  (I'm sure you could come up with an algorithm to keep walking, but it's never been worth the effort for me)  Now that we know where we're going, our actual line of code is:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;CustomTableCell *cell = (CustomTableCell *)[[button superview] superview];&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We have the cell, and can now get the indexPath:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;NSIndexPath *indexPath = [[self mainTableView] indexPathForCell:cell];&lt;br /&gt;NSLog(@"indexPath is: %@", [indexPath description]);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now you have the exact same location that you would have in cellForRow.  So you can access your array the same way, and go manipulate your data or otherwise make something happen as you see fit.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Cell Identifiers Suck&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;I mentioned in Part 4 that I don't like the way Apple put the cell identifier into the cell's XIB.  About a year ago, I discovered &lt;a href="http://iphonedevelopment.blogspot.com/2010/04/table-view-cells-redux.html"&gt;a blog post by Jeff LaMarche&lt;/a&gt; that provides a workaround.  I won't bother repeating what he has said there, so let's just apply the code with a little twist.&lt;br /&gt;&lt;br /&gt;Add this to CustomTableCell.h:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;+ (NSString *)reuseIdentifier;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And then add these to CustomTableCell.h:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;- (NSString *)reuseIdentifier&lt;br /&gt;{&lt;br /&gt;   return [[self class] reuseIdentifier];&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;+ (NSString *)reuseIdentifier&lt;br /&gt;{&lt;br /&gt;   return NSStringFromClass([self class]);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;It's the same thing Jeff shows, I just took out the constant declaration as mentioned in the comments.  This allows for easy copy-pasting into any cell class.&lt;br /&gt;&lt;br /&gt;You can now blank out the identifier setting in IB, or forget to include it altogether.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;And Now For Something New&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;There are a variety of debates regarding XIB-based cells.  First there are the usual arguments with the "I only use code, IB is the debbil" people.  Then there is another layer for use in cells, with people arguing that XIBs are slower than code because you have to go do disk to load the cell.  There must have been some truth to this, because Apple added a new class for OS 4 to address it.  It's too bad it took so long, because if anything we needed it more in slower-hardware days of OS 2 than we do today, but they felt it was important enough to create, so we should at least give it a look.&lt;br /&gt;&lt;br /&gt;Now presenting: UINib.&lt;br /&gt;&lt;br /&gt;Read the documentation and you'll find that it is basically a caching and instantiation class for loading XIBs.  You can use it for any kind of XIB loading, but you can be pretty confident that it was developed for table cells.  It doesn't wind up making a tremendous difference in terms of code - if anything it adds some - but I'll take Apple at their word that this is faster in terms of performance.  I like using UINib as a property, so add this to BasicViewController.h:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;@interface BasicViewController : UIViewController &amp;lt;UITableViewDelegate, UITableViewDataSource&amp;gt;&lt;br /&gt;{&lt;br /&gt;   UITableView *ivMainTableView;&lt;br /&gt;   CustomTableCell *ivCustomTableCell;&lt;br /&gt;   &lt;strong&gt;UINib *ivCustomTableCellNib;&lt;/strong&gt;&lt;br /&gt;}&lt;br /&gt;@property (nonatomic, retain) IBOutlet UITableView *mainTableView;&lt;br /&gt;@property (nonatomic, retain) IBOutlet CustomTableCell *customTableCell;&lt;br /&gt;&lt;strong&gt;@property (nonatomic, retain) UINib *customTableCellNib;&lt;/strong&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And then I implement it with a custom getter in the .m file:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;@synthesize customTableCellNib = ivCustomTableCellNib;&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;// dealloc&lt;br /&gt;[ivCustomTableCellNib release], ivCustomTableCellNib = nil;&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;- (UINib *)customTableCellNib&lt;br /&gt;{&lt;br /&gt;   if (ivCustomTableCellNib == nil)&lt;br /&gt;   {&lt;br /&gt;      ivCustomTableCellNib = [[UINib nibWithNibName:NSStringFromClass([CustomTableCell class]) bundle:nil] retain];&lt;br /&gt;   }&lt;br /&gt;   return ivCustomTableCellNib;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I customize the getter so that I don't have to care who creates it or when.  The first time it is needed, it will get created.&lt;br /&gt;&lt;br /&gt;So we've added a property, and we've added a method.  What's the payoff?  Well, not much really.  Here is what we had before:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;if (cell == nil)&lt;br /&gt;{&lt;br /&gt;   [[NSBundle mainBundle] loadNibNamed:@"CustomTableCell" owner:self options:nil];&lt;br /&gt;   cell = [self customTableCell];&lt;br /&gt;   [self setCustomTableCell:nil];&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This now becomes:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;if (cell == nil)&lt;br /&gt;{&lt;br /&gt;   [[self customTableCellNib] instantiateWithOwner:self options:nil];&lt;br /&gt;   cell = [self customTableCell];&lt;br /&gt;   [self setCustomTableCell:nil];&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Wow.  We changed one line of code.  Yay.  Well, this is more about performance than it is about code reduction.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;A Couple Of Tweaks&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;Speaking of performance, there is one thing you want to be careful of in cell design, whether it is in code or via XIB: avoid transparency.  With our fancy iPhone 4's and iPad 2's, this is becoming less and less of an issue, but the basic premise remains the same: the device likes opacity a whole lot more than it does transparency.&lt;br /&gt;&lt;br /&gt;What this specifically means as far as the view items go is to turn on the Opaque toggle, and supply a background color.  The Opaque toggle is here:&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 597px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part5/opaque_1.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;When you first turn it on, the items will often default to black.  Simply apply a different color:&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 321px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part5/opaque_2.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;Naturally this won't work if your background is not consistent, like a gradient or an image.  In those cases, you're pretty much stuck with the transparency.&lt;br /&gt;&lt;br /&gt;The next thing to be aware of isn't performance related, but it is very visual so you'll want to make sure to catch it.  For labels (and some others like image views) you will want to specify a highlight color:&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 271px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part5/highlight.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;This is the color that the text will be when the user highlights a row.  Generally speaking you will want it to be white, but that's a design decision for you.  At some point in the last couple releases of Xcode, and I'm not sure when this started, the default automatically became white.  So hopefully you shouldn't have to mess with this too much.  But if you're on a slightly older version of Xcode, be aware that the default used to be black.  Regardless, if you tap a cell, and don't see nice white text, this is where you need to go to fix it.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Holy Crap, This Is Long&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;Now you know why I split this into 2 parts.  Head back to the cell's XIB, and we're going to make a slight change.  Make the cell taller.  You can add more stuff if you want, but for immediate purposes, all I want is something like this:&lt;br /&gt;&lt;br /&gt;&lt;!-- bigcell --&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 322px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part5/bigcell.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;Depending on how you set up the label's masks, when you run the app now you should get something like this:&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 332px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part5/bigcell_bad.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;Well that's no good.  What happened here?  I made the cell taller, why isn't it displaying that way?  Ah, because nobody told the table that the row height should change.  So we need to do that.&lt;br /&gt;&lt;br /&gt;The &lt;strong&gt;wrong&lt;/strong&gt; answer is to use this delegate method:&lt;br /&gt;&lt;pre&gt;tableView:heightForRowAtIndexPath:&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This method should ONLY be used if your table will feature more than 1 row height.  If you have tall rows and short rows and everything in between, then this method is pretty much your only option.  But if your rows are all the same height, then you will slam against this method over and over and over again (seriously, add some logs and watch how much it gets called) just to return the same number each time.&lt;br /&gt;&lt;br /&gt;Fortunately, there is a better answer: UITableView has a rowHeight property.  In fact, we've already utilized it, we just didn't realize it.  Open up the BasicViewController.xib, select the table view, then hit Cmd-3.  Right there at the top of the inspector: row height of 44.0.  So we could simply change this number to match whatever our cell height is, and things will be good.&lt;br /&gt;&lt;br /&gt;Or will they?  Hard-coding numbers is generally something to avoid.  And really, we could change the cell design again, and change the height again, and then we just forget to come back and change this setting.  Wouldn't it be nice if we could establish a link between our cell height and our row height?&lt;br /&gt;&lt;br /&gt;We can't quite get there directly, but we sure can take a measurement and use that to define the row height.&lt;br /&gt;&lt;br /&gt;So first we need a cell.  viewDidLoad is a good place to do this, and we are literally going to do the same thing we do in cellForRow to get a cell:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;[[self customTableCellNib] instantiateWithOwner:self options:nil];&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;From what we learned before, we know that the cell property is now occupied.  So we can grab that cell, take a measurement, and drive the table:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;[[self customTableCellNib] instantiateWithOwner:self options:nil];&lt;br /&gt;[[self mainTableView] setRowHeight:[[self customTableCell] frame].size.height];&lt;br /&gt;[self setCustomTableCell:nil];&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We get the frame of the cell, then get the height, and give that value to the rowHeight property.  Then we clear out the cell property since we're done with it.&lt;br /&gt;&lt;br /&gt;Try again:&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 332px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part5/bigcell_good.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;Muuuuch better.  Now you can change the cell's height as much as you want, and the table will always display the correct row height.&lt;br /&gt;&lt;br /&gt;Thus concludes a couple of lengthy posts on this topic.  But don't let that volume scare you away from using XIB-based cells.  After you've run through the process a few times, it gets less intimidating.  And once you're there, you get to take full advantage of Interface Builder's promise to make view layout easier.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://homepage.mac.com/brianslick/blogpics/tableviews/part5/TableViewTutorial_Part5.zip"&gt;TableViewTutorial_Part5.zip&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38820386-1955557820121803323?l=clingingtoideas.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/I1hdJluZZVup9nb4ChnjuH1fI-M/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/I1hdJluZZVup9nb4ChnjuH1fI-M/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/I1hdJluZZVup9nb4ChnjuH1fI-M/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/I1hdJluZZVup9nb4ChnjuH1fI-M/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ClingingToIdeas/~4/8K8VTBY4ZzA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://clingingtoideas.blogspot.com/feeds/1955557820121803323/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=38820386&amp;postID=1955557820121803323" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/1955557820121803323?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/1955557820121803323?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ClingingToIdeas/~3/8K8VTBY4ZzA/uitableview-how-to-part-5-more-about.html" title="UITableView How-To: Part 5 - More About XIB-Based Cells" /><author><name>Brian Slick</name><uri>http://www.blogger.com/profile/16637228173640532515</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>3</thr:total><feedburner:origLink>http://clingingtoideas.blogspot.com/2011/03/uitableview-how-to-part-5-more-about.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUAERn45fCp7ImA9WhZTEkQ.&quot;"><id>tag:blogger.com,1999:blog-38820386.post-5652337341920199990</id><published>2011-03-04T22:30:00.000-05:00</published><updated>2011-03-16T13:48:27.024-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-03-16T13:48:27.024-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="UITableViewCell" /><category scheme="http://www.blogger.com/atom/ns#" term="Interface Builder" /><category scheme="http://www.blogger.com/atom/ns#" term="UITableViewDelegate" /><category scheme="http://www.blogger.com/atom/ns#" term="UITableView" /><category scheme="http://www.blogger.com/atom/ns#" term="cocoa touch" /><category scheme="http://www.blogger.com/atom/ns#" term="iphone sdk" /><category scheme="http://www.blogger.com/atom/ns#" term="cocoa" /><category scheme="http://www.blogger.com/atom/ns#" term="UITableViewDataSource" /><category scheme="http://www.blogger.com/atom/ns#" term="iphone" /><category scheme="http://www.blogger.com/atom/ns#" term="UINib" /><title>UITableView How-To: Part 4 - XIB-Based Cells</title><content type="html">&lt;a href="http://clingingtoideas.blogspot.com/2010/01/uitableview-how-to-part-1-view.html"&gt;Part 1&lt;/a&gt; | &lt;a href="http://clingingtoideas.blogspot.com/2010/02/uitableview-how-to-part-2-search.html"&gt;Part 2&lt;/a&gt; | &lt;a href="http://clingingtoideas.blogspot.com/2011/02/uitableview-how-to-part-3-multiple.html"&gt;Part 3&lt;/a&gt; | &lt;a href="http://clingingtoideas.blogspot.com/2011/03/uitableview-how-to-part-5-more-about.html"&gt;Part 5&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Up to this point in the series, the focus has been on basic table and data structures.  Now let's take a look at customizing the appearance of the table view.&lt;br /&gt;&lt;br /&gt;I'm not intending to spend much time talking about standard cells, but there actually are a number of ways to customize cells without needing to subclass.  UITableViewCell's initWithStyle: method accepts a parameter for which Apple has provided several standard options.  You can show text in several places, and you can add images and accessories.  Take a quick glance at the Settings app on your phone; those are standard cells.  So before you dive into subclassing cells, make sure you are aware of the built-in options.  They can save you a lot of time.  This post at &lt;a href="http://cocoawithlove.com/2009/04/easy-custom-uitableview-drawing.html"&gt;Cocoa With Love&lt;/a&gt; is definitely worth reading.&lt;br /&gt;&lt;br /&gt;But, let's assume that those standard configurations are inadequate for your awesome table design.  Or perhaps you've seen another app that displays lots of crazy things in a table, and you wonder how it was done.  Chances are that the answer will be the same either way: custom table cells.  Like most visual things in Cocoa, there is a code-based approach and an Interface Builder-based approach.  I'm going to focus on using IB, as the layout is significantly easier, although the setup has some nuances.  This approach is based on the Apple sample project called TaggedLocations.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Overview&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;Let's take a quick look at the players involved, because the process to set this up is a tad convoluted:&lt;br /&gt;&lt;br /&gt;ViewController.h&lt;br /&gt;ViewController.m&lt;br /&gt;ViewController.xib&lt;br /&gt;&lt;br /&gt;TableCell.h&lt;br /&gt;TableCell.m&lt;br /&gt;TableCell.xib&lt;br /&gt;&lt;br /&gt;In the same way that you would reference a button in your view by creating an IBOutlet, the same thing will be done here.  So the view controller will have an IBOutlet for the cell:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;@property (nonatomic, retain) IBOutlet TableCell *tableCell;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In order to do this without causing any build errors, the cell class needs to exist.  But this outlet needs to exist in order to to complete the TableCell.xib, so there is something of a chicken-and-egg situation.  The basic steps are:&lt;br /&gt;&lt;br /&gt;1. Create the view controller class&lt;br /&gt;2. Create the cell class&lt;br /&gt;3. Create the IBOutlet in the view controller&lt;br /&gt;4. Design the cell&lt;br /&gt;5. Rewire the table delegate methods to use the cell&lt;br /&gt;&lt;br /&gt;So there is a fair amount of back-and-forth between the classes, but after you get used to it, it's not so bad.  Let's begin...&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;1.  Create a view controller.&lt;/strong&gt;  I'm calling this one BasicViewController, but you can use whatever you want.  Don't bother getting too hung up with delegate methods for now.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;2.  Add a new file.&lt;/strong&gt;  This will be a UITableViewCell subclass, and it's not immediately clear how to do this. Just choose Objective-C class, and then select UITableViewCell from the pull-down menu:&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 470px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part4/cell_subclass.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;Call it CustomTableCell.  This will create the .h and .m file, but we want a XIB file, too.  So add a new file for that as well.  Select User Interface at the side, and then choose a View XIB.  iPhone-vs-iPad doesn't really matter, but I tend to go with iPhone.&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 541px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part4/create_xib.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;Give it the same CustomTableCell name.  You should now have .h, .m, and .xib files for CustomTableCell.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;3.  Now create an IBOutlet for the cell in the view controller.&lt;/strong&gt;  You will need @class in the .h file and #import in the .m class.  The highlights of what you should wind up with are:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;// BasicViewController.h&lt;br /&gt;#import &amp;lt;UIKit/UIKit.h&amp;gt;&lt;br /&gt;@class CustomTableCell;&lt;br /&gt;&lt;br /&gt;@interface BasicViewController : UIViewController &amp;lt;UITableViewDelegate, UITableViewDataSource&amp;gt;&lt;br /&gt;{&lt;br /&gt;   UITableView *ivMainTableView;&lt;br /&gt;   CustomTableCell *ivCustomTableCell;&lt;br /&gt;}&lt;br /&gt;@property (nonatomic, retain) IBOutlet UITableView *mainTableView;&lt;br /&gt;@property (nonatomic, retain) IBOutlet CustomTableCell *customTableCell;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;// BasicViewController.m&lt;br /&gt;#import "CustomTableCell.h"&lt;br /&gt;&lt;br /&gt;@implementation BasicViewController&lt;br /&gt;&lt;br /&gt;@synthesize mainTableView = ivMainTableView;&lt;br /&gt;@synthesize customTableCell = ivCustomTableCell;&lt;br /&gt;&lt;br /&gt;- (void)dealloc&lt;br /&gt;{&lt;br /&gt;   [ivMainTableView release], ivMainTableView = nil;&lt;br /&gt;   [ivCustomTableCell release], ivCustomTableCell = nil;&lt;br /&gt;   [super dealloc];&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;4.  Configure the cell.&lt;/strong&gt;  Open up CustomTableCell.xib, as we have some preliminary things to take care of.  You should see:&lt;br /&gt;File's Owner&lt;br /&gt;First Responder&lt;br /&gt;View&lt;br /&gt;&lt;br /&gt;Select View and delete it.  Now go to your Library palette, find the Table View Cell, and drag it to the spot where View was.&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 322px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part4/xib_setup_1.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;Now we need to change classes.  Select File's Owner, and go to the inspector panel for Identity (Cmd-4).  Change the class to BasicViewController.  Now select the table view cell, and in the inspector change the class to CustomTableCell.&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 594px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part4/xib_setup_2.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;Now we need to connect the IBOutlet we made earlier.  Ctrl-click on File's Owner.  Select the customTableCell outlet, and drag that to the CustomTableCell item.&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 480px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part4/xib_setup_3.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;Hey, wait a second... this isn't my view controller's XIB!  You are correct.  But the view controller will create this class - it will be the file's owner - so it does make some sense to do this.  But don't get carried away.  For example, you do see the view outlet, but don't mess with that here as that is being populated in your view controller's XIB.  We are doing this for one reason and at this point one reason only: to get access to that cell IBOutlet.&lt;br /&gt;&lt;br /&gt;There is a lot of customization we could potentially do here, but in the interest of quickly moving along to see this in action, simply drop a couple of a labels onto the cell.  We'll come back later to wire everything up.&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 327px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part4/xib_setup_4.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;5.  Configure the table delegate methods.&lt;/strong&gt;  Return to BasicViewController, and for now tell it there are 10 rows.  The important change happens in cellForRow:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;- (UITableViewCell *)tableView:(UITableView *)tableView&lt;br /&gt;cellForRowAtIndexPath:(NSIndexPath *)indexPath&lt;br /&gt;{&lt;br /&gt;   static NSString *CellIdentifier = @"CellIdentifier";&lt;br /&gt;&lt;br /&gt;   CustomTableCell *cell = (CustomTableCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];&lt;br /&gt;   if (cell == nil)&lt;br /&gt;   {&lt;br /&gt;      [[NSBundle mainBundle] loadNibNamed:@"CustomTableCell" owner:self options:nil];&lt;br /&gt;      cell = [self customTableCell];&lt;br /&gt;      [self setCustomTableCell:nil];&lt;br /&gt;   }&lt;br /&gt;   return cell;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;For the most part, this is pretty similar to what we've done before.  Instead of a UITableViewCell, we're using a CustomTableCell, so we change the class types to reflect that.  We're still asking for an available cell, and if one isn't available then we create one.  The creation part is different than we've done before, naturally since we aren't just using code this time.&lt;br /&gt;&lt;br /&gt;The first thing we do is load the XIB file.  We indicate which class, and who should own it.  The next thing we do is wave our hands and say there is some black magic happening here.  Then we assign the cell property to our cell variable.  Hrm, what?  How did that get there?  Well, let's back up and address that black magic.&lt;br /&gt;&lt;br /&gt;The documentation has this to say about loadNibNamed:owner:options:&lt;br /&gt;&lt;blockquote&gt;During the loading process, this method unarchives each object, initializes it, sets its properties to their configured values, and &lt;strong&gt;reestablishes any connections to other objects&lt;/strong&gt;.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;For our purposes, the important part is the last bit.&lt;br /&gt;&lt;br /&gt;Think about what you do with a normal view controller.  You place, say, an image view in IB, you create an IBOutlet for it, and then make the connection in IB.  So after you instantiate your view controller, what happens?  The XIB is loaded, which means an image view is also instantiated, and this image view is then assigned to your IBOutlet/property.  When you go to talk to your image view using the property - [[self imageView] setImage:...]; - the image view &lt;em&gt;is&lt;/em&gt; there already.&lt;br /&gt;&lt;br /&gt;What we're doing here is exactly the same, only splitting things up into separate files.  Instead of an image view, it's a table cell, and instead of being in the view controller's XIB, it is in a separate XIB.  But the act of loading the XIB causes the outlets to be populated, so we end up with the same result.  Black magic indeed.&lt;br /&gt;&lt;br /&gt;So:&lt;br /&gt;1.  We load the XIB file&lt;br /&gt;2.  The IBOutlet gets populated with the cell&lt;br /&gt;3.  We assign that cell to our local cell variable&lt;br /&gt;4.  We clear out the property since we don't need to keep it around&lt;br /&gt;&lt;br /&gt;Go ahead and run the app at this point, and if you've wired everything correctly, you should see:&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 327px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part4/test_run.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Configure the cell&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;Let's circle back around and finish up the cell.  First of all, we skipped a really important step.  Recall what the initializer for a standard cell looks like:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We pass two parameters: a style, AND a reuse identifier.  Now compare to how we just created the cell:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;[[NSBundle mainBundle] loadNibNamed:@"CustomTableCell" owner:self options:nil];&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Uh oh, no reuse identifier parameter.  That's a problem if we want good table scrolling performance (and we do).  I wish Apple would have handled this differently, but what they did is imbed the reuse identifier into the XIB file.  So open it back up, and select the cell.  Then go to the inspector panel again, Cmd-1.&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 647px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part4/reuse_identifier.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;It will be blank when you first look at it, so you will need to type it in.  The important part here, and one unfortunate aspect of Apple's decision to do this, is that whatever you type in here, needs to match what you type in here:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;static NSString *CellIdentifier = @"&lt;strong&gt;CustomCellIdentifier&lt;/strong&gt;";&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So you have to make sure the same thing is typed in 2 places, and if you screw up either one then you won't recycle cells.  This will hurt scrolling performance.  I really wish Apple had gone with more of an initWithNibName:bundle:reuseIdentifier: approach for these cells.  Oh well.&lt;br /&gt;&lt;br /&gt;Let's add some properties to the cell so that we can talk to the labels.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;// CustomTableCell.h&lt;br /&gt;@interface CustomTableCell : UITableViewCell&lt;br /&gt;{&lt;br /&gt;   UILabel *ivRedLabel;&lt;br /&gt;   UILabel *ivGreenLabel;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@property (nonatomic, retain) IBOutlet UILabel *redLabel;&lt;br /&gt;@property (nonatomic, retain) IBOutlet UILabel *greenLabel;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;// CustomTableCell.m&lt;br /&gt;&lt;br /&gt;@implementation CustomTableCell&lt;br /&gt;&lt;br /&gt;@synthesize redLabel = ivRedLabel;&lt;br /&gt;@synthesize greenLabel = ivGreenLabel;&lt;br /&gt;&lt;br /&gt;....&lt;br /&gt;&lt;br /&gt;- (void)dealloc&lt;br /&gt;{&lt;br /&gt;   [ivRedLabel release], ivRedLabel = nil;&lt;br /&gt;   [ivGreenLabel release], ivGreenLabel = nil;&lt;br /&gt;   [super dealloc];&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Standard stuff here.  Where it gets tricky is actually making the connections in IB.  You are probably accustomed to dragging from File's Owner to establish IBOutlet connections.  Ah, but remember which class we're dealing with here.  File's Owner is the view controller, but we are adding these IBOutlets to the cell.  So you can drag from File's Owner all that you want, but you won't be able to create the links.  You have to drag from the cell class to the labels:&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 360px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part4/xib_setup_5.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;Now let's head back to the view controller and put some data in the labels.  I'm not going to bother setting up any data; refer to Part 3 for some thoughts on how to arrange your data for this purpose.  For now, just drop in something so that you can see different text in each field:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;   ...&lt;br /&gt;&lt;br /&gt;   NSUInteger row = [indexPath row];&lt;br /&gt;   [[cell redLabel] setText:[NSString stringWithFormat:@"Red %d", row]];&lt;br /&gt;   [[cell greenLabel] setText:[NSString stringWithFormat:@"Green %d", row]];&lt;br /&gt;	&lt;br /&gt;   return cell;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;With standard cells, you are talking to [cell textLabel] or [cell detailTextLabel].  Same idea, just using the properties that you've created.&lt;br /&gt;&lt;br /&gt;Your cell is a blank canvas ready to be customized to your heart's delight.  Want 5 labels?  Good.  Want 10 images?  Great.  Go nuts.&lt;br /&gt;&lt;br /&gt;This is turning into a longer post than I thought, and I still have a lot to talk about.  So I'm going to split this up into 2 posts.  Tune in later for the sequel.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://homepage.mac.com/brianslick/blogpics/tableviews/part4/TableViewTutorial_Part4.zip"&gt;TableViewTutorial_Part4.zip&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38820386-5652337341920199990?l=clingingtoideas.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/Rb5AUpqWlXuJhOm-sL1-RTQs5Lo/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Rb5AUpqWlXuJhOm-sL1-RTQs5Lo/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/Rb5AUpqWlXuJhOm-sL1-RTQs5Lo/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Rb5AUpqWlXuJhOm-sL1-RTQs5Lo/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ClingingToIdeas/~4/fXci-5wjZ4o" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://clingingtoideas.blogspot.com/feeds/5652337341920199990/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=38820386&amp;postID=5652337341920199990" title="5 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/5652337341920199990?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/5652337341920199990?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ClingingToIdeas/~3/fXci-5wjZ4o/uitableview-how-to-part-4-xib-based.html" title="UITableView How-To: Part 4 - XIB-Based Cells" /><author><name>Brian Slick</name><uri>http://www.blogger.com/profile/16637228173640532515</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>5</thr:total><feedburner:origLink>http://clingingtoideas.blogspot.com/2011/03/uitableview-how-to-part-4-xib-based.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUEMRHw7eip7ImA9WhZTEkQ.&quot;"><id>tag:blogger.com,1999:blog-38820386.post-268067928830993938</id><published>2011-02-02T22:47:00.001-05:00</published><updated>2011-03-16T13:48:05.202-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-03-16T13:48:05.202-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Interface Builder" /><category scheme="http://www.blogger.com/atom/ns#" term="UITableViewDelegate" /><category scheme="http://www.blogger.com/atom/ns#" term="UITableView" /><category scheme="http://www.blogger.com/atom/ns#" term="cocoa touch" /><category scheme="http://www.blogger.com/atom/ns#" term="iphone sdk" /><category scheme="http://www.blogger.com/atom/ns#" term="Xcode" /><category scheme="http://www.blogger.com/atom/ns#" term="cocoa" /><category scheme="http://www.blogger.com/atom/ns#" term="UITableViewDataSource" /><category scheme="http://www.blogger.com/atom/ns#" term="iphone" /><title>UITableView How-To: Part 3 - Multiple Sections</title><content type="html">&lt;a href="http://clingingtoideas.blogspot.com/2010/01/uitableview-how-to-part-1-view.html"&gt;Part 1&lt;/a&gt; | &lt;a href="http://clingingtoideas.blogspot.com/2010/02/uitableview-how-to-part-2-search.html"&gt;Part 2&lt;/a&gt; | &lt;a href="http://clingingtoideas.blogspot.com/2011/03/uitableview-how-to-part-4-xib-based.html"&gt;Part 4&lt;/a&gt; | &lt;a href="http://clingingtoideas.blogspot.com/2011/03/uitableview-how-to-part-5-more-about.html"&gt;Part 5&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;In my experience so far, people seem to have a knack for making multi-section table views harder than they really are.  The key to simplifying things is to prepare your data in such a way that pain is removed from your table delegate methods.  If your delegate methods are nothing but switch/case statements, then you've likely given yourself a pretty good headache.&lt;br /&gt;&lt;br /&gt;Let's quickly revisit some key elements from Part 1.  To determine the number of rows, we did this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;NSInteger rows = [[self contentsList] count];&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;...and to get the information to show in the cell, we did this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;NSString *contentForThisRow = [[self contentsList] objectAtIndex:[indexPath row]];&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We count up everything we have, and then we use the row parameter to extract the specific piece of information.  That's fine when we have one continuous list, but if you want a sectioned display, then you don't have one continuous list anymore.  And this is true regardless of the display type.&lt;br /&gt;&lt;br /&gt;There are two default display options for table views.  Plain:&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 621px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part3/plain_table_view.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;And grouped:&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 621px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part3/grouped_table_view.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;This is purely visual fluff.  You can go back to the exercise in Part 1 if you want and flip it to grouped, and nothing else needs to change.  The easiest way to manage this sectioned/grouped appearance is to group your data as well.  The long list of 500 names that you have in your address book needs to be broken up into pieces.&lt;br /&gt;&lt;br /&gt;How you actually get your data broken up is a programming exercise that I'm not going to go into here.  There are lots of ways to do it, and the 'correct' approach will depend on your actual data and your specific needs.  What I will show here are two different structures that you can use that simplify the delegate methods considerably.  They are:&lt;br /&gt;&lt;br /&gt;&lt;OL&gt;&lt;br /&gt;&lt;LI&gt;An array of arrays&lt;/LI&gt;&lt;br /&gt;&lt;LI&gt;A dictionary, and an array of keys&lt;/LI&gt;&lt;br /&gt;&lt;/OL&gt;&lt;br /&gt;But first we need to introduce a delegate method that we haven't seen or used yet.  In Part 1, we answered the question "how many rows in this section".  The second half of that question is important, as we never indicated how many sections there are.  This is yet another question the table view can ask, and it is:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This is an optional method, as evidenced by the fact we didn't use it in the previous exercises.  But clearly the table works without it, so what gives?  As noted in the documentation, the default value is 1.  So the tables we've seen so far actually were sectioned table views, just with only 1 section.&lt;br /&gt;&lt;br /&gt;Just like we did for the number of rows, we should probably base the answer on a calculation.  If you have a simple table that will only ever have 2 sections, then by all means go ahead and hard-code a 2.  But if the number could change, it needs to be related to your data somehow.  More on that in a moment.&lt;br /&gt;&lt;br /&gt;For now, I'm going to take the code from Part 1, remove a couple of colors, and then set the number of sections to 3.&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 335px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part3/repeated_rows.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;Notice that the rows repeat; I have 3 groups of the same thing over and over again.  Why is that?  Well, let's remember how we collected the data to display:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;NSString *contentForThisRow = [[self contentsList] objectAtIndex:[indexPath row]];&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Using the row alone, we pull data from the array.  Why does it repeat?  Because the row numbering starts over for &lt;em&gt;each section&lt;/em&gt;.  Section and row numbers look like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Section 0&lt;br /&gt;   Row 0&lt;br /&gt;   Row 1&lt;br /&gt;   Row 2&lt;br /&gt;Section 1&lt;br /&gt;   Row 0&lt;br /&gt;   Row 1&lt;br /&gt;   Row 2&lt;br /&gt;Section 2&lt;br /&gt;   Row 0&lt;br /&gt;   Row 1&lt;br /&gt;   Row 2&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Three sections, so the table view asked for something to display in Row 0 three times.  And that's exactly what we gave it: the first item in the array, 3 separate times.&lt;br /&gt;&lt;br /&gt;This is why arranging the data in a particular way is important.&lt;br /&gt;&lt;br /&gt;How do we find our way around in the table?  Well, we've already seen this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;[indexPath row]&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now we also need to use this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;[indexPath section]&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;NSIndexPath actually does a lot more than this, but for most iPhone purposes it is used to describe a section and row location in a table view.  In Part 1, we used the row parameter to select an item from the array.  We are still going to do that, but we will now use the section parameter to decide &lt;em&gt;which&lt;/em&gt; array.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Array of Arrays&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;As previously stated, the key is arranging your data in a way to facilitate a sectioned table view.  We'll keep the same contentsList array that we had before, but we'll change the contents.  Before, it contained only strings.  Now, it will contain arrays.  Those arrays will contain strings.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;NSArray *firstSection = [NSArray arrayWithObjects:@"Red", @"Blue", nil];&lt;br /&gt;NSArray *secondSection = [NSArray arrayWithObjects:@"Orange", @"Green", @"Purple", nil];&lt;br /&gt;NSArray *thirdSection = [NSArray arrayWithObject:@"Yellow"];&lt;br /&gt;	&lt;br /&gt;NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:firstSection, secondSection, thirdSection, nil];&lt;br /&gt;[self setContentsList:array];&lt;br /&gt;[array release], array = nil;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Same basic idea as before, but we've added some structure.  We need to make adjustments to the delegate methods to account for this new structure.  First, our new delegate method for number of sections:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView&lt;br /&gt;{&lt;br /&gt;   NSInteger sections = [[self contentsList] count];&lt;br /&gt;	&lt;br /&gt;   return sections;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This is the same calculation we started with, but now we are answering a different question.  In Part 1, this calculation was for the number of rows.  Now it is the number of sections.  So far so good.  Now we need to define the number of rows.  You'll note I set up the arrays so that each one has a different number of objects.  This is to help reinforce that these numbers probably shouldn't be hard-coded.  You want everything to work whether your array has 5 objects or 500 objects.&lt;br /&gt;&lt;br /&gt;In Part 1, the number of rows was the number of items in the main array.  That is no longer the case.  We must first identify which sub-array we're interested in, and then count that sub-array.  There was a parameter we ignored before:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;- (NSInteger)tableView:(UITableView *)tableView&lt;br /&gt;numberOfRowsInSection:(NSInteger)&lt;strong&gt;section&lt;/strong&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;How many rows are in &lt;em&gt;this&lt;/em&gt; section?  We've told the table how many sections there will be, and the table will now call this method for each section, passing in the appropriate value.  We'll use this to identify which array we want.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;NSArray *sectionContents = [[self contentsList] objectAtIndex:section];&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;For the first section in the table, I want a reference to the first sub-array in the main array.  Second array for the second section, and so on.  The number of rows is then the count of this sub-array.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;- (NSInteger)tableView:(UITableView *)tableView&lt;br /&gt;numberOfRowsInSection:(NSInteger)section&lt;br /&gt;{&lt;br /&gt;   NSArray *sectionContents = [[self contentsList] objectAtIndex:section];&lt;br /&gt;   NSInteger rows = [sectionContents count];&lt;br /&gt;	&lt;br /&gt;   return rows;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So it is the same idea as what we did before, we just have to count a different array each time.  It's not horribly complicated, you just have to plan for it.&lt;br /&gt;&lt;br /&gt;We use this same concept again to determine what the row contents are.  The only difference is that we get to the section value through the indexPath parameter.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;NSArray *sectionContents = [[self contentsList] objectAtIndex:[indexPath section]];&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now we do the same thing we did before, using the row parameter, but using this array instead of the main one.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;- (UITableViewCell *)tableView:(UITableView *)tableView&lt;br /&gt;cellForRowAtIndexPath:(NSIndexPath *)indexPath&lt;br /&gt;{&lt;br /&gt;   NSArray *sectionContents = [[self contentsList] objectAtIndex:[indexPath section]];&lt;br /&gt;   NSString *contentForThisRow = [sectionContents objectAtIndex:[indexPath row]];&lt;br /&gt;   ...&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;After this, nothing is different than what was done in Part 1.  Feed this string into the cell, and you should be good to go.  If everything is wired up correctly, then you should see:&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 335px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part3/result_array_of_arrays.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;That's really all there is to it.  Once you have this structure in place, you can add or remove as many colors as you want - to/from each section - and you don't have to mess with the delegate methods anymore.  We added 1 delegate method, and 1 line of code each to two existing delegate methods (plus the extra stuff for setting up the data) vs. what we had in Part 1.  Easy!&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;A dictionary, and an array of keys&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;Again there are many possible ways to structure your data, so I offer this next one merely as another example.  But it is handy if you want even more data in your table view, specifically headers.  If you look at the address book, you'll see letters for each group of people - A's, B's, etc. - and this data has to be set up somewhere, somehow.&lt;br /&gt;&lt;br /&gt;Dictionaries store data using keys, typically strings.  So you store something by name, and you retrieve something by name.  Those names can be easily used as section headers.  The problem is that dictionaries do not have order.  There is no first object, second object, etc., and tables really like for things to be in order.  So in addition to using the dictionary, we will continue to use an array to provide order.&lt;br /&gt;&lt;br /&gt;Again, how you set up the data is pretty important.  So let's start with the basics, we have a dictionary and an array:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;@interface DictionaryViewController : UIViewController &amp;lt;UITableViewDelegate, UITableViewDataSource&amp;gt;&lt;br /&gt;{&lt;br /&gt;   UITableView *ivMainTableView;&lt;br /&gt;	&lt;br /&gt;   NSMutableArray *ivSectionKeys;&lt;br /&gt;   NSMutableDictionary *ivSectionContents;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@property (nonatomic, retain) IBOutlet UITableView *mainTableView;&lt;br /&gt;@property (nonatomic, retain) NSMutableArray *sectionKeys;&lt;br /&gt;@property (nonatomic, retain) NSMutableDictionary *sectionContents;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I've kinda shown my hand here with the names.  The dictionary will hold the contents of each section, and the array will hold the keys.  The contents will be arrays, just like in the previous example.  We're just going to access them in a different way.  The data is prepared like so:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;NSMutableArray *keys = [[NSMutableArray alloc] init];&lt;br /&gt;NSMutableDictionary *contents = [[NSMutableDictionary alloc] init];&lt;br /&gt;	&lt;br /&gt;NSString *colorKey = @"Colors";&lt;br /&gt;NSString *clothingKey = @"Clothing";&lt;br /&gt;NSString *miscKey = @"Misc";&lt;br /&gt;	&lt;br /&gt;[contents setObject:[NSArray arrayWithObjects:@"Red", @"Blue", nil] forKey:colorKey];&lt;br /&gt;[contents setObject:[NSArray arrayWithObjects:@"Pants", @"Shirt", @"Socks", nil] forKey:clothingKey];&lt;br /&gt;[contents setObject:[NSArray arrayWithObjects:@"Wankle Rotary Engine", nil] forKey:miscKey];&lt;br /&gt;	&lt;br /&gt;[keys addObject:clothingKey];&lt;br /&gt;[keys addObject:miscKey];&lt;br /&gt;[keys addObject:colorKey];&lt;br /&gt;	&lt;br /&gt;[self setSectionKeys:keys];&lt;br /&gt;[self setSectionContents:contents];&lt;br /&gt;	&lt;br /&gt;[keys release], keys = nil;&lt;br /&gt;[contents release], contents = nil;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This should look reasonably similar to what we did before.  We've added a dictionary, and you add data to a dictionary differently than you do an array, but otherwise it is the same idea.  If you're paying attention to details (and as a programmer, you should be) then you'll notice that the order I added the keys is different than the order I added the arrays.  I only did this to illustrate that the order of the dictionary doesn't matter, and the order of the &lt;em&gt;array&lt;/em&gt; is what will be driving the table.&lt;br /&gt;&lt;br /&gt;After this, the approach is pretty similar to what we did before.  We need to tell the table how many sections:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView&lt;br /&gt;{&lt;br /&gt;   NSInteger sections = [[self sectionKeys] count];&lt;br /&gt;	&lt;br /&gt;   return sections;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Since the dictionary and the array have the same number of objects, I could have counted either one.  But typically you'll want to use the array.  If I'm testing various arrangements, I will often make the contents the same regardless, and observe differences by messing with the keys.  Don't want colors today?  Just don't add the key to the array, and nothing else needs to change.&lt;br /&gt;&lt;br /&gt;Now we need to provide the number of rows.  This is the same approach as last time, just going through the dictionary.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;- (NSInteger)tableView:(UITableView *)tableView&lt;br /&gt;numberOfRowsInSection:(NSInteger)section&lt;br /&gt;{&lt;br /&gt;   NSString *key = [[self sectionKeys] objectAtIndex:section];&lt;br /&gt;   NSArray *contents = [[self sectionContents] objectForKey:key];&lt;br /&gt;   NSInteger rows = [contents count];&lt;br /&gt;&lt;br /&gt;   return rows;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We grab the key using the section parameter, then grab the sub-array using that key.  Same thing for the row contents:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;- (UITableViewCell *)tableView:(UITableView *)tableView&lt;br /&gt;cellForRowAtIndexPath:(NSIndexPath *)indexPath&lt;br /&gt;{&lt;br /&gt;   NSString *key = [[self sectionKeys] objectAtIndex:[indexPath section]];&lt;br /&gt;   NSArray *contents = [[self sectionContents] objectForKey:key];&lt;br /&gt;   NSString *contentForThisRow = [contents objectAtIndex:[indexPath row]];&lt;br /&gt;   ...&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;At this point, we've essentially recreated the first example.  But we went this way for a reason, and that reason is section headers.  There is another delegate method:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;- (NSString *)tableView:(UITableView *)tableView&lt;br /&gt;titleForHeaderInSection:(NSInteger)section&lt;br /&gt;{&lt;br /&gt;   NSString *key = [[self sectionKeys] objectAtIndex:section];&lt;br /&gt;   &lt;br /&gt;   return key;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We've already seen how to grab the key, so we simply do that and use the key as the header.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Food For Thought&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;Usually when I see rookie attempts at multi-section tables, there is a lot of code like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;if (section == 0)&lt;br /&gt;{&lt;br /&gt;   ...&lt;br /&gt;}&lt;br /&gt;else if (section == 1)&lt;br /&gt;{&lt;br /&gt;   ...&lt;br /&gt;}&lt;br /&gt;...&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;For a simple structure: Ok, it probably doesn't make a big difference&lt;BR&gt;For a complex structure: No, just no&lt;br /&gt;&lt;br /&gt;This is paving the way for a modification nightmare.  We've only looked at cellForRow so far, but you're going to do the same thing in didSelectRow (we'll get to that later), too.  If you decide to rearrange things, you've got to remember everywhere that it is supposed to change, and of course you'll forget, and things will go badly.&lt;br /&gt;&lt;br /&gt;You'll notice that the code I've posted so far doesn't look anything like this.  The logic is provided up front by the structure of the data, so it isn't necessary to complicate the delegate methods.  All they have to do is select data, no other decisions are necessary.&lt;br /&gt;&lt;br /&gt;But let's assume for a moment that there is indeed a reason to further customize in the delegate methods.  Let's say that all of the text in the colors section should be red.  No problem.  But you still don't want to hard-code like this.  What happens if tomorrow you decide that colors should be section 5?  Then you have to change all of these statements.&lt;br /&gt;&lt;br /&gt;There isn't really a good option for the array-of-arrays case, so this may be a good vote in favor of the dictionary approach.  Rather than hard-coding the section &lt;em&gt;number&lt;/em&gt;, I can be flexible according to the section &lt;em&gt;key&lt;/em&gt;.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;if ([key isEqualToString:@"Colors"])&lt;br /&gt;{&lt;br /&gt;   // Make them red&lt;br /&gt;}&lt;br /&gt;else&lt;br /&gt;{&lt;br /&gt;   // Make them black&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now you've got a condition that will trigger correctly regardless of the order of the data.  And if today you are testing without colors, no problem.  Drop the key, and this condition will never trigger.&lt;br /&gt;&lt;br /&gt;Once you get comfortable with the basic concepts here, you may want to take a look at a post I made a long time ago: &lt;a href="http://clingingtoideas.blogspot.com/2009/09/taming-table-views.html"&gt;Taming Table Views&lt;/a&gt;.  There, I show the structure of a custom model class that I use all of the time with sectioned table views.  This would be used with the array-of-arrays approach, but instead -of-arrays, it would be -of-DisplaySections.  It has a field for the header, a field for the letter index, a field for behind-the-scenes stuff if needed, and it has an array property for the contents.  A couple months after I wrote that, I discovered that Apple has a similar class (actually a protocol) for working with CoreData stuff called NSFetchedResultsSectionInfo.&lt;br /&gt;&lt;br /&gt;One last comment regarding searching.  If all you do is take the code in these samples and apply them to your project from Part 2, then searching will not work.  Keep in mind the way we've changed the structure.  It used to be an array of strings.  It is now an array &lt;strong&gt;of&lt;/strong&gt; arrays of strings.  So you will have to adapt the search routine to this new structure.  It is along the same lines as what we've done above in the delegate methods, so you should be able to figure it out.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://homepage.mac.com/brianslick/blogpics/tableviews/part3/TableViewTutorial_Part3.zip"&gt;TableViewTutorial_Part3.zip&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38820386-268067928830993938?l=clingingtoideas.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/cRF84mWrD1_SLRkRt5W5cv-G63g/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/cRF84mWrD1_SLRkRt5W5cv-G63g/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/cRF84mWrD1_SLRkRt5W5cv-G63g/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/cRF84mWrD1_SLRkRt5W5cv-G63g/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ClingingToIdeas/~4/Kn1JST_hpJk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://clingingtoideas.blogspot.com/feeds/268067928830993938/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=38820386&amp;postID=268067928830993938" title="8 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/268067928830993938?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/268067928830993938?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ClingingToIdeas/~3/Kn1JST_hpJk/uitableview-how-to-part-3-multiple.html" title="UITableView How-To: Part 3 - Multiple Sections" /><author><name>The Slick One</name><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="31" height="32" src="http://1.bp.blogspot.com/_nJq3n1l5xlM/SmYOXU5LAyI/AAAAAAAAABU/-SvDv814nmI/S220/DSC_1796.jpg" /></author><thr:total>8</thr:total><feedburner:origLink>http://clingingtoideas.blogspot.com/2011/02/uitableview-how-to-part-3-multiple.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUECQXwzcCp7ImA9WhZTEkQ.&quot;"><id>tag:blogger.com,1999:blog-38820386.post-1883218805813201395</id><published>2010-02-24T17:37:00.002-05:00</published><updated>2011-03-16T13:47:40.288-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-03-16T13:47:40.288-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="UIViewController" /><category scheme="http://www.blogger.com/atom/ns#" term="UITableViewDelegate" /><category scheme="http://www.blogger.com/atom/ns#" term="UITableView" /><category scheme="http://www.blogger.com/atom/ns#" term="cocoa touch" /><category scheme="http://www.blogger.com/atom/ns#" term="iphone sdk" /><category scheme="http://www.blogger.com/atom/ns#" term="Xcode" /><category scheme="http://www.blogger.com/atom/ns#" term="cocoa" /><category scheme="http://www.blogger.com/atom/ns#" term="UISearchDisplayController" /><category scheme="http://www.blogger.com/atom/ns#" term="UITableViewDataSource" /><category scheme="http://www.blogger.com/atom/ns#" term="iphone" /><title>UITableView How-To: Part 2 - Search</title><content type="html">&lt;a href="http://clingingtoideas.blogspot.com/2010/01/uitableview-how-to-part-1-view.html"&gt;Part 1&lt;/a&gt; | &lt;a href="http://clingingtoideas.blogspot.com/2011/02/uitableview-how-to-part-3-multiple.html"&gt;Part 3&lt;/a&gt; | &lt;a href="http://clingingtoideas.blogspot.com/2011/03/uitableview-how-to-part-4-xib-based.html"&gt;Part 4&lt;/a&gt; | &lt;a href="http://clingingtoideas.blogspot.com/2011/03/uitableview-how-to-part-5-more-about.html"&gt;Part 5&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;If you have a lengthy list of data, you should provide the ability for your users to search through that data.  Starting with OS 3.0, Apple made integrating search into table views easy enough that there really isn't a good reason not to include it.&lt;br /&gt;&lt;br /&gt;This post is based heavily, if not entirely, on Apple's sample project called TableSearch.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;UISearchDisplayController&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;The search UI that Apple provides is basically a table view with a search bar.  They provide some pretty animations, such as sliding the search bar up to cover up the navigation bar (handy for maximizing available space, especially in landscape).  The search table view is overlaid onto your existing view, so both your original table and the search table need data.  The principles of delegate and data source as discussed in the last post still apply.&lt;br /&gt;&lt;br /&gt;In addition to creating the controller itself, Apple also gave UIViewController a searchDisplayController property, which makes it easier to access.  This obviously isn't populated by default, but be aware that it exists.&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 500px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part2/outlet_empty.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Data&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;Say you have a list of 10 items.  The user performs a search, and only 2 items meet the criteria.  Now think back to our datasource and delegate methods.  We are answering a lot of questions about the table: how many sections, how many rows, what cell should be displayed (and what should the cell contain)?  We still have to answer those questions, only now there are two tables involved.  Even if there was only a single table, something would still have to be done with the data.  There are two basic approaches you can take: 1) Remove items that don't match, or 2) Create a whole new list containing only items that do match.  If you go with #1, you need some way of restoring the full list.  So really, in either case, you are talking about 2 sources of data: the full list, and the search results.  Since a separate table is involved for the search results, #2 probably makes more sense, and is what Apple shows in their demo.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Step Up To The Bar&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;We'll start with the easiest part of this project.  Open up the view controller XIB in Interface Builder, and make sure you can see the table view.  Find the Search Bar And Search Display Controller item in the library.&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 500px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part2/search_and_controller.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;Do note that this is a separate choice from the standalone search bar.  You can certainly roll your own solution using just the bar, but the controller is what makes the work relatively easy.  So be sure to grab the one with the little orange circle.&lt;br /&gt;&lt;br /&gt;In order to get the search bar to scroll with the table, we are going to add it as the table's header.  Grab the library item, and drag to the upper portion of the table view.  You should see a blue highlight, which is your confirmation that you will get the header.&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 500px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part2/drag_to_header.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;A number of things happen automatically when you do this.  Let's take a quick look at the object list:&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 500px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part2/after_drag.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;Even though we didn't directly place it there, the Search Display Controller has been added.  If we inspect the connections...&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 500px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part2/outlet_full.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;...we see a whole host of additions.  The searchDisplayController property on the UIViewController has been populated.  The necessary delegate and datasource connections have been made for the search controller, and the delegate has also been specified for the search bar itself.  Not bad for one drag-n-drop operation.  We are now done with IB, the rest is handled in code.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Changes to .h&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;Here is the end result, then I'll explain what's going on.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;//  SampleViewController.h&lt;br /&gt;&lt;br /&gt;#import &amp;lt;UIKit/UIKit.h&amp;gt;&lt;br /&gt;&lt;br /&gt;@interface SampleViewController : UIViewController &amp;lt;UITableViewDataSource, UITableViewDelegate, &lt;strong&gt;UISearchDisplayDelegate, UISearchBarDelegate&lt;/strong&gt;&amp;gt;&lt;br /&gt;{&lt;br /&gt;   UITableView *mainTableView;&lt;br /&gt;	&lt;br /&gt;   NSMutableArray *contentsList;&lt;br /&gt;   &lt;strong&gt;NSMutableArray *searchResults;&lt;br /&gt;   NSString *savedSearchTerm;&lt;/strong&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@property (nonatomic, retain) IBOutlet UITableView *mainTableView;&lt;br /&gt;@property (nonatomic, retain) NSMutableArray *contentsList;&lt;br /&gt;&lt;strong&gt;@property (nonatomic, retain) NSMutableArray *searchResults;&lt;br /&gt;@property (nonatomic, copy) NSString *savedSearchTerm;&lt;br /&gt;&lt;br /&gt;- (void)handleSearchForTerm:(NSString *)searchTerm;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;@end&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;First, we conform to the UISearchDisplayDelegate and UISearchBarDelegate protocols.  Same idea as what we did for the table view previously.&lt;br /&gt;&lt;br /&gt;Next we declare a couple of new instance variables and properties.  The searchResults array will hold items that match the search criteria.  The savedSearchTerm is something that Apple shows in their sample, and they use it to restore the search when returning to this screen.&lt;br /&gt;&lt;br /&gt;Finally, we declare a method that will do the grunt work of searching through the data.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Changes to .m&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;We'll start at the top and work our way down.  First, synthesize properties and handle memory management duties.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;@implementation SampleViewController&lt;br /&gt;&lt;br /&gt;@synthesize mainTableView;&lt;br /&gt;@synthesize contentsList;&lt;br /&gt;&lt;strong&gt;@synthesize searchResults;&lt;br /&gt;@synthesize savedSearchTerm;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;- (void)dealloc&lt;br /&gt;{&lt;br /&gt;   [mainTableView release], mainTableView = nil;&lt;br /&gt;   [contentsList release], contentsList = nil;&lt;br /&gt;   &lt;strong&gt;[searchResults release], searchResults = nil;&lt;br /&gt;   [savedSearchTerm release], savedSearchTerm = nil;&lt;/strong&gt;&lt;br /&gt;	&lt;br /&gt;   [super dealloc];&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;- (void)viewDidUnload&lt;br /&gt;{&lt;br /&gt;   [super viewDidUnload];&lt;br /&gt;	&lt;br /&gt;   // Save the state of the search UI so that it can be restored if the view is re-created.&lt;br /&gt;   [self setSavedSearchTerm:[[[self searchDisplayController] searchBar] text]];&lt;br /&gt;	&lt;br /&gt;   [self setSearchResults:nil];&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That last bit is from Apple's sample.  The counterpart is here:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;- (void)viewDidLoad&lt;br /&gt;{&lt;br /&gt;   [super viewDidLoad];&lt;br /&gt;	&lt;br /&gt;   ...&lt;br /&gt;   &lt;br /&gt;   // Restore search term&lt;br /&gt;   if ([self savedSearchTerm])&lt;br /&gt;   {&lt;br /&gt;      [[[self searchDisplayController] searchBar] setText:[self savedSearchTerm]];&lt;br /&gt;   }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Saving/restoring the search criteria, nothing fancy.  Then we get to the search routine itself:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;- (void)handleSearchForTerm:(NSString *)searchTerm&lt;br /&gt;{&lt;br /&gt;   [self setSavedSearchTerm:searchTerm];&lt;br /&gt;	&lt;br /&gt;   if ([self searchResults] == nil)&lt;br /&gt;   {&lt;br /&gt;      NSMutableArray *array = [[NSMutableArray alloc] init];&lt;br /&gt;      [self setSearchResults:array];&lt;br /&gt;      [array release], array = nil;&lt;br /&gt;   }&lt;br /&gt;	&lt;br /&gt;   [[self searchResults] removeAllObjects];&lt;br /&gt;	&lt;br /&gt;   if ([[self savedSearchTerm] length] != 0)&lt;br /&gt;   {&lt;br /&gt;      for (NSString *currentString in [self contentsList])&lt;br /&gt;      {&lt;br /&gt;         if ([currentString rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location != NSNotFound)&lt;br /&gt;         {&lt;br /&gt;            [[self searchResults] addObject:currentString];&lt;br /&gt;         }&lt;br /&gt;      }&lt;br /&gt;   }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;First we store the search term.  Then we lazily create the searchResults array if needed.  Next we clear out any previous search results.  Then we loop through our main data, find any matching items, and add them to the searchResults array.&lt;br /&gt;&lt;br /&gt;This method could vary greatly depending on what kind of data you are working with, and how it is arranged.  This particular implementation is based on an example in the Mark/LaMarche book.&lt;br /&gt;&lt;br /&gt;Now we get to the fun part: the table datasource and delegate methods.  We've discussed a bit already about the need to have 2 separate data lists, and pointed out that we will be dealing with 2 separate tables.  However, we only have one set of delegate methods here in the controller.  We could use a completely separate object, as mentioned in the previous post, but that's not really necessary.  So, we need some way of determining which set of data to use.  You might get away with using some kind of flag, say BOOL isCurrentlySearching or something along those lines.  I have fought enough battles with the search display controller in attempting to do other tasks to know that this approach won't work.  Thus, we'll go with the way Apple's sample shows, and that is to make decisions based on which table is asking for information.  In hindsight, this is a really obvious approach, but I'm not always on the ball as quickly as I should be.&lt;br /&gt;&lt;br /&gt;First, the number of rows:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;- (NSInteger)tableView:(UITableView *)tableView&lt;br /&gt; numberOfRowsInSection:(NSInteger)section&lt;br /&gt;{&lt;br /&gt;   NSInteger rows;&lt;br /&gt;	&lt;br /&gt;   if (tableView == [[self searchDisplayController] searchResultsTableView])&lt;br /&gt;      rows = [[self searchResults] count];&lt;br /&gt;   else&lt;br /&gt;      rows = [[self contentsList] count];&lt;br /&gt;	&lt;br /&gt;   return rows;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The important thing to realize here is that all of these table view delegate methods include the table view itself as a parameter.  This allows you the means to identify which table is making the request.  If you had a reason to design a view with multiple tables, this is exactly what you would do.  If the table asking for info is the search table, we provide an answer based on the search list, otherwise we use our main list.&lt;br /&gt;&lt;br /&gt;We do the exact same thing when providing a cell:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;- (UITableViewCell *)tableView:(UITableView *)tableView&lt;br /&gt;		 cellForRowAtIndexPath:(NSIndexPath *)indexPath&lt;br /&gt;{&lt;br /&gt;   NSInteger row = [indexPath row];&lt;br /&gt;   NSString *contentForThisRow = nil;&lt;br /&gt;	&lt;br /&gt;   if (tableView == [[self searchDisplayController] searchResultsTableView])&lt;br /&gt;      contentForThisRow = [[self searchResults] objectAtIndex:row];&lt;br /&gt;   else&lt;br /&gt;      contentForThisRow = [[self contentsList] objectAtIndex:row];&lt;br /&gt;	&lt;br /&gt;   static NSString *CellIdentifier = @"CellIdentifier";&lt;br /&gt;	&lt;br /&gt;   UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];&lt;br /&gt;   if (cell == nil)&lt;br /&gt;   {&lt;br /&gt;      cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];&lt;br /&gt;   }&lt;br /&gt;	&lt;br /&gt;   [[cell textLabel] setText:contentForThisRow];&lt;br /&gt;	&lt;br /&gt;   return cell;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Constructing the cell itself doesn't need to change just because we're using a search table.  The key thing is to make sure we're grabbing the right piece of information to populate that cell.  "Green" might be at row 4 in our main list, but it could be at row 2 in the search results.  Once again, we make a decision based on which table is asking, and grab data from the appropriate list.&lt;br /&gt;&lt;br /&gt;I'm not really going into cell selection yet, but you would need to do something similar in tableView:didSelectRowAtIndexPath:.  The index path can, and most likely will, be different depending on which table is being shown.&lt;br /&gt;&lt;br /&gt;At last, we reach the part that makes this all work.  We've been dealing with table delegate methods up to this point, but the search controller has delegate methods of its own.  So we'll utilize a couple of those to make the actual search happen.  First, to begin the search:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;- (BOOL)searchDisplayController:(UISearchDisplayController *)controller &lt;br /&gt;shouldReloadTableForSearchString:(NSString *)searchString&lt;br /&gt;{&lt;br /&gt;   [self handleSearchForTerm:searchString];&lt;br /&gt;    &lt;br /&gt;   return YES;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And that's it.  The documentation notes this is an optional method, and NOT implementing it will cause the search table to reload as the search term changes.  So the only reason we're doing this is to define what logic should be performed in response to the search string.  And this next part is also optional, but if you want to do any cleanup after the search, this is where you could do it:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller&lt;br /&gt;{&lt;br /&gt;   [self setSavedSearchTerm:nil];&lt;br /&gt;	&lt;br /&gt;   [[self mainTableView] reloadData];&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We no longer need the search term to be saved, so get rid of it.  And maybe it is appropriate to refresh the main table view.&lt;br /&gt;&lt;br /&gt;That's pretty much all there is to it at a basic level.  Obviously this is a simple app so far, but the search display controller handles a lot duties on its own.  Here is the sample project for everything so far:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://homepage.mac.com/brianslick/blogpics/tableviews/part2/TableViewTutorial_Part2.zip"&gt;TableViewTutorial_Part2.zip&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38820386-1883218805813201395?l=clingingtoideas.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/EzGwTsxkNPak_V7h4ccC4YsnKPo/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/EzGwTsxkNPak_V7h4ccC4YsnKPo/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/EzGwTsxkNPak_V7h4ccC4YsnKPo/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/EzGwTsxkNPak_V7h4ccC4YsnKPo/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ClingingToIdeas/~4/36khcrX45lk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://clingingtoideas.blogspot.com/feeds/1883218805813201395/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=38820386&amp;postID=1883218805813201395" title="40 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/1883218805813201395?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/1883218805813201395?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ClingingToIdeas/~3/36khcrX45lk/uitableview-how-to-part-2-search.html" title="UITableView How-To: Part 2 - Search" /><author><name>The Slick One</name><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="31" height="32" src="http://1.bp.blogspot.com/_nJq3n1l5xlM/SmYOXU5LAyI/AAAAAAAAABU/-SvDv814nmI/S220/DSC_1796.jpg" /></author><thr:total>40</thr:total><feedburner:origLink>http://clingingtoideas.blogspot.com/2010/02/uitableview-how-to-part-2-search.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUEHRXY5fip7ImA9WhZTEkQ.&quot;"><id>tag:blogger.com,1999:blog-38820386.post-6784882399557885710</id><published>2010-01-27T00:03:00.002-05:00</published><updated>2011-03-16T13:47:14.826-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-03-16T13:47:14.826-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Interface Builder" /><category scheme="http://www.blogger.com/atom/ns#" term="UIViewController" /><category scheme="http://www.blogger.com/atom/ns#" term="UITableViewDelegate" /><category scheme="http://www.blogger.com/atom/ns#" term="UITableView" /><category scheme="http://www.blogger.com/atom/ns#" term="iphone sdk" /><category scheme="http://www.blogger.com/atom/ns#" term="Xcode" /><category scheme="http://www.blogger.com/atom/ns#" term="cocoa" /><category scheme="http://www.blogger.com/atom/ns#" term="UITableViewDataSource" /><category scheme="http://www.blogger.com/atom/ns#" term="iphone" /><title>UITableView How-To: Part 1 - View Controller Setup</title><content type="html">&lt;a href="http://clingingtoideas.blogspot.com/2010/02/uitableview-how-to-part-2-search.html"&gt;Part 2&lt;/a&gt; | &lt;a href="http://clingingtoideas.blogspot.com/2011/02/uitableview-how-to-part-3-multiple.html"&gt;Part 3&lt;/a&gt; | &lt;a href="http://clingingtoideas.blogspot.com/2011/03/uitableview-how-to-part-4-xib-based.html"&gt;Part 4&lt;/a&gt; | &lt;a href="http://clingingtoideas.blogspot.com/2011/03/uitableview-how-to-part-5-more-about.html"&gt;Part 5&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;6 months ago, I was preparing for a semi-major restructuring of SlickShopper.  My 1.0 had hard-coded sizes everywhere, but I wanted to support screen rotation, so something had to change.  I was in the process of rebuilding things using Interface Builder when I made some key discoveries about UINavigationControllers (they have a toolbar property) and UITableViewControllers (they inherently support resizing due to rotation).  I bailed on the IB stuff, and started over using table view controllers exclusively.  Version 1.5 onward contains table view controllers exclusively.  I patted myself on the back for so deftly having avoided IB.&lt;br /&gt;&lt;br /&gt;Today, I'm long past my fear of IB, and I've mostly given up on pure table view controllers.  I use IB as much as possible, and prefer to set everything up as a plain view controller.  I enjoy the flexibility this provides.  (Tip:  Don't name your view controllers as SomethingTableViewController, because the 'Table' part becomes incorrect if you change your mind about implementation later)  I encounter a number of people struggling with basic aspects of table views, so I'm going to pool together the techniques I've learned from books and Apple's sample programs.&lt;br /&gt;&lt;br /&gt;This will be the first installment of several posts devoted to the creation of plain view controllers that feature a table view.  Today I'm going to focus on basic setup of the view controller.  Future installments will look at using IB-based table cells, how to implement a search bar, and any other useful things I think of along the way.  Please feel free to post requests in the comments area.  My intention is to be as step-by-step as necessary, but I will also attempt to include a sample project at the end of each post.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Initial Setup&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;I'm not really going to go into the various places this view controller could be used.  Theoretically, it should be perfectly usable in a view-based app, a navigation-based app, tab-based app, etc.  So, create a new project using whatever template you like.  I'm going to use the navigation-based template, as I intend to show how to pass data to a sub-controller at some point in this series.  But feel free to use whatever template you want, as the choice doesn't really impact what I'm going to do here.&lt;br /&gt;&lt;br /&gt;After the project has been set up, create a new file.&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 500px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part1/add_new_file_view_controller.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;Choose a UIViewController subclass, and hit the toggle to indicate that you want to use a XIB file.&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 500px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part1/add_view_controller_subclass.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;I'm calling mine SampleViewController, you can call it whatever you want.  The XIB checkbox is relatively new, so if you don't have it, simply create your own XIB file, and give it the same name.  And then start downloading the newest version of Xcode.&lt;br /&gt;&lt;br /&gt;We need to do work in all 3 files that were just created, but in order to avoid bouncing around I'm going to work in this order:  .h -&gt; .xib -&gt; .m.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Prepare The Header&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;Your .h file should look like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;#import &amp;lt;UIKit/UIKit.h&amp;gt;&lt;br /&gt;&lt;br /&gt;@interface SampleViewController : UIViewController {&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@end&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Since this is a plain view controller, we need to add some information to it in order to work with a table view.  The parts that we are about to add are included for free with table view controllers, hence the appeal.  But it is easy enough to add manually, so here we go.&lt;br /&gt;&lt;br /&gt;First, we need to adopt a couple of protocols.  Table views are designed to be generic, and rely on other objects to provide customization.  For our immediate use, customization mostly refers to providing content, but it can also apply to appearance.  The necessary protocols are UITableViewDataSource, and UITableViewDelegate.  Data source, as the name implies, provides the content.  The delegate pattern is used throughout Cocoa, and indicates that one object will be doing work on behalf of another object.  There are two protocols because they serve different needs, and if desired they could be distinct objects.  We are already in a class that is perfectly capable of doing the work, but if you had a reason to do so, you could certainly make two more external classes to accomplish the same thing.  For simplicity, generally speaking you'll just use your view controller.  To indicate that your view controller can serve in these roles, add this to the .h file:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;#import &lt; UIKit/UIKit.h &gt;&lt;br /&gt;&lt;br /&gt;@interface SampleViewController : UIViewController &lt;strong&gt;&amp;lt;UITableViewDataSource, UITableViewDelegate&amp;gt;&lt;/strong&gt;&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@end&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;There are no required methods in UITableViewDelegate, but there are in UITableViewDataSource.  So, if you build your project before we finish up, you will get some warning messages related to the absence of those required methods.  You can ignore the warnings for now, but by the time we finish up, make sure the warnings are gone.&lt;br /&gt;&lt;br /&gt;We are going to graphically place a table view into our main view in Interface Builder, just as if we were placing a button or a label.  We will have a reason to talk to that table view object. The way to establish that line of communication is to declare IBOutlets here in the .h file.  We need to declare a table view instance variable, and we'll communicate with it using a property, like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;#import &lt; UIKit/UIKit.h &gt;&lt;br /&gt;&lt;br /&gt;@interface SampleViewController : UIViewController &amp;lt;UITableViewDataSource, UITableViewDelegate&amp;gt;&lt;br /&gt;{&lt;br /&gt;   &lt;strong&gt;UITableView *mainTableView;&lt;/strong&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;@property (nonatomic, retain) IBOutlet UITableView *mainTableView;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;@end&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You may have seen the "IBOutlet" tag on the first line, and you may have seen it on both lines.  Apple's examples are shifting towards showing it only on the second line, so that's what I will do.  Showing it in both places is not necessary.&lt;br /&gt;&lt;br /&gt;I recommend that you not call it "tableView".  It's probably not a huge deal necessarily, but the delegate methods will use "tableView" as a local variable name, so best to avoid the clash.  Also, table view controllers automatically have a tableView property, so using a different name helps to reinforce that we aren't using a table view controller.&lt;br /&gt;&lt;br /&gt;We're basically done now, but before we move on, let's take care of the structure that will hold our contents.  I'm just going to use an array for now, as arrays work quite well with the way table views expect to receive information.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;#import &lt;UIKit/UIKit.h&gt;&lt;br /&gt;&lt;br /&gt;@interface SampleViewController : UIViewController &amp;lt;UITableViewDataSource, UITableViewDelegate&amp;gt;&lt;br /&gt;{&lt;br /&gt;   UITableView *mainTableView;&lt;br /&gt;	&lt;br /&gt;   &lt;strong&gt;NSMutableArray *contentsList;&lt;/strong&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@property (nonatomic, retain) IBOutlet UITableView *mainTableView;&lt;br /&gt;&lt;strong&gt;@property (nonatomic, retain) NSMutableArray *contentsList;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;@end&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Lay Out The Interface&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;Open up the XIB file.  We want the view to be capable of supporting landscape, so we need to change the resizing masks.  Select the view, then hit Cmd-3 to bring up the size inspector.  Toggle the masks as shown here so that the view is fully flexible, and go ahead and make the height 480 for good measure.&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 500px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part1/resize_mask_view.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;If you are unable to make these changes, hit Cmd-1, turn off any simulated UI elements like the status bar, then come back and try again.  (Disclosure: I only figured that out just now while typing this up... I thought it was a bug.  I've been deleting and re-creating the view for quite some time)&lt;br /&gt;&lt;br /&gt;Drag a table view from the palette onto the view.  It should automatically expand to fill the entire view.&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 500px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part1/add_table_view.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;The resizing masks should already be set, but go ahead and verify them for the table view just in case.&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 500px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part1/resize_mask_table_view.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;In order to talk to the table view, we need to use the IBOutlet that we declared in the .h.  To do that, Right-click (Ctrl-click) on File's Owner, and drag to the table view.  (I've resized the view for sake of screen capture here)&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 500px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part1/table_iboutlet_01.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;You should see the table view highlight, and you should see "Table View" appear in a little box at the lower right, thus confirming your selection.  When you let go, a window will appear:&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 500px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part1/table_iboutlet_02.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;Select the name of the IBOutlet that was created in the .h file.&lt;br /&gt;&lt;br /&gt;We're not quite done yet.  Remember the data source and delegate from the .h file?  That declaration simply published the fact that our view controller is willing to serve that role.  But that alone does not mean that the table view knows who to talk to.  There could be any number of conforming classes eligible, so we need to identify specifically which class(es) this table view will use.&lt;br /&gt;&lt;br /&gt;Select the table view, and hit Cmd-2.  At the top of the inspector are outlets for the delegate and dataSource.  Select the circle next to each one, and drag to File's Owner (regular left-click drag).&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 500px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part1/datasource.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 500px;" src="http://homepage.mac.com/brianslick/blogpics/tableviews/part1/delegate.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;So, the view controller is able to talk to the table view, the table view will request information from the view controller, and the whole thing is resizable.  Thus concludes our trip to IB.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Implement&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;Now for the hard part.  Most of what we will be doing in the .m file would be exactly the same or very similar if we were using a table view controller instead.  Let's start at the top and work our way down.&lt;br /&gt;&lt;br /&gt;First, synthesize the properties:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;#import "SampleViewController.h"&lt;br /&gt;&lt;br /&gt;@implementation SampleViewController&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;@synthesize mainTableView;&lt;br /&gt;@synthesize contentsList;&lt;/strong&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Most examples show dealloc at the bottom, but I prefer to have it up top so I can quickly glance at the properties.  Follow memory management rules and release the properties.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;- (void)dealloc&lt;br /&gt;{&lt;br /&gt;   NSLog(@"&gt;&gt;&gt; Entering %s &lt;&lt;&lt;", __PRETTY_FUNCTION__);&lt;br /&gt;	&lt;br /&gt;   &lt;strong&gt;[mainTableView release], mainTableView = nil;&lt;br /&gt;   [contentsList release], contentsList = nil;&lt;/strong&gt;&lt;br /&gt;	&lt;br /&gt;   [super dealloc];&lt;br /&gt;	&lt;br /&gt;   NSLog(@"&amp;lt;&amp;lt;&amp;lt; Leaving %s &amp;gt;&amp;gt;&amp;gt;", __PRETTY_FUNCTION__);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Somewhere we need to build the data that will appear in the table view.  For a simple case like this, viewDidLoad will work just fine.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;- (void)viewDidLoad&lt;br /&gt;{&lt;br /&gt;   NSLog(@"&gt;&gt;&gt; Entering %s &lt;&lt;&lt;", __PRETTY_FUNCTION__);&lt;br /&gt;	&lt;br /&gt;   [super viewDidLoad];&lt;br /&gt;	&lt;br /&gt;   &lt;strong&gt;NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:@"Red", @"Blue", @"Green", @"Black", @"Purple", nil];&lt;br /&gt;   [self setContentsList:array];&lt;br /&gt;   [array release], array = nil;&lt;/strong&gt;&lt;br /&gt;	&lt;br /&gt;   NSLog(@"&amp;lt;&amp;lt;&amp;lt; Leaving %s &amp;gt;&amp;gt;&amp;gt;", __PRETTY_FUNCTION__);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Build an array, stick it into the property, then release it.&lt;br /&gt;&lt;br /&gt;For this example, we aren't too worried about the displayed contents being incorrect.  But in a real app, if activity in another view controller could cause the contents here to change, we want to make sure that the user sees the updated information.  To make the table refresh every time the view is displayed, we'll use viewWillAppear.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;- (void)viewWillAppear:(BOOL)animated&lt;br /&gt;{&lt;br /&gt;   NSLog(@"&gt;&gt;&gt; Entering %s &lt;&lt;&lt;", __PRETTY_FUNCTION__);&lt;br /&gt;	&lt;br /&gt;   [super viewWillAppear:animated];&lt;br /&gt;	&lt;br /&gt;   &lt;strong&gt;[[self mainTableView] reloadData];&lt;/strong&gt;&lt;br /&gt;	&lt;br /&gt;   NSLog(@"&amp;lt;&amp;lt;&amp;lt; Leaving %s &amp;gt;&amp;gt;&amp;gt;", __PRETTY_FUNCTION__);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I mentioned before that UITableViewDataSource has some required methods, so let's get those out of the way.  First, the table is going to ask the data source how many rows are involved.  The answer should be based on our array, and we use this method to respond to the table's question:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;- (NSInteger)tableView:(UITableView *)tableView&lt;br /&gt; numberOfRowsInSection:(NSInteger)section&lt;br /&gt;{&lt;br /&gt;   NSLog(@"&gt;&gt;&gt; Entering %s &lt;&lt;&lt;", __PRETTY_FUNCTION__);&lt;br /&gt;	&lt;br /&gt;   NSInteger rows = [[self contentsList] count];&lt;br /&gt;	&lt;br /&gt;   NSLog(@"rows is: %d", rows);&lt;br /&gt;   NSLog(@"&amp;lt;&amp;lt;&amp;lt; Leaving %s &amp;gt;&amp;gt;&amp;gt;", __PRETTY_FUNCTION__);&lt;br /&gt;   return rows;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Note the section variable that we aren't using here.  So far, we only have one section, so there isn't a need to worry about it.  In a future post, I'll show how to do a multi-section table, at which point this method gets a bit more involved.&lt;br /&gt;&lt;br /&gt;Next, the table is going to ask for a view to display in each row.  Apple has provided a UIView subclass called UITableViewCell that is pre-configured for many common table needs.  You create a cell, give it some content, and then give that cell to the table.  Repeat as needed for each row.  A lot of explanation is going to be needed here, so let's jump to the end result, and I'll discuss afterwards...&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;- (UITableViewCell *)tableView:(UITableView *)tableView&lt;br /&gt;		 cellForRowAtIndexPath:(NSIndexPath *)indexPath&lt;br /&gt;{&lt;br /&gt;   NSLog(@"&gt;&gt;&gt; Entering %s &lt;&lt;&lt;", __PRETTY_FUNCTION__);&lt;br /&gt;	&lt;br /&gt;   NSString *contentForThisRow = [[self contentsList] objectAtIndex:[indexPath row]];&lt;br /&gt;	&lt;br /&gt;   static NSString *CellIdentifier = @"CellIdentifier";&lt;br /&gt;	&lt;br /&gt;   UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];&lt;br /&gt;   if (cell == nil)&lt;br /&gt;   {&lt;br /&gt;      cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];&lt;br /&gt;      // Do anything that should be the same on EACH cell here.  Fonts, colors, etc.&lt;br /&gt;   }&lt;br /&gt;	&lt;br /&gt;   // Do anything that COULD be different on each cell here.  Text, images, etc.&lt;br /&gt;   [[cell textLabel] setText:contentForThisRow];&lt;br /&gt;	&lt;br /&gt;   NSLog(@"&amp;lt;&amp;lt;&amp;lt; Leaving %s &amp;gt;&amp;gt;&amp;gt;", __PRETTY_FUNCTION__);&lt;br /&gt;   return cell;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In summary:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Grab the object that has (or in this case, is) the information we want to display for this row&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Ask the table view if any cells are available for recycling.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;If not, create a new cell&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Specify the content for the cell&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;I'll talk more about cell customization at a later date, but for now please note the comments I added regarding where you should customize different elements of the cell.&lt;br /&gt;&lt;br /&gt;First, a little bit about the NSIndexPath.  If you were trying to describe the location of a point on a grid, you would most likely use (X,Y) coordinates.  The index path provides a way of describing a location within the table, but instead of (X,Y) coordinates, it is using (section, row) coordinates.  These are numbered in the exact same way an array is, so the first item is 0, the second item is 1, and so on.  We only have one section, so we will only be dealing with section 0 for now.  Within our only section, we are providing five pieces of information, so we'll be talking about row 0, row 1,....up to row 4.&lt;br /&gt;&lt;br /&gt;So this method begins with the table asking the question "Hey, I'm now at the first section and the second row... what should I show here?"  We need to figure out which row is being requested, and we do that by asking the indexPath for its row value.  [indexPath row]  (later we'll do the same thing to get a section value).  Assuming we are building the table up from scratch, we should be dealing with the first row, so row 0.  Now I know which piece of information I want from the array: the first item, so the item at index 0.  For convenience I assign that to a local variable.&lt;br /&gt;&lt;br /&gt;Next I declare a string variable.  I don't actually know what "static" technically means, other than the obviously implied "this does not change".  I suppose you could #define a constant instead.  The purpose of this string will be to allow the table view to identify cells in a queue that it will create.&lt;br /&gt;&lt;br /&gt;The table's cell queue exists because flinging your way through a list of data needs to happen as fast as possible, but building a cell from scratch can be expensive from a performance standpoint.  If we were providing 1000 strings for this table, we don't want to actually create 1000 cells.  We only need to create enough to cover the visible screen, plus a couple extra for buffer, and then we can reuse those cells over and over again.  As a cell slides off the top of the screen, it goes into the queue, only to reappear at the bottom of the screen with new content.  We don't want to build new cells if it can be avoided.&lt;br /&gt;&lt;br /&gt;Thus, the next thing we do is ask the table if any cells are available for reuse, here:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We use the string identifier, because there could be multiple kinds of cells being stored, so we want to make sure we get the right kind.  If a cell is available, it will be provided to the cell variable.  However, if there aren't any cells available (as would be the case when starting from scratch), the return from this call is nil.  So, we find out if we actually have a cell right now:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;if (cell == nil)&lt;br /&gt;{&lt;br /&gt;   cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];&lt;br /&gt;   // Do anything that should be the same on EACH cell here.  Fonts, colors, etc.&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;If there is a cell, this part doesn't happen.  If there isn't a cell, this part will build a new one.&lt;br /&gt;&lt;br /&gt;Lastly, we provide our content:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;// Do anything that COULD be different on each cell here.  Text, images, etc.&lt;br /&gt;[[cell textLabel] setText:contentForThisRow];&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Standard cells have a label property, and I'm setting the text using our content string.  I'm going to again emphasize the comments I've put in there.  When you get to this point of the method, you have 2 possibilities:  1) The cell is brand new, or 2) The cell has been recycled.  Recycled cells will most likely still have their old content, so you cannot make assumptions about the state of the cell you are working with.  It could be pristine, it could be dirty.  So this area of the method MUST make sure the end result is correct.  If you alternate font colors between red and green, then you should have an if/else statement here that makes the font red OR makes the font green.  You cannot assuming the incoming color is correct.&lt;br /&gt;&lt;br /&gt;That covers the required methods for the data source.  There are many, many other optional methods - from dataSource and from delegate - for performing a variety of tasks, but for now I just want to draw attention to one of them.&lt;br /&gt;&lt;br /&gt;If you build-and-run your app, you should see the list of colors in your table.  If you tap a row, it will stay highlighted.  Let's turn that off.  This method is how the table says "Hey, I was touched here... what do you want me to do about it?"&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;- (void)tableView:(UITableView *)tableView&lt;br /&gt;didSelectRowAtIndexPath:(NSIndexPath *)indexPath&lt;br /&gt;{&lt;br /&gt;   NSLog(@"&gt;&gt;&gt; Entering %s &lt;&lt;&lt;", __PRETTY_FUNCTION__);&lt;br /&gt;	&lt;br /&gt;   &lt;strong&gt;[tableView deselectRowAtIndexPath:indexPath animated:YES];&lt;/strong&gt;&lt;br /&gt;	&lt;br /&gt;   NSLog(@"&amp;lt;&amp;lt;&amp;lt; Leaving %s &amp;gt;&amp;gt;&amp;gt;", __PRETTY_FUNCTION__);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We will do a lot more with this method later, but for now we'll simply deselect the row.&lt;br /&gt;&lt;br /&gt;And thus concludes this edition of table talk.  Tune in, uh... later... for the next installment.  In the meantime, here is the sample project for this stage of the exercise.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://homepage.mac.com/brianslick/blogpics/tableviews/part1/TableViewTutorial_Part1.zip"&gt;TableViewTutorial_Part1.zip&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38820386-6784882399557885710?l=clingingtoideas.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/UWtvb62R7pp542vbFalhtKs_ltc/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/UWtvb62R7pp542vbFalhtKs_ltc/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/UWtvb62R7pp542vbFalhtKs_ltc/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/UWtvb62R7pp542vbFalhtKs_ltc/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ClingingToIdeas/~4/btb5z6vRBPQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://clingingtoideas.blogspot.com/feeds/6784882399557885710/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=38820386&amp;postID=6784882399557885710" title="19 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/6784882399557885710?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/6784882399557885710?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ClingingToIdeas/~3/btb5z6vRBPQ/uitableview-how-to-part-1-view.html" title="UITableView How-To: Part 1 - View Controller Setup" /><author><name>The Slick One</name><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="31" height="32" src="http://1.bp.blogspot.com/_nJq3n1l5xlM/SmYOXU5LAyI/AAAAAAAAABU/-SvDv814nmI/S220/DSC_1796.jpg" /></author><thr:total>19</thr:total><feedburner:origLink>http://clingingtoideas.blogspot.com/2010/01/uitableview-how-to-part-1-view.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkMEQno9cCp7ImA9WxNbF00.&quot;"><id>tag:blogger.com,1999:blog-38820386.post-3178540632542060050</id><published>2009-11-20T02:00:00.000-05:00</published><updated>2009-11-20T02:00:03.468-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-11-20T02:00:03.468-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="personal" /><category scheme="http://www.blogger.com/atom/ns#" term="iphone" /><category scheme="http://www.blogger.com/atom/ns#" term="business" /><title>Goals and Achievements</title><content type="html">Today marks the 1-year anniversary of the loss of my job.  Well, technically, I lost it on Nov 21, 2008, but it was the Friday before Thanksgiving, so this is close enough.  A quick glance back through relevant blog posts:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://clingingtoideas.blogspot.com/2008/11/time-on-my-hands.html"&gt;A few days after&lt;/a&gt;, no clue what I was going to do.&lt;br /&gt;&lt;a href="http://clingingtoideas.blogspot.com/2009/01/looking-back.html"&gt;A month later&lt;/a&gt;, still toying with going back to engineering, just starting to mess with iPhone programming.&lt;br /&gt;&lt;a href="http://clingingtoideas.blogspot.com/2009/02/official-end-of-era.html"&gt;A couple months after that&lt;/a&gt;, bailed on engineering, unsure of where iPhone stuff will go.&lt;br /&gt;&lt;a href="http://clingingtoideas.blogspot.com/2009/06/it-has-begun.html"&gt;Three months later&lt;/a&gt;, iPhone app available for sale.&lt;br /&gt;&lt;br /&gt;Since I'm forging my way into a brand new world, I didn't really have any idea of what to expect.  But I did have some rough goals in mind, so let's take a quick look at how I did.&lt;br /&gt;&lt;br /&gt;In early March, I decided to join Apple's iPhone Developer program.  The standard program costs $99 + tax, and is required to test programs on an iPhone, and sell apps on the store.  My app, &lt;a href="http://www.briterideas.com/BriTer_Ideas/SlickShopper.html"&gt;SlickShopper&lt;/a&gt;, was coming along well enough I felt this to be a prudent step.  I had no idea what to expect in terms of sales, but figured I had mostly missed the gold rush that happened in the early days of iPhone apps.  So my first goal was simple: recover my developer fee.  With tax, that was $105.44.  My app went on sale May 31, and my sales data says I hit $106.57 on June 27, so four weeks.&lt;br /&gt;&lt;br /&gt;And I was &lt;strong&gt;thrilled&lt;/strong&gt;!&lt;br /&gt;&lt;br /&gt;Of course developers don't get this money right away.  Apple pays out in certain minimums, and for a previous time period.  So I would not actually get my first check from Apple until July 30.  And due to the way Apple pays out for various regions, that first check was for only $86.10.  This car is going to Vegas, baybee!&lt;br /&gt;&lt;br /&gt;The next couple of goals are a bit intermixed, so bear with me for a moment.  This will make sense in a few paragraphs.&lt;br /&gt;&lt;br /&gt;Towards the end of March, I made the decision to attend Apple's World Wide Developer Conference (WWDC).  This was something of a gamble at the time, as I still had no idea if iPhone development was going to be my future, and I was still quite a ways off from having an app ready to sell.  I decided to go for it.  In hindsight, worth every penny, and definitely put me on the path to where I am today.  But, it was a big cost nonetheless.  The conference itself isn't cheap, plus travel, hotel, and so on.  So the next goal was to recover that cost.&lt;br /&gt;&lt;br /&gt;Now it is important to state that my accomplishment of this goal is NOT due to sales of my app.  Far from it; in fact to date the app has only brought in maybe 1/3 of that cost.  I have supplemented that income in other ways.  However, after being negative for most of the year, I did finally reach break-even in the first week of September.&lt;br /&gt;&lt;br /&gt;Again, &lt;strong&gt;thrilled&lt;/strong&gt;!&lt;br /&gt;&lt;br /&gt;That supplemental income?  Strange/funny story.&lt;br /&gt;&lt;br /&gt;You would be hard-pressed to perform a Google search for anything iPhone development-related and not turn up a link on &lt;a href="http://www.iphonedevsdk.com/"&gt;iPhone Dev SDK&lt;/a&gt;.  I've found many answers there, and continue to do so.  Sometime in July, I came to the realization that I actually had enough knowledge to answer questions for other people.  I made an account, and soon was burning a significant amount of time responding to posts.  I enjoyed doing it; it reminded me of the time I used to answer I-DEAS questions on the ICCON mailing list.  And I loved being in a teaching mode.  I seem to do my best learning when I'm teaching other people.  So with a decent nudge from WWDC, time spent on iPDSDK really helped to refine and solidify my understanding of concepts I've been struggling with for a couple years.&lt;br /&gt;&lt;br /&gt;One day, a clearly frustrated guy was lamenting that he could not find good answers.  He had even offered to pay people $10/question, and didn't get good results.  I jokingly responded that I'd be happy to answer questions for $10, not really expecting anything.  I'd been answering lots and lots of questions for free, and probably would have in this case, too.  But hey, if he wants to offer, I'll accept.&lt;br /&gt;&lt;br /&gt;We emailed back and forth a bit, and he was able to look at my post history and determine that I had a decent chance of knowing what I was talking about.  He sent me his code, I spent a couple of hours going over it and making notes, recommending better techniques, pointing out where problems were and how to fix them.  I returned his code, expecting that to be the last I ever heard from him.  A couple days went by, I hadn't heard anything, so I assumed my initial assumption was correct.&lt;br /&gt;&lt;br /&gt;But wait!  An email.  He's having trouble sending me a PayPal payment.  Uh huh.  Sure.  But he is not in the US, so let's go see what options are in PayPal... huh.  Sure enough.  Toggle that, change that.  Ok buddy, try again.  Another day or two goes by.  Yeah, that's what I thought.&lt;br /&gt;&lt;br /&gt;But wait!  Another email.  This time with a screen shot of his attempt to send me PayPal.  And for $20!  Back to PayPal options.  I now accept money from anyone, anywhere, for any reason.  The next day, I get money.&lt;br /&gt;&lt;br /&gt;Let's back up a moment.  I developed SlickShopper for me.  I wanted it.  It was my first app, my practice app, my test of "can I actually do this?"  If other people are willing to buy it, great, but this is for me.  But, I was not ignorant of the fact that it was also my programming resume.  I don't know that I had any specific expectations, but in the back of my mind I thought it would be nice if this app would help me to land a new job.  I guess I was thinking more in terms of a regular 9-5 job, but whatever.&lt;br /&gt;&lt;br /&gt;So, someone else - excluding SlickShopper customers - paid me money for my efforts.  I received that first payment on July 29th, beating my first check from Apple by a single day.  I received the last severance check from my job in February, so my period of zero income ended at five months.  It certainly wasn't Real Money yet, but anything is better than zero.&lt;br /&gt;&lt;br /&gt;Things continued like this for several weeks.  He'd ask questions, I'd answer, he'd send me money.  It took me a while to grasp what I had stumbled into.  In the middle of this, other people started coming to me looking for similar arrangements.  In some cases, I'd already been helping someone in a public thread, and they'd decide they were willing to pay for individual attention.  In other cases, people would browse the forums, land on some of my posts, decide I sounded knowledgeable, and ask if I could help.&lt;br /&gt;&lt;br /&gt;Eventually I would take a more proactive role towards landing jobs.  There is a jobs forum, and people will frequently post that they are looking for developers.  Most of the ones I went after would turn out to be beyond my skill level, and I'd have to decline.  But I'd get smaller ones, and gradually built up a small pool of regular clients.  Now I'm doing everything from basic support to full-bore application development.&lt;br /&gt;&lt;br /&gt;At this point, I'm happy to report that &lt;a href="http://briterideas.com/"&gt;BriTer Ideas LLC&lt;/a&gt; has experienced 5 consecutive months of record revenue, and signs are looking good for the future.&lt;br /&gt;&lt;br /&gt;I found my new job.&lt;br /&gt;&lt;br /&gt;The ratio of contract services income : SlickShopper sales is so skewed it isn't even funny.  I can make more in an hour doing work for other people than SlickShopper will make in a week.  I actually have started on version 2, but I'm so busy with other things (read: actually making money) that I don't honestly know when I'll have time to finish it.  And I feel bad, too, because the new features are things that I want but didn't know how to do for version 1.&lt;br /&gt;&lt;br /&gt;I still have a ways to go in order to regain my former glory.  I'm hovering at just under half of what my former salary was, so I'd like to find ways of bringing in more income.  Exceeding my former salary is my next main goal.  I feel like the potential is there, I just need to put myself into position to take advantage of it.  Also, like most small/home business owners, I need to add on the stress of being concerned about from where the next job will come.  That will be less of a concern once my wife finds a new job, but for the moment it's nerve-wracking.&lt;br /&gt;&lt;br /&gt;Otherwise, I'm having a blast.  I can't remember the last time I had this much fun in engineering, if I even ever did.  I literally do work 7 days a week.  I won't pretend that I'm 100% productive each day, but I've been hitting it hard for months and don't feel like I'm forcing myself to do it.&lt;br /&gt;&lt;br /&gt;A year ago, I said:&lt;br /&gt;&lt;blockquote&gt;"One way or another, 2009 should be an interesting year."&lt;/blockquote&gt;&lt;br /&gt;I was right.  And I'm really looking forward to 2010.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38820386-3178540632542060050?l=clingingtoideas.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/VdkDEuEM579yvnGtQhBRrYlAoAA/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/VdkDEuEM579yvnGtQhBRrYlAoAA/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/VdkDEuEM579yvnGtQhBRrYlAoAA/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/VdkDEuEM579yvnGtQhBRrYlAoAA/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ClingingToIdeas/~4/OBuBdl-D6WY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://clingingtoideas.blogspot.com/feeds/3178540632542060050/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=38820386&amp;postID=3178540632542060050" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/3178540632542060050?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/3178540632542060050?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ClingingToIdeas/~3/OBuBdl-D6WY/goals-and-achievements.html" title="Goals and Achievements" /><author><name>The Slick One</name><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="31" height="32" src="http://1.bp.blogspot.com/_nJq3n1l5xlM/SmYOXU5LAyI/AAAAAAAAABU/-SvDv814nmI/S220/DSC_1796.jpg" /></author><thr:total>1</thr:total><feedburner:origLink>http://clingingtoideas.blogspot.com/2009/11/goals-and-achievements.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0MESXc6eip7ImA9WxNRF0U.&quot;"><id>tag:blogger.com,1999:blog-38820386.post-754808622441574823</id><published>2009-09-12T17:03:00.001-04:00</published><updated>2009-09-12T17:03:28.912-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-09-12T17:03:28.912-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Subversion" /><category scheme="http://www.blogger.com/atom/ns#" term="Xcode" /><category scheme="http://www.blogger.com/atom/ns#" term="cocoa" /><title>Subversion Basics In Xcode</title><content type="html">My &lt;a href="http://clingingtoideas.blogspot.com/2009/09/getting-started-using-beanstalk-with.html"&gt;last post&lt;/a&gt; discussed how to set up Xcode to use Subversion via &lt;a href="http://beanstalkapp.com/"&gt;Beanstalk&lt;/a&gt;.  Now let's take a look at some common activities.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Editing Files&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;This is easy enough... make some changes to a file, then save it.&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 335px;" src="http://homepage.mac.com/brianslick/blogpics/beanstalk/file_modify.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;The SCM column will provide status information when appropriate for each file.  In this case, 'M' is shown, indicating that the file has been modified.  You could see any number of M's, depending on how many files you change between commits (more on commit in a moment).  See the &lt;a href="http://developer.apple.com/mac/library/documentation/DeveloperTools/Conceptual/XcodeSourceManagement/30-Source_Control/source_control.html#//apple_ref/doc/uid/TP40002686-SW22"&gt;Apple documentation&lt;/a&gt; for a complete list of possible symbols.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Adding Files&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;Whether you create a new file inside your project, or add existing files from elsewhere, the end result is the same:&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 335px;" src="http://homepage.mac.com/brianslick/blogpics/beanstalk/file_add_01.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;The SCM column will display a '?', and as seen from the help pop-up, that means the status is unknown.  This indicates that the file is not yet in the repository - it is in your local project only.&lt;br /&gt;&lt;br /&gt;To indicate that you would like to add these files to your repository, right-click on any appropriate files, and choose 'Add to Repository'.&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 335px;" src="http://homepage.mac.com/brianslick/blogpics/beanstalk/file_add_02.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;The '?' will change to an 'A'.&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 335px;" src="http://homepage.mac.com/brianslick/blogpics/beanstalk/file_add_03.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;This indicates that the file will be added to the repository during the next commit.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Committing Changes&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;I am more accustomed to "check in" terminology, but a commit is exactly the same thing.  Local changes will be uploaded to the repository.&lt;br /&gt;&lt;br /&gt;Go to the SCM menu, and choose 'Commit Entire Project...'.&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 335px;" src="http://homepage.mac.com/brianslick/blogpics/beanstalk/commit_01.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;I believe you can do commits on individual files, but I haven't had much of a reason to do so.  I pretty much always commit the entire project.&lt;br /&gt;&lt;br /&gt;Xcode will present a dialog box allowing you to enter notes related to this commit.&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 426px;" src="http://homepage.mac.com/brianslick/blogpics/beanstalk/commit_02.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;Once you are done, the M's and A's should all be gone, indicating the files are now in the repository.&lt;br /&gt;&lt;br /&gt;How often you commit is up to you.  I personally attempt to do so at minimum nightly for any files that I work on.  If I achieve some major milestone earlier in the day, I'll send up an extra commit.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Updating Files&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;If other people share your repository, or if you access it from multiple computers, you will probably see this at some point:&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 275px;" src="http://homepage.mac.com/brianslick/blogpics/beanstalk/update_01.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;The U indicates that the file in the repository is newer than your local file, and it needs to be updated.  As a convenience, Xcode will repeat the status indicator on the group folder, just in case you are not displaying all files at the moment.&lt;br /&gt;&lt;br /&gt;You can either right-click on individual files and update...&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 289px;" src="http://homepage.mac.com/brianslick/blogpics/beanstalk/update_02.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;...or you can update the entire project.&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 289px;" src="http://homepage.mac.com/brianslick/blogpics/beanstalk/update_03.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;And this concludes what I know about Subversion.  Hopefully these two posts will get you off to a good start.  Enjoy.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38820386-754808622441574823?l=clingingtoideas.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/Qk0zNkkQikIV2h-DQzOdvsL8s8U/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Qk0zNkkQikIV2h-DQzOdvsL8s8U/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/Qk0zNkkQikIV2h-DQzOdvsL8s8U/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Qk0zNkkQikIV2h-DQzOdvsL8s8U/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ClingingToIdeas/~4/piEGtLbu5dc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://clingingtoideas.blogspot.com/feeds/754808622441574823/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=38820386&amp;postID=754808622441574823" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/754808622441574823?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/754808622441574823?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ClingingToIdeas/~3/piEGtLbu5dc/subversion-basics-in-xcode.html" title="Subversion Basics In Xcode" /><author><name>The Slick One</name><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="31" height="32" src="http://1.bp.blogspot.com/_nJq3n1l5xlM/SmYOXU5LAyI/AAAAAAAAABU/-SvDv814nmI/S220/DSC_1796.jpg" /></author><thr:total>1</thr:total><feedburner:origLink>http://clingingtoideas.blogspot.com/2009/09/subversion-basics-in-xcode.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkMARX0-cCp7ImA9WxBSGEU.&quot;"><id>tag:blogger.com,1999:blog-38820386.post-6041775362348306394</id><published>2009-09-12T01:08:00.001-04:00</published><updated>2009-12-26T23:34:04.358-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-12-26T23:34:04.358-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Subversion" /><category scheme="http://www.blogger.com/atom/ns#" term="Xcode" /><category scheme="http://www.blogger.com/atom/ns#" term="cocoa" /><title>Getting Started Using Beanstalk With Xcode</title><content type="html">I have lived in a data-managed world for years.  I-DEAS has long distinguished itself as one of, if not THE, only CAD programs with an integrated data manager.  So I am quite comfortable with the concepts of access control, version control, and pretty much any other -controls that go along with data management.&lt;br /&gt;&lt;br /&gt;When I first got started coding, I quickly realized that it sure would be nice to have the backups that go along with regular check-ins into a managed database.  I did some poking around, and it sounded like Subversion was a pretty safe way to go.  But as far as actually implementing it, well, that sounded like a bigger hurdle than I was willing to tackle at the time.&lt;br /&gt;&lt;br /&gt;I don't remember the specifics, but I was doing some Googling for hosted Subversion services, and landed on &lt;a href="http://beanstalkapp.com/"&gt;Beanstalk&lt;/a&gt;.  I knew nearly nothing about it, and don't know much more today, but most importantly they offer a &lt;a href="http://beanstalkapp.com/pricing"&gt;freebie&lt;/a&gt; account.  It's a nice way to try out Subversion; it doesn't cost anything other than time.&lt;br /&gt;&lt;br /&gt;The question then became how do I get the work I've already done into Beanstalk, and then how do I use it?  If there is a handy guide out there, I didn't find it.  So allow me to present my attempt at providing that guide.&lt;br /&gt;&lt;br /&gt;I should preface this by saying that beyond initial setup, I don't know a whole about Subversion.  I'm only working by myself; I'm not trying to coordinate efforts with a whole team of people.  I don't know how to branch.  I really only use it to commit my changes regularly, and if necessary revert back to an older version of something.&lt;br /&gt;&lt;br /&gt;Preamble out of the way, let's begin...&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Repository Configuration&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;Once you have created your Beanstalk account, using your web browser go to the Repositories tab.&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 329px;" src="http://homepage.mac.com/brianslick/blogpics/beanstalk/beanstalk_repositories_tab.png" border="0" alt="" /&gt;&lt;br /&gt;Click on the desired repository&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px;" src="http://homepage.mac.com/brianslick/blogpics/beanstalk/beanstalk_repository_select.png" border="0" alt="" /&gt;&lt;br /&gt;Then copy your repository URL.&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 346px;" src="http://homepage.mac.com/brianslick/blogpics/beanstalk/beanstalk_repository_url.png" border="0" alt="" /&gt;&lt;br /&gt;You are now finished with your web browser, as pretty much everything else is done in Xcode.  So, fire it up.&lt;br /&gt;&lt;br /&gt;You will be using the SCM menu a lot, so find it now, and select Configure SCM Repositories.&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 346px;" src="http://homepage.mac.com/brianslick/blogpics/beanstalk/configure_scm_01.png" border="0" alt="" /&gt;&lt;br /&gt;This takes you into Xcode's preferences under the SCM tab.&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 625px;" src="http://homepage.mac.com/brianslick/blogpics/beanstalk/add_repository_01.png" border="0" alt="" /&gt;&lt;br /&gt;Here you can see that I have several repositories already.  Click the + button at the bottom of the Repositories list to create a new one.  Keep your eye on the green dot that says 'Authenticated', as that will be important.&lt;br /&gt;&lt;br /&gt;Xcode will prompt you for a name for this repository, so call it whatever you want.  This does not need to be the same name as your project, but you will probably want it to at least be related.  Select 'Subversion' for the SCM System.&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 405px;" src="http://homepage.mac.com/brianslick/blogpics/beanstalk/add_repository_02.png" border="0" alt="" /&gt;&lt;br /&gt;We now have a new blank repository.&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 625px;" src="http://homepage.mac.com/brianslick/blogpics/beanstalk/add_repository_03.png" border="0" alt="" /&gt;&lt;br /&gt;Notice that our green dot is no longer a green dot.  By the time we're done, it should be, and that's the indication that everything is set up correctly.&lt;br /&gt;&lt;br /&gt;If that Beanstalk URL isn't in your clipboard anymore, go back and copy it.  Paste it into the URL field.  Any time you make a change to a text field here, tab out of it to see the result.&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 625px;" src="http://homepage.mac.com/brianslick/blogpics/beanstalk/add_repository_04.png" border="0" alt="" /&gt;&lt;br /&gt;Notice that the scheme, host, and path were automatically determined from the URL.  And our green dot is now an angry red dot.  Fear not, you merely need to provide your username and password.  Don't forget to hit tab after each text field change.&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 625px;" src="http://homepage.mac.com/brianslick/blogpics/beanstalk/add_repository_05.png" border="0" alt="" /&gt;&lt;br /&gt;Once your information is correct, you should have a green dot.  If not, verify your login and password.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Initial Upload&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;At this point, Xcode can talk to Beanstalk.  However, there aren't any files in there yet to talk to.  You can exit the Preferences panel, and go back up to the SCM menu and hit Repositories.&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 301px;" src="http://homepage.mac.com/brianslick/blogpics/beanstalk/repositories_01.png" border="0" alt="" /&gt;&lt;br /&gt;You'll see a listing of your repositories, and if you select one, you'll see the files and folders contained therein.  This is looking at Beanstalk; these are not local files.  The existing folders - branches, tags, trunk - I believe are relatively standard for Subversion.&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 633px;" src="http://homepage.mac.com/brianslick/blogpics/beanstalk/repositories_02.png" border="0" alt="" /&gt;&lt;br /&gt;We are interested in the trunk folder, so select that.&lt;br /&gt;&lt;br /&gt;To add your project, hit the 'Import' button.&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 633px;" src="http://homepage.mac.com/brianslick/blogpics/beanstalk/repositories_03.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;Select the folder that contains your project.&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 635px;" src="http://homepage.mac.com/brianslick/blogpics/beanstalk/repositories_04.png" border="0" alt="" /&gt;&lt;br /&gt;If you want, enter in a comment indicating what you are doing.  But since this is the first time you are doing anything, it should probably be pretty easy to figure out later.&lt;br /&gt;&lt;br /&gt;After you hit 'Import', Xcode will begin uploading your local files into Beanstalk.&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 633px;" src="http://homepage.mac.com/brianslick/blogpics/beanstalk/repositories_05.png" border="0" alt="" /&gt;&lt;br /&gt;You can click around to verify that all of your files are there.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Initial Download&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;I don't know if this next part is actually required, or if you can continue working with your local files.  For the sake of this blog post, we're going to assume it is required.  If you poke around in Subversion documentation, you may see the phrase "Working Copy".  This refers to the local files on your hard drive that you have retrieved from Subversion.  So, we need to create that working copy.&lt;br /&gt;&lt;br /&gt;In the Repositories window, select the same folder that we just uploaded.  Then hit the 'Checkout' button.&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 633px;" src="http://homepage.mac.com/brianslick/blogpics/beanstalk/checkout_01.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;Xcode will prompt you for a download location.  I personally use my &lt;a href="http://www.getdropbox.com/"&gt;Dropbox&lt;/a&gt; folder for an additional layer of protection.&lt;br /&gt;&lt;br /&gt;When the download is complete, Xcode will provide an option to open that project.  Go ahead and do so, because we aren't quite done yet.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Final Setup&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;Once the project is open, right-click on Groups &amp; Files, and turn on the SCM option.&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 268px;" src="http://homepage.mac.com/brianslick/blogpics/beanstalk/scm_status_01.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;This will reveal a new column that displays status information about the files.&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 232px;" src="http://homepage.mac.com/brianslick/blogpics/beanstalk/scm_status_02.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;There isn't much to see yet, since we haven't done anything.  But the key thing to notice is that the SCM icon has an X through it.  I don't really know why, but there is one thing left to do.  Select the project file, then right-click, Get Info.&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 277px;" src="http://homepage.mac.com/brianslick/blogpics/beanstalk/scm_status_03.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;This will take you to the project info panel.  I'm taking these screenshots using Xcode 3.2, and this panel did see some slight modifications, but the core element we're looking for is still there in previous versions, you just may have to poke around for it a bit.&lt;br /&gt;&lt;br /&gt;Select the 'General' tab.&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 469px;" src="http://homepage.mac.com/brianslick/blogpics/beanstalk/scm_status_04.png" border="0" alt="" /&gt;&lt;br /&gt;Then hit the 'Configure Roots &amp; SCM' button.&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 469px;" src="http://homepage.mac.com/brianslick/blogpics/beanstalk/scm_status_05.png" border="0" alt="" /&gt;&lt;br /&gt;Make sure the pull-down menu says Subversion.  In the Repository column, hit the pull-down there to select your repository.  The name will most likely have "Recommended" next to it.  I honestly don't know why this step is necessary; Xcode seems to know this stuff already, so you'd think it could set itself up.  Oh well.&lt;br /&gt;&lt;br /&gt;Once that is done, the X should disappear from the column's icon.&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 279px;" src="http://homepage.mac.com/brianslick/blogpics/beanstalk/scm_status_06.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;At last we are set up!&lt;br /&gt;&lt;br /&gt;Come back later for &lt;a href="http://clingingtoideas.blogspot.com/2009/09/subversion-basics-in-xcode.html"&gt;part 2&lt;/a&gt;, at which point I'll discuss some usage basics.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38820386-6041775362348306394?l=clingingtoideas.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/I3TONWWkwnWEh5bkxj0EG8jZyJY/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/I3TONWWkwnWEh5bkxj0EG8jZyJY/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/I3TONWWkwnWEh5bkxj0EG8jZyJY/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/I3TONWWkwnWEh5bkxj0EG8jZyJY/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ClingingToIdeas/~4/Ew7RlbWX8TQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://clingingtoideas.blogspot.com/feeds/6041775362348306394/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=38820386&amp;postID=6041775362348306394" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/6041775362348306394?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/6041775362348306394?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ClingingToIdeas/~3/Ew7RlbWX8TQ/getting-started-using-beanstalk-with.html" title="Getting Started Using Beanstalk With Xcode" /><author><name>The Slick One</name><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="31" height="32" src="http://1.bp.blogspot.com/_nJq3n1l5xlM/SmYOXU5LAyI/AAAAAAAAABU/-SvDv814nmI/S220/DSC_1796.jpg" /></author><thr:total>1</thr:total><feedburner:origLink>http://clingingtoideas.blogspot.com/2009/09/getting-started-using-beanstalk-with.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DU4EQX8zfip7ImA9WxNRFUg.&quot;"><id>tag:blogger.com,1999:blog-38820386.post-1873729925807488337</id><published>2009-09-10T00:45:00.000-04:00</published><updated>2009-09-10T00:45:00.186-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-09-10T00:45:00.186-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="slickshopper" /><category scheme="http://www.blogger.com/atom/ns#" term="cocoa" /><category scheme="http://www.blogger.com/atom/ns#" term="iphone" /><title>Taming Table Views</title><content type="html">This is the table view from one of the primary screens in &lt;a href="http://www.briterideas.com/BriTer_Ideas/SlickShopper.html"&gt;SlickShopper&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 322px;" src="http://homepage.mac.com/brianslick/blogpics/slickshopper/cells/overview.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;It may not look like much, but there is actually a lot going on here.  The key aspects I want to talk about now are the sections headers and the index.  More specifically, what the presence of those items means as far as preparing the data that is to be shown in the table.&lt;br /&gt;&lt;br /&gt;Naturally I have a complete list - an array - of all grocery items.  However, as soon as I made the decision that that I wanted section headers, I could no longer use that list directly.  Well, that may not be technically true, but from a "reasonable effort" standpoint, true enough.  In order to display section headers, the table view data has to be broken up into groups, with each group corresponding to a section in the table.&lt;br /&gt;&lt;br /&gt;The number of sections is defined using the table view data source method:&lt;br /&gt;&lt;pre&gt;– numberOfSectionsInTableView:&lt;/pre&gt;&lt;br /&gt;I've got "selected" and "not selected", so all I need to return here is 2.  So far so good.  I need 2 arrays, one for selected, one for not selected, and I need to run through my master list and add each grocery item to the appropriate array.  The remaining tableview delegate methods are straightforward; needing only to ping each array for information.  Easy.&lt;br /&gt;&lt;br /&gt;...until I decided that having the index on the side would be a really useful thing.  Why does that matter?  Well, the first clue is in the method name that provides the index:&lt;br /&gt;&lt;pre&gt;- sectionIndexTitlesForTableView:&lt;/pre&gt;&lt;br /&gt;Index titles for a &lt;em&gt;section&lt;/em&gt;.  Well, at the moment I only have 2 sections, but I'm going to want potentially almost 30 items to appear in the index.  Although you don't technically have to have a 1:1 section:index title ratio, it's a lot easier if you do.  When tapping the letter 'G' in the index, you want to navigate to items starting with the letter G, and that is most easily accomplished by grouping those items together, and having that be a section in the table.&lt;br /&gt;&lt;br /&gt;So now I'm looking at a whole mess of arrays: one for items starting with A, one for B, and so on.  Not a problem, I can add each of those separate arrays to one main array, and go from there.  Oh, but if no items start with a particular letter - say, Q - I don't want to show the letter in the index.  My array contains 24 arrays, and I'm looking at the 6th one, so which letter should appear in the index?  F?  What if there were no E's... it could be G.  Ugh.  &lt;strong&gt;Now&lt;/strong&gt; I need &lt;em&gt;another&lt;/em&gt; array to keep track of index letters.    I suppose I could grab grocery items and extract their first letter on the fly, but that has some issues (ex: I want to display "#" instead of "1", "2", etc).  I've already got my array of arrays to provide the contents, now I have another array for the letters, but I think we're almo... aw, crap... The section headers!  The screen shot above is only showing 2 headers, but there are indeed many sections involved.  And I have a different view of this same information that uses many headers.  Now I need some means of keeping track of the headers - maybe &lt;em&gt;another&lt;/em&gt; array?  I've got dozens of arrays now, each keeping track of different-but-related pieces of information... stop the insanity!&lt;br /&gt;&lt;br /&gt;After spending way too much effort doing exactly that, I finally hit upon what I consider to be a better way: a custom container object.  Something that can correlate to the sections in a table, and contain the necessary pieces of information all in one place: the section contents, the section header, the index letter, and for bonus points some handy methods for messing with all of that stuff.  So, I now present what I call a DisplaySection:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;//  DisplaySection.h&lt;br /&gt;//  SlickShopper&lt;br /&gt;//&lt;br /&gt;//  Created by Brian Slick on 4/5/09.&lt;br /&gt;//  Copyright 2009 BriTer Ideas LLC. All rights reserved.&lt;br /&gt;#import Foundation/Foundation.h  (Yeah, I know)&lt;br /&gt;@interface DisplaySection : NSObject&lt;br /&gt;{&lt;br /&gt;   NSString *myName;&lt;br /&gt;   NSString *mySectionHeader;&lt;br /&gt;   NSString *mySectionIndexLetter;&lt;br /&gt;   NSMutableArray *myContents;&lt;br /&gt;}&lt;br /&gt;// A few sample methods...&lt;br /&gt;- (void)addGroceryItem:(GroceryItem *)item;&lt;br /&gt;- (NSUInteger)countOfGroceryItems;&lt;br /&gt;- (GroceryItem *)groceryItemAtIndex:(NSUInteger)index;&lt;br /&gt;@end&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;There are a lot of ways to tailor this to individual liking.  If you like _instanceVariable names, do that.  If you want to make this generic with things like addObject, or countOfContents, do that.  If you want to access the properties directly without going through wordy method names, do that.  Those are implementation details that do not affect the core concept.&lt;br /&gt;&lt;br /&gt;The point here is that everything I want to know about my section is here in one place: contents, section header, section index letter.  On top of that, I found it necessary to add an extra property for behind-the-scenes purposes.&lt;br /&gt;&lt;br /&gt;How does this work?  Well, I don't want to give away all of the family jewels, but in essence most of the sorting work that I described before still has to happen.  I still have to iterate through my master list of grocery items.  I still have to have a place to put each item, so that means creating a bunch of these DisplaySections, and filling them with appropriate content.  And I want these to be presented nicely for the table, so that means putting a bunch of these DisplaySections into an array.&lt;br /&gt;&lt;br /&gt;So what's really different?  For one thing, I've moved all appropriate logic into the sorting routine.  Take the section header, for example.  Before, I was defining that in the table delegate, making decisions based on the index path.  If section is 0, header is this, else if section is 1, header is that, and so on.  Those decisions still have to be made, but I no longer do that in the delegate methods.  As I iterate through the main array, I make those decisions at that time, and react accordingly.  This is my first DisplaySection, so the header property should be set to this.  This DisplaySection will contain all items starting with H, so set the index letter accordingly.  All of the logic is in one place.  If I want to change behaviors, that's where I go.&lt;br /&gt;&lt;br /&gt;A side effect of this is that it simplifies the delegate methods tremendously.  Here is how the header gets returned:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;- (NSString *)tableView:(UITableView *)tableView&lt;br /&gt;titleForHeaderInSection:(NSInteger)section&lt;br /&gt;{&lt;br /&gt;   return [[[DataController sharedDataController] displaySectionAtIndex:section] sectionHeader];&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;(I'm using a singleton object for the data controller... go &lt;a href="http://cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html"&gt;here&lt;/a&gt; for more info)&lt;br /&gt;&lt;br /&gt;I can assure you that this was more than one line of code previously.  Now I ask the sorted array for the appropriate DisplaySection, and I grab the needed piece of information.  The logic is removed from the delegate, and it merely does what it is supposed to do - relay information from the model to the view.&lt;br /&gt;&lt;br /&gt;For the index letters, I elected to spin my own array in the delegate method.  I really wish Apple had gone with more of a:&lt;br /&gt;&lt;pre&gt;- (NSString *)tableView:indexTitleForSection:&lt;/pre&gt;&lt;br /&gt;approach, but they didn't.  So I just loop over each DisplaySection, grab the index letter property, and build a new array in:&lt;br /&gt;&lt;pre&gt;- (NSArray *)sectionIndexTitlesForTableView:&lt;/pre&gt;&lt;br /&gt;There is probably a better way, but this covers my needs.  I'm pretty sure that this method is only hit once during a reloadData, so I don't consider it much of a performance hit.&lt;br /&gt;&lt;br /&gt;How do I handle the case of nothing-starts-with-that-letter?  That's part of the sorting routine.  I actually go ahead and build the maximum possible number of DisplaySections up front, and add them to the array.  Then I loop through the grocery items, and add them to the appropriate DisplaySection.  After that is done, I loop back through the DisplaySections, and remove any that are empty.&lt;br /&gt;&lt;br /&gt;Once I had this technique down, it was a lot easier for me to go back and add the index to several more of my views.  So I'm very happy with this approach, but I'm always interested in suggestions.  Have a better way?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38820386-1873729925807488337?l=clingingtoideas.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/EBSll8ptQrZS3P7xoIg_c_UJTQI/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/EBSll8ptQrZS3P7xoIg_c_UJTQI/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/EBSll8ptQrZS3P7xoIg_c_UJTQI/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/EBSll8ptQrZS3P7xoIg_c_UJTQI/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ClingingToIdeas/~4/7heV25F9_NY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://clingingtoideas.blogspot.com/feeds/1873729925807488337/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=38820386&amp;postID=1873729925807488337" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/1873729925807488337?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/1873729925807488337?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ClingingToIdeas/~3/7heV25F9_NY/taming-table-views.html" title="Taming Table Views" /><author><name>The Slick One</name><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="31" height="32" src="http://1.bp.blogspot.com/_nJq3n1l5xlM/SmYOXU5LAyI/AAAAAAAAABU/-SvDv814nmI/S220/DSC_1796.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://clingingtoideas.blogspot.com/2009/09/taming-table-views.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUAGSHg7fSp7ImA9WxNTFU4.&quot;"><id>tag:blogger.com,1999:blog-38820386.post-1733632165970657194</id><published>2009-08-17T15:07:00.003-04:00</published><updated>2009-08-17T15:35:29.605-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-08-17T15:35:29.605-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="cocoa" /><category scheme="http://www.blogger.com/atom/ns#" term="iphone" /><title>It's NSLog, NSLog...</title><content type="html">&lt;p&gt;&lt;i&gt;&lt;span class="Apple-style-span"  style="font-size:x-small;"&gt;What rolls downstairs, alone or in pairs, and over your neighbor's dog?&lt;br /&gt;What's great for a snack, and fits on your back? It's NSLog, NSLog, NSLog!&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;&lt;p&gt;I do crazy amounts of logging in my programs. This has proven to be an invaluable tool for me as I learn how Cocoa/Cocoa Touch works. Being relatively new to Object-Oriented Programming, it has taken me a while to wrap my head around when methods get called, in what order, and so on. So I borrowed a technique I was using in the I-DEAS macro language in order to help me figure out what was happening:&lt;/p&gt;&lt;pre&gt;NSLog(@"Now entering viewDidLoad in RootViewController");&lt;br /&gt;// Do stuff&lt;br /&gt;NSLog(@"Now leaving viewDidLoad in RootViewController");&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;The console will then present a nice history of what the program did. This is useful, especially for things like UITableView delegate methods, where simply putting the view on the screen can cause several methods to be triggered. Read back through the console, see which methods were called, note the order, learn how the system works.&lt;/p&gt;&lt;p&gt;I do both the 'entering' and 'leaving' part because simply putting the 'entering' one does not provide enough information, especially when trying to track down where a crash happened. A given method could branch out into any number of other methods, so without knowing when a method has finished, you don't really know which one crashed. You can make some partial assumptions by figuring out if expected methods were NOT called, but still, you don't get the complete picture. By adding the 'leaving' message, it's a matter of figuring out which method(s) started, but didn't finish.&lt;/p&gt;&lt;p&gt;The information provided by this technique is incredibly valuable, but it has (at least) two key drawbacks:&lt;br /&gt;&lt;br /&gt;1. The log messages themselves must be customized for each method, in each class. You can get away with some copy-n-pasting, but you are going to have to retype something every single time. That's annoying, time consuming, and a good source for mistakes (if you forget to change the information).&lt;br /&gt;&lt;br /&gt;2. The performance hit from displaying this many log messages is considerable, particularly on the iPhone. It's useful for debugging, but is not something you want to do for shipping code.&lt;/p&gt;&lt;p&gt;&lt;i&gt;&lt;span class="Apple-style-span"  style="font-size:x-small;"&gt;It's NSLog, it's NSLog... It's big, it's heavy, it's wood.&lt;br /&gt;It's NSLog, it's NSLog, it's better than bad, it's good!&lt;/span&gt;&lt;br /&gt;&lt;/i&gt;&lt;/p&gt;&lt;p&gt;I was doing this - what I'll call "the hard way" - for while before I decided there had to be an easier way. I did some Googling, and landed on &lt;a href="http://groups.google.com/group/xcodephoenix/browse_thread/thread/d35a46cc868b208d/f99563124e3f9d85?lnk=raot"&gt;this page&lt;/a&gt;. There are several good tips there, but this is the important part:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;NSLog(@"%s", __PRETTY_FUNCTION__);&lt;/p&gt;&lt;p&gt;Will print a log line similar to:&lt;/p&gt;&lt;p&gt;*2008-12-12 09:22:49.552 Healthcheck[79016:20b] -[HealthcheckAppDelegate applicationDidFinishLaunching:]*&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Perfect. That is copy-paste-able, no additional typing required. However, I still want my entering/leaving deal, so adapting this to my preferred style:&lt;/p&gt;&lt;pre&gt;NSLog(@"&amp;gt;&amp;gt;&amp;gt; Entering %s &amp;lt;&amp;lt;&amp;lt;", __PRETTY_FUNCTION__);&lt;br /&gt;// Do stuff&lt;br /&gt;NSLog(@"&amp;lt;&amp;lt;&amp;lt; Leaving %s &amp;gt;&amp;gt;&amp;gt;", __PRETTY_FUNCTION__);&lt;/pre&gt;&lt;p&gt;And I do this everywhere. Every single method. I'm not kidding.&lt;/p&gt;&lt;p&gt;Experienced programmers reading this are thinking I'm pretty ridiculous right now. But I need the extra help. And I highly, highly recommend this for people just getting started. I've been answering a lot of questions on &lt;a href="http://www.iphonedevsdk.com/"&gt;iPhone Dev SDK&lt;/a&gt;, and am amazed at the number of people who know nothing about logging. And I don't mean noobs, I mean people with apps on the store! I'd probably still be floundering around with my very first bug without liberal use of logging. So, my hat's off to people who can get by without, but I have to assume that life could be easier for them.&lt;/p&gt;&lt;p&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-size: x-small;"&gt;Everyone wants a NSLog&lt;br /&gt;You're gonna love it, NSLog&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;&lt;p&gt;So that addressed the first problem, but what about the second? Fortunately, I got an answer to that without really even asking the question. After finding the pretty function thing, and posting on Twitter about how awesome that is, &lt;a href="http://www.danilocampos.com/"&gt;Danilo Campos&lt;/a&gt; was kind enough to make an off-hand remark that I should disable my log messages before submitting my app to the store. I had no idea how to do that, so with a couple of generous emails, he got me squared away. So full credit goes to him for this technique.&lt;/p&gt;&lt;p&gt;First, your program should have a *.pch file in it somewhere, named something like &amp;lt;YourAppName&amp;gt;_Prefix.pch. In my case, this file is SlickShopper_Prefix.pch. Add the following to that file:&lt;/p&gt;&lt;pre&gt;#ifdef DCBLOCKNSLOGSTATEMENTS&lt;br /&gt;#define NSLog(format, ...)&lt;br /&gt;#else&lt;br /&gt;#define NSLog(format, ...) NSLog(format, ## __VA_ARGS__)&lt;br /&gt;#endif&lt;/pre&gt;&lt;p&gt;The 'DC' part is for &lt;strong&gt;D&lt;/strong&gt;anilo &lt;strong&gt;C&lt;/strong&gt;ampos, so if you want to change that, you can. I've decided to leave it alone, along with a commented note giving him credit. Now I won't claim to totally understand what this says, but it looks like if DCBLOCKNSLOGSTATEMENTS is defined, do something to NSLog statements, if not, do something else. In this case, the 'do something' part is to make the NSLog statements not do anything, and the 'do something else' case is leave them alone. So that's the easy part. The harder part is getting this to work.&lt;/p&gt;&lt;p&gt;What you need to do now is mess with the settings in your build configurations panel. To do this, select your project, and hit the Info button, like so:&lt;/p&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 252px;" src="http://homepage.mac.com/brianslick/blogpics/nslog/project_info.png" border="0" alt="" /&gt;&lt;p&gt;Next, go to the 'Build' tab, select the "Release" configuration, and do a search for "preprocessor". Ex:&lt;/p&gt;&lt;p&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 652px;" src="http://homepage.mac.com/brianslick/blogpics/nslog/preprocessor_search.png" border="0" alt="" /&gt;You are looking for the ones listed under "GCC 4.2 - Preprocessing" - the "Preprocessor Macros" one and the "Preprocessor Macros Not Used In Precompiler" one. I occasionally cannot find these items. The solution seems to be to go to the 'General' tab. There is a pull-down menu next to "Base SDK for All Configurations". Set that to "iPhone Simulator 3.0", and then try searching again.&lt;/p&gt;&lt;p&gt;It is very important that you select the Release configuration. Do not do this in Debug. We're about to kill NSLog statements; we want those in Debug.&lt;/p&gt;&lt;p&gt;Now, double-click in the 'Value' column field next to Preprocessor Macros. A sheet will slide in.&lt;/p&gt;&lt;p&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 652px;" src="http://homepage.mac.com/brianslick/blogpics/nslog/preprocessor_macros.png" border="0" alt="" /&gt;&lt;/p&gt;&lt;p&gt;Click the plus button to add a new value.&lt;/p&gt;&lt;p&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 652px;" src="http://homepage.mac.com/brianslick/blogpics/nslog/preprocessor_add.png" border="0" alt="" /&gt;&lt;/p&gt;&lt;p&gt;Paste in "DCBLOCKNSLOGSTATEMENTS" (or whatever you called it).&lt;/p&gt;&lt;p&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 652px;" src="http://homepage.mac.com/brianslick/blogpics/nslog/preprocessor_paste.png" border="0" alt="" /&gt;&lt;/p&gt;&lt;p&gt;Hit OK...&lt;/p&gt;&lt;p&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 652px;" src="http://homepage.mac.com/brianslick/blogpics/nslog/preprocessor_done.png" border="0" alt="" /&gt;&lt;/p&gt;&lt;p&gt;...and then do the same thing for the second item.&lt;/p&gt;&lt;p&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 652px;" src="http://homepage.mac.com/brianslick/blogpics/nslog/preprocessor_both.png" border="0" alt="" /&gt;&lt;/p&gt;&lt;p&gt;And that's it. Just as a quick check, flip over to the Debug configuration:&lt;/p&gt;&lt;p&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 652px;" src="http://homepage.mac.com/brianslick/blogpics/nslog/preprocessor_debug.png" border="0" alt="" /&gt;&lt;/p&gt;&lt;p&gt;You do NOT want to see the items we just added here.&lt;/p&gt;&lt;p&gt;If you have additional configurations already, say one for beta-testing and another for App Store submission, you will probably want to repeat these steps for those configurations as well.&lt;/p&gt;&lt;p&gt;Now test it out. Build-and-go with Debug, and observe your log messages. Do it again with Release, and observe the lack of log messages, and the snappier performance.&lt;/p&gt;&lt;p&gt;Next, head over to Danilo's web site and buy some of his apps as a thank you for making this technique known. And uh, if you want to buy &lt;a href="http://www.itunes.com/apps/slickshopper"&gt;mine&lt;/a&gt; for typing this up, well that'd be nice, too.&lt;/p&gt;&lt;p&gt;Have a better way? Let me know in the comments.&lt;/p&gt;&lt;p&gt;&lt;i&gt;&lt;span class="Apple-style-span"  style="font-size:x-small;"&gt;Everyone wants a NSLog. You're gonna love it, NSLog&lt;br /&gt;Come on and get your NSLog. Everyone needs a NSLog&lt;br /&gt;NSLog NSLog NSLog!&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38820386-1733632165970657194?l=clingingtoideas.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/smOSjWoPZDA7MVuylzUf7DWKohY/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/smOSjWoPZDA7MVuylzUf7DWKohY/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/smOSjWoPZDA7MVuylzUf7DWKohY/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/smOSjWoPZDA7MVuylzUf7DWKohY/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ClingingToIdeas/~4/Sy3aSLfVisE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://clingingtoideas.blogspot.com/feeds/1733632165970657194/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=38820386&amp;postID=1733632165970657194" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/1733632165970657194?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/1733632165970657194?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ClingingToIdeas/~3/Sy3aSLfVisE/it-nslog-nslog.html" title="It&amp;#39;s NSLog, NSLog..." /><author><name>The Slick One</name><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="31" height="32" src="http://1.bp.blogspot.com/_nJq3n1l5xlM/SmYOXU5LAyI/AAAAAAAAABU/-SvDv814nmI/S220/DSC_1796.jpg" /></author><thr:total>4</thr:total><feedburner:origLink>http://clingingtoideas.blogspot.com/2009/08/it-nslog-nslog.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ak8ESHg-cCp7ImA9WhZSEEg.&quot;"><id>tag:blogger.com,1999:blog-38820386.post-1335744363482054702</id><published>2009-07-20T11:58:00.001-04:00</published><updated>2011-03-25T09:13:29.658-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-03-25T09:13:29.658-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="UITableView" /><category scheme="http://www.blogger.com/atom/ns#" term="cocoa touch" /><category scheme="http://www.blogger.com/atom/ns#" term="cocoa" /><category scheme="http://www.blogger.com/atom/ns#" term="around the web" /><category scheme="http://www.blogger.com/atom/ns#" term="iphone" /><title>Now Presenting... My iPhone Presentations</title><content type="html">The &lt;a href="http://www.mobile.awesomeinc.org/"&gt;Mobile Mini-Conference&lt;/a&gt; has come and gone.  I was not told whether Awesome Inc would be providing the presentations after the fact, so I'll just short-circuit the process and make mine available now.  Here you go:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://homepage.mac.com/brianslick/blogpics/ResourcesTipsTricks.pdf"&gt;iPhone Development Resources, Tips, and Tricks&lt;/a&gt; (~7MB PDF)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://homepage.mac.com/brianslick/blogpics/UITableView.pdf"&gt;UITableView Overview&lt;/a&gt; (~1MB PDF)&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;Feel free to contact me with any further questions. Contact info is at the end of each presentation.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38820386-1335744363482054702?l=clingingtoideas.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/XPgDMC5VRRbRAXFwI35xTeM-kDE/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/XPgDMC5VRRbRAXFwI35xTeM-kDE/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/XPgDMC5VRRbRAXFwI35xTeM-kDE/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/XPgDMC5VRRbRAXFwI35xTeM-kDE/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ClingingToIdeas/~4/TQkkTQljsHs" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://clingingtoideas.blogspot.com/feeds/1335744363482054702/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=38820386&amp;postID=1335744363482054702" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/1335744363482054702?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/1335744363482054702?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ClingingToIdeas/~3/TQkkTQljsHs/now-presenting-my-iphone-presentations.html" title="Now Presenting... My iPhone Presentations" /><author><name>The Slick One</name><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="31" height="32" src="http://1.bp.blogspot.com/_nJq3n1l5xlM/SmYOXU5LAyI/AAAAAAAAABU/-SvDv814nmI/S220/DSC_1796.jpg" /></author><thr:total>2</thr:total><feedburner:origLink>http://clingingtoideas.blogspot.com/2009/07/now-presenting-my-iphone-presentations.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUAFR3s9eyp7ImA9WxJUE0o.&quot;"><id>tag:blogger.com,1999:blog-38820386.post-6587700818228914369</id><published>2009-07-12T00:41:00.001-04:00</published><updated>2009-07-12T00:41:56.563-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-07-12T00:41:56.563-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="around the web" /><category scheme="http://www.blogger.com/atom/ns#" term="iphone" /><category scheme="http://www.blogger.com/atom/ns#" term="business" /><title>Now Presenting... Me</title><content type="html">&lt;a href="http://awesomeinc.org/"&gt;Awesome Inc&lt;/a&gt; is hosting what they are calling &lt;a href="http://www.mobile.awesomeinc.org/"&gt;Mobile Mini-Conference&lt;/a&gt; at their facility in Lexington, KY on Saturday, July 18th.  This is ostensibly a generic mobile device conference, but as the schedule demonstrates there will be a particular focus on the iPhone.  Here is a fancy badge:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.mobile.awesomeinc.org" target="_blank"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand; "src="http://www.mobile.awesomeinc.org/uploads/badge.png" width="289" border="0" alt="Awesome Inc. Mobile Conference" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I have been invited to speak, and will be giving two presentations.  They are:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;UITableView Overview&lt;/li&gt;&lt;br /&gt;&lt;li&gt;iPhone Development Resources, Tips, and Tricks&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;I hadn't even started &lt;a href="http://www.briterideas.com/BriTer_Ideas/SlickShopper.html"&gt;SlickShopper&lt;/a&gt; 6 months ago, so if you had told me then that I'd be giving iPhone presentations now, I'd have thought and said you were crazy.  But here we go.  It's tough to do much justice to any development topic in 30 minutes, but I think my presentations will be worthwhile.&lt;br /&gt;&lt;br /&gt;I'm not really nervous, per se, as I've done plenty of presentations before.  However, this will be the first in a long time where I'm presenting on a topic where I don't have a solid and lengthy background.  I'm also accustomed to practicing my presentations on my coworkers, but I don't have any of those at the moment.  I'll do fine on the speaking part, but I'm not looking forward to fielding questions.  Whenever I talk about I-DEAS, I'm among, if not THE, most knowledgeable subject experts in the room.  I'll be shocked if that is the case with iPhone development, and a couple of well-placed questions could shatter the illusion that I know what I'm talking about.  But, this is a brand new conference, so I don't really know what to expect.&lt;br /&gt;&lt;br /&gt;So at any rate, if you are within a reasonable drive of Lexington and have any interest in iPhone development, come check us out.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38820386-6587700818228914369?l=clingingtoideas.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/gCP8C45R3o_4lUBSIPQ9d5JqtF4/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/gCP8C45R3o_4lUBSIPQ9d5JqtF4/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/gCP8C45R3o_4lUBSIPQ9d5JqtF4/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/gCP8C45R3o_4lUBSIPQ9d5JqtF4/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ClingingToIdeas/~4/CGsSa08VxqU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://clingingtoideas.blogspot.com/feeds/6587700818228914369/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=38820386&amp;postID=6587700818228914369" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/6587700818228914369?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/6587700818228914369?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ClingingToIdeas/~3/CGsSa08VxqU/now-presenting-me.html" title="Now Presenting... Me" /><author><name>The Slick One</name><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="31" height="32" src="http://1.bp.blogspot.com/_nJq3n1l5xlM/SmYOXU5LAyI/AAAAAAAAABU/-SvDv814nmI/S220/DSC_1796.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://clingingtoideas.blogspot.com/2009/07/now-presenting-me.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CU8EQn8_fyp7ImA9WxJVFUo.&quot;"><id>tag:blogger.com,1999:blog-38820386.post-9157700109964456820</id><published>2009-07-02T18:30:00.000-04:00</published><updated>2009-07-02T18:30:03.147-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-07-02T18:30:03.147-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="slickshopper" /><category scheme="http://www.blogger.com/atom/ns#" term="cocoa" /><category scheme="http://www.blogger.com/atom/ns#" term="iphone" /><category scheme="http://www.blogger.com/atom/ns#" term="business" /><title>Rethinking The Problem</title><content type="html">I believe this is officially the first of what will hopefully be many technical posts about my iPhone program &lt;a href="http://www.briterideas.com/BriTer_Ideas/SlickShopper.html"&gt;SlickShopper&lt;/a&gt;.  I'm just about to release a semi-major update, and a couple of revelations I had along the way are, I feel, deserving of some attention.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;The Reason&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;SlickShopper doesn't break any new ground in the area of user interface.  It is a simple, table-driven interface that uses built-in widgets almost everywhere.  But the actual table cell - the row of information that is displayed - is a key area where I needed to provide something a little beyond what Apple includes out of the box.  Here is a sample that is typical of my three main list views:&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 322px;" src="http://homepage.mac.com/brianslick/blogpics/slickshopper/cells/overview.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;It's not super fancy, but for sake of this discussion, the important thing to notice is the number of pieces of information.  There is the 1) description, 2) quantity, 3) cost, and 4) checkbox.  So far, so good.&lt;br /&gt;&lt;br /&gt;I am a big believer of options in software.  As a user, I generally want options, and as a developer, if two reasonable people can see two reasonable ways of doing something, then if possible I'd like to support both people.  And I carried this philosophy into this program.  Except for the description, each other piece of information can be disabled.  If you don't care about costs or quantities, you can turn them off.  I personally can't stand having the checkboxes there (they are great looking; don't get me wrong... but I don't like having the second touch zone that they provide), so I turn those off, too.&lt;br /&gt;&lt;br /&gt;The more math-oriented readers might already see the factorial that is forming here.  I have 3 items with different display possibilities, so right now I have 3! = 3x2x1 = 6 variations.  But I'm not done yet.  I do have one display mode where I need to show the checkboxes, even if the user has turned them off.  I need them for visual distinction between selected and not-selected, but I just want them for display; I don't want the second touch zone.  So add one more option, and now I'm at 4! = 4x3x2x1 = 24 possibilities.  It actually isn't this bad; due to some overlap of the options, it turns out that I really only have 12 scenarios to handle.  For example:&lt;br /&gt;checkboxes ON, quantity OFF, price ON; or&lt;br /&gt;checkboxes ON, quantity ON, price OFF&lt;br /&gt;...and so on.  The logic for determining which scenario I'm in is going to be convoluted pretty much no matter what, but what about displaying of the actual cells?&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;The First Attempt&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;This would have been much simpler without the checkboxes.  I've had enough trouble getting my head wrapped around the use of Interface Builder for iPhone development as is, but I couldn't find any examples that included a button in the cell.  The only examples I could find built the cells in code, so that's what I did.  I'm not going to bother laying out the code for them, but I'll list a few of the file names just to give an idea of what I was doing:&lt;br /&gt;&lt;br /&gt;CompleteListCell.h&lt;br /&gt;CompleteNoCheckListCell.h&lt;br /&gt;CompleteImageOnlyListCell.h&lt;br /&gt;MinimalListCell.h&lt;br /&gt;MinimalNoCheckListCell.h&lt;br /&gt;MinimalImageOnlyListCell.h&lt;br /&gt;&lt;br /&gt;So first I decide how much information is being shown, based on the preference settings.  If costs, prices, and checkboxes are all turned on, then I use the CompleteListCell.  If the user turns off the checkboxes, then I fall back to CompleteNoCheckListCell.  BUT, if the user then chooses a display mode where I need to show the checkboxes, I swap in the CompleteImageOnlyListCell, which replaces the button with an image.&lt;br /&gt;&lt;br /&gt;This approach certainly does work, and is what shipped with my 1.0 and 1.1 versions.  When I originally planned this out, I came up with 12 variations, so I have 12 of these files.  Making them was easy enough; I built the most complicated one first, then pretty much just copy-pasted to make the others, removing code that no longer applied.  And as long as I don't ever need to change anything (HA!), these files will be just fine.  But if I decide to, say, change the font used for the description, I have to make that change in 12 places.  And if I decide to add more information to my cell, I'm going to need a crapload of new files.  So, this approach is workable, but ultimately doomed.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Duh&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;The first revelation concerns the checkboxes.  What normally happens when a user taps on a row is that tableView:didSelectRowAtIndexPath: is called, and in the method you decide what you want to do in response to that tap.  In this case, I want to slide in a detail view:&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px;" src="http://homepage.mac.com/brianslick/blogpics/slickshopper/cells/touchrow.gif" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;Easy enough.  However, the whole point of having the checkbox is to provide an alternate action.  If the user hits the checkbox, I want the row to animate out, like this:&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px;" src="http://homepage.mac.com/brianslick/blogpics/slickshopper/cells/touchcheckbox.gif" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;I have the checkbox pointed to a buttonPressed: method, where I do the necessary steps to animate out the row.&lt;br /&gt;&lt;br /&gt;The stumbling block was when I needed to show the checkboxes, but I didn't want them to function as buttons.  So instead of imbedding the button into my table cell, I chose to make extra cells that contained the checkmark image.  And then based on the appropriate preference setting, I chose which cell to display.  I was thinking about this far too literally.&lt;br /&gt;&lt;br /&gt;It suddenly occurred to me that I don't need to care about what kind of physical item - button or image - is being displayed, I only care about what happens when it is touched.  If it is an image, normal row selection should happen, if it is a button, something extra should happen.  But how do I tell the difference if I don't use a physically different item?&lt;br /&gt;&lt;br /&gt;The answer is so blindingly obvious (in hindsight, of course) and simple that I'm really mad I didn't figure it out sooner.  And that answer is the buttonPressed: method.  I already have a method for declaring what should happen when the button is tapped, so what I need to do is make a small change to it to accommodate both of my needs.  Here is the code:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;if (![userDefaults boolForKey:kPrefsCheckboxesKey])&lt;br /&gt;{&lt;br /&gt;     [self tableView:[self tableView] didSelectRowAtIndexPath:indexPath];&lt;br /&gt;     return;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;First, I figure out what the preference setting is.  If the checkboxes are turned off, I redirect to the exact same method that would normally be called when a row is tapped, and then bail out of the rest of the method.  The end result appears to be only a single touch zone for the user, even though technically it is still two.  I get my desired behavior, and this lets me remove the CompleteImageOnlyListCell.h cell and equivalent variations.  So, this realization alone allowed me to go from 12 to 8 cells.&lt;br /&gt;&lt;br /&gt;Don't overthink the problem, kids.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;A Better Example&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;I didn't get super motivated to evaluate my cell structure until I saw &lt;a href="http://www.cocoabuilder.com/archive/message/cocoa/2009/6/27/239742"&gt;this post&lt;/a&gt; on the Cocoa-dev mailing list by mmalc Crawford.  I had glossed over it originally, not realizing it was actually an iPhone question.  But sure enough, down under the 'Replicated content' section he provides instructions for using Interface Builder to graphically build cells that contain buttons.  I believe that mmalc is one of Apple's tech writers, and have since found almost the exact same explanation in the "A Closer Look at Table-View Cells" section of the "Table View Programming Guide for iPhone OS".  There is also a relatively new example called TaggedLocations that demonstrates the technique.&lt;br /&gt;&lt;br /&gt;So, just like the first go-around, I laid out the most complicated version first, just to see if I could make the technique work.  Here is what that looks like, with color added so that you can more easily see the different UILabels involved:&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px;" src="http://homepage.mac.com/brianslick/blogpics/slickshopper/cells/ibcell.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;There really isn't too much code going along with this cell.  This is my header file, and the implementation doesn't contain a whole lot more.  It pretty much just synthesizes the accessors, inits, and deallocs.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;@interface CompletePortraitListCell : UITableViewCell&lt;br /&gt;{&lt;br /&gt; UIButton *checkButton;&lt;br /&gt; UILabel *primaryLabel;&lt;br /&gt; UILabel *leftSubLabel;&lt;br /&gt; UILabel *rightSubLabel;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@property (nonatomic, retain) IBOutlet UIButton *checkButton;&lt;br /&gt;@property (nonatomic, retain) IBOutlet UILabel *primaryLabel;&lt;br /&gt;@property (nonatomic, retain) IBOutlet UILabel *leftSubLabel;&lt;br /&gt;@property (nonatomic, retain) IBOutlet UILabel *rightSubLabel;&lt;br /&gt;&lt;br /&gt;@end&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So that's good, but doesn't really help me a whole lot yet.  Don't I need 7 more of these?  As it turns out, no, this is the only file I need, and it can handle all 8 of the remaining variations.&lt;br /&gt;&lt;br /&gt;I should probably take a moment to say that everything I'm about to achieve probably could have been used on my older code-only cell, too.  But I do feel confident in saying that it probably would have been a bit more complicated to do so.  And I can make size and position changes in IB so much more quickly than I can in code.&lt;br /&gt;&lt;br /&gt;We begin with the complete cell as shown before, and let's take a quick look at it running on the phone to make sure it works:&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px;" src="http://homepage.mac.com/brianslick/blogpics/slickshopper/cells/completecell.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;The first option I want to handle is when the user has elected to display only one piece of additional information - quantity OR cost, but not both.  I made the decision early on that in this case, the piece of information would be displayed at the left (green box), regardless of which piece of information it was.  I can redirect the text into either box, so that's not hard.  And it turns out that choosing not to display a box isn't hard, either.  UILabel inherits from UIView, which in turn has a hidden property.  So, after initializing my cell, all I have to do is:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;[[cell rightSubLabel] setHidden:YES];&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This results in:&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px;" src="http://homepage.mac.com/brianslick/blogpics/slickshopper/cells/partialcell1.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;Or, if only cost is being displayed:&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px;" src="http://homepage.mac.com/brianslick/blogpics/slickshopper/cells/partialcell2.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;So if hiding can make things go away, maybe I can do that with the button, too.  And I can:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;[[cell checkButton] setHidden:YES];&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;...which results in:&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px;" src="http://homepage.mac.com/brianslick/blogpics/slickshopper/cells/hiddencheck1.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;So the checkbox is gone, but now the text looks funny dangling out in space.  I need to shift the text over to occupy the space vacated by the checkbox.  This is accomplished by messing with the UILabel's frame.  First, I'll grab the existing frame for the description label:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;CGRect primaryFrame = [[cell primaryLabel] frame];&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Then I'll feed that frame right back into the label, but make an adjustment to the X coordinate:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[[cell primaryLabel] setFrame:CGRectMake(primaryFrame.origin.x - 30,&lt;br /&gt;                                         primaryFrame.origin.y,&lt;br /&gt;                                         primaryFrame.size.width,&lt;br /&gt;                                         primaryFrame.size.height)];&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Let's see what that does for us:&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px;" src="http://homepage.mac.com/brianslick/blogpics/slickshopper/cells/hiddencheck2.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;Well, we did succeed in pulling the label to the left, but the entire label moved.  With short descriptions, the user might never notice the difference, but once they get some longer titles, they'll see their text getting truncated at an odd location.  So at the same time we're moving the box to the left, we need to make the box wider to fill up the space.  So, with a minor correction to the previous code, we now do this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;CGFloat adjustment = 30;&lt;br /&gt;[[cell primaryLabel] setFrame:CGRectMake(primaryFrame.origin.x - adjustment,&lt;br /&gt;                                         primaryFrame.origin.y,&lt;br /&gt;                                         primaryFrame.size.width + adjustment,&lt;br /&gt;                                         primaryFrame.size.height)];&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;...and check our results:&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px;" src="http://homepage.mac.com/brianslick/blogpics/slickshopper/cells/hiddencheck3.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;Bingo.  We can do the exact same thing with the green box.  The blue one doesn't need to move in this case.&lt;br /&gt;&lt;br /&gt;Speaking of moving a text box, that can probably handle the case where prices AND quantities are turned off.  All that needs to happen there is for the description label to move down.  Since we don't want the box to get taller as it moves, all we need to do is increase the Y coordinate:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;CGFloat adjustment = 8;&lt;br /&gt;[[cell primaryLabel] setFrame:CGRectMake(primaryFrame.origin.x,&lt;br /&gt;                                         primaryFrame.origin.y + adjustment,&lt;br /&gt;                                         primaryFrame.size.width,&lt;br /&gt;                                         primaryFrame.size.height)];&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And of course hide the two detail boxes:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;[[cell rightSubLabel] setHidden:YES];&lt;br /&gt;[[cell leftSubLabel] setHidden: YES];&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;...resulting in:&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px;" src="http://homepage.mac.com/brianslick/blogpics/slickshopper/cells/minimalcell.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;At this point, the only differences between selected and not-selected are the checkbox state, and the color of the text.  We'll stay with the black for selected items, so for unselected items we'll change the text color:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;[[cell primaryLabel] setTextColor:[UIColor grayColor]];&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The checkbox is similarly easy.  In Interface Builder, select the button, and then in the Inspector set an appropriate image for "Default State Configuration", and the checked image for "Selected State configuration".  After that it is simply a matter of feeding a boolean in for the state, which in this case is based on a property of my data model:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;[[cell checkButton] setSelected:[currentItem isSelected]];&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So after all of this, we are left with two cases: 1) not-selected item, no checkbox, 2) selected, but all options (checkbox, prices, quantities) turned off.  Technically, our same cell could handle this.  Set the checkbox to hidden, make the appropriate transformation to the frame coordinates, and we're there.  But, this may perhaps be overkill.  After all, we're talking about a cell that contains only a single line of text.  That's a basic UITableViewCell.  No reason to reinvent the wheel for that.  Set the font size and color, done.&lt;br /&gt;&lt;br /&gt;And as an added bonus - and my real reason for messing with my cells in the first place - this all works in landscape, too:&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 482px;" src="http://homepage.mac.com/brianslick/blogpics/slickshopper/cells/landscape.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;There's a little sneak peak at SlickShopper 1.5 for loyal readers.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Yeah, So What?&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;What did I actually accomplish?  The key difference is that I went from 12 custom cell classes to only 1 (plus 1 built-in cell).  That's less code to manage.  And the new custom class is designed in Interface Builder, which means even less code.  Less code is always good (less to break).  And by having all of the important cell design in one place, that means a one-time change can have a wide-ranging impact.&lt;br /&gt;&lt;br /&gt;I'm sure at some point I'll learn about a reason not to have done this, but for now it's working just fine.  The only real weakness I can see at the moment is when I move the labels in code.  Right now the transitions are all relative, so if I move the red box in IB, then the red box will also move in the simplified display.  I'll have to make corrections to the code at that point.  I could hard-code the destination coordinates, but then I'm really putting a wall between the nice stuff I now have in IB and what is happening in my code.&lt;br /&gt;&lt;br /&gt;But for now, I'm happy, proud of myself, and grateful to mmalc.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38820386-9157700109964456820?l=clingingtoideas.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/uTLY5aZbYr5BnlkDw3aMQ9gTgmg/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/uTLY5aZbYr5BnlkDw3aMQ9gTgmg/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/uTLY5aZbYr5BnlkDw3aMQ9gTgmg/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/uTLY5aZbYr5BnlkDw3aMQ9gTgmg/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ClingingToIdeas/~4/qnCyQssfts0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://clingingtoideas.blogspot.com/feeds/9157700109964456820/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=38820386&amp;postID=9157700109964456820" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/9157700109964456820?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/9157700109964456820?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ClingingToIdeas/~3/qnCyQssfts0/rethinking-problem.html" title="Rethinking The Problem" /><author><name>The Slick One</name><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="31" height="32" src="http://1.bp.blogspot.com/_nJq3n1l5xlM/SmYOXU5LAyI/AAAAAAAAABU/-SvDv814nmI/S220/DSC_1796.jpg" /></author><thr:total>1</thr:total><feedburner:origLink>http://clingingtoideas.blogspot.com/2009/07/rethinking-problem.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D08ESXc8eyp7ImA9WxJXGUg.&quot;"><id>tag:blogger.com,1999:blog-38820386.post-777982361994141795</id><published>2009-06-14T01:03:00.001-04:00</published><updated>2009-06-14T01:03:28.973-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-06-14T01:03:28.973-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="personal" /><category scheme="http://www.blogger.com/atom/ns#" term="Apple" /><title>WWDC Recap</title><content type="html">I'm on my way home from WWDC and have a bit of a layover here in Minneapolis/St. Paul, so I thought I'd jot down some passing thoughts.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Let's Get Physical&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;I've identified three categories of physical activities that happened a lot during this conference.  Unfortunately, I only prepared for one of them.&lt;br /&gt;&lt;br /&gt;1.  Walking.  Lots and lots of walking.  This is the one I prepared for.  I've been doing a couple laps around the neighborhood each day for several weeks, and that exercise served me well.&lt;br /&gt;2.  Standing.  Standing in line.  More standing in line.  Even more standing in line.  And yeah, some more standing in line.  Oh, and also some standing around during the social mixers.  And don't forget about standing in line.  Oddly enough, I found that even though the standing really wore on me, as soon as I started walking I was fine.  I don't know exactly how to prepare for this, but I'd suggest simply standing for a 1/2 hour several times a day.  Maybe shuffle forward a couple feet every few minutes.  For bonus practice, hold your laptop bog.&lt;br /&gt;3.  Sitting.  I thought the worst seats on this trip would be on the airplanes.  I was wrong.  On Monday I was thanking and asking for another, on Tuesday I was able to keep to name, rank, and serial number, but by Wednesday I was giving up state secrets and begging for mercy.  In order to prepare for this, I suggest finding a nice concrete block, and bringing along a small sheet of plywood for a backrest.  Sit on this for an hour, four times a day.  You probably still won't be adequately prepared, but at least you'll be more ready to accept the almost total loss of the concept of personal comfort.  Chances are you won't be able to sit on the end of a row, so as you sit practice keeping hold of your laptop without crossing your legs.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Big Brother&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;This is the first conference I've attended where all of the content was provided by the host company.  Every single lecture was given by Apple employees.  I don't think I can adequately articulate how much less useful PLM World and SolidWorks World would have been with exclusively UGS- and SW-provided content.  Certainly, it is nice to get information direct from the horse's mouth, but there is so much valuable knowledge in the user base.  On average, the useful information acquired that can be immediately applied tends to come from user presentations.  This is largely due to the fact that the host company is mostly talking about what's coming up.  The next version of SolidWorks, the next version of NX, etc.; capabilities that aren't even available yet and won't be for months.  Users stand up and talk about what they've been doing for the last year, and provide a little reality to combat the unicorns and roses offered by the company.  I was already overwhelmed by the amount of information that Apple provided, but I can't help but wonder how much more valuable and tangible information I could have acquired from a few decent user presentations.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Brush With Greatness&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;I didn't get to meet everyone that I wanted to, but I did wind up being surprised at the numbers I did encounter.  Several book authors, several of my Twitter followers, and so on.  It's tough to meet people when all you know is a screen name.  Who knows how many times I walked past someone I would have liked to meet, but didn't know that's who they were.  I did see a couple of people I would have liked to talk to, but didn't have a convenient situation to do so, and judging from Twitter there were a significant number people present that I would have loved to meet but never saw.  Maybe next year, on the assumption this whole programming gig lasts long enough to justify a return, I'll have to send out some emails in advance to arrange meetings.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38820386-777982361994141795?l=clingingtoideas.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/Skg9br8LtDIWI0XZ7tKiPDwKXPw/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Skg9br8LtDIWI0XZ7tKiPDwKXPw/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/Skg9br8LtDIWI0XZ7tKiPDwKXPw/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Skg9br8LtDIWI0XZ7tKiPDwKXPw/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ClingingToIdeas/~4/LMLBc1K7N8g" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://clingingtoideas.blogspot.com/feeds/777982361994141795/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=38820386&amp;postID=777982361994141795" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/777982361994141795?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/777982361994141795?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ClingingToIdeas/~3/LMLBc1K7N8g/wwdc-recap.html" title="WWDC Recap" /><author><name>The Slick One</name><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="31" height="32" src="http://1.bp.blogspot.com/_nJq3n1l5xlM/SmYOXU5LAyI/AAAAAAAAABU/-SvDv814nmI/S220/DSC_1796.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://clingingtoideas.blogspot.com/2009/06/wwdc-recap.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUYDQ3s-eyp7ImA9WxJXFkw.&quot;"><id>tag:blogger.com,1999:blog-38820386.post-1300985727730460102</id><published>2009-06-10T02:59:00.001-04:00</published><updated>2009-06-10T02:59:32.553-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-06-10T02:59:32.553-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="random" /><category scheme="http://www.blogger.com/atom/ns#" term="ideas" /><category scheme="http://www.blogger.com/atom/ns#" term="nx" /><category scheme="http://www.blogger.com/atom/ns#" term="ugs" /><category scheme="http://www.blogger.com/atom/ns#" term="solidworks" /><category scheme="http://www.blogger.com/atom/ns#" term="Apple" /><title>Conference Comparison</title><content type="html">This week I'm in San Francisco attending Apple's World Wide Developer Conference (WWDC).  This represents a number of firsts for me.  First time attending an Apple conference of any kind, first time attending a non-CAD (well, week-long) conference, might even be my first visit to San Francisco (don't remember).&lt;br /&gt;&lt;br /&gt;It is only natural that I draw comparisons to my prior conference exposure.  I attended PLM World for several years, and last year attended SolidWorks world for the first time.  I don't remember if I blogged about it, but I believed at the time that SWW was hands-down the best conference experience I had witnessed.  Can Apple trump that?  In short, no, but they come pretty close.  So let's take a look at some aspects that have jumped to my attention.&lt;br /&gt;&lt;br /&gt;It should be noted that I have missed the last 2 PLMW conferences, so some of my information may be out of date, and my complaints may have been addressed in the meantime.&lt;br /&gt;&lt;br /&gt;Planning Ability&lt;br /&gt;&lt;br /&gt;PLMW and SWW both announce their conference dates for the following year at each conference, and sometimes even 2 years in advance.  Although everyone knows there will be a WWDC each year, Apple announced the early-June dates in late-March, barely 2 months of warning.  That's no good.  There's no reason to be so secretive about the dates.&lt;br /&gt;&lt;br /&gt;Winner: PLMW/SWW&lt;br /&gt;Loser: WWDC&lt;br /&gt;&lt;br /&gt;Preparing Ability&lt;br /&gt;&lt;br /&gt;I got used to PLMW publishing session schedules well in advance of the conference.  There would often be revisions, but the bulk of the schedule was made public early on.  Either SWW's site really sucked, or I am an idiot, or blind, whatever, but I didn't find session schedules until almost immediately before the event.  In fact, I think it was my wife who pointed out to me where it was on the site, and I swear I didn't see it before then.  So I'm willing to acknowledge it could have been my oversight, but it left a bad taste in my mouth either way.  Apple only released a session list about a week or so before the event.  So that's bad.  BUT, they did provide the ability to broadcast a calendar subscription, and indicate on their web site which sessions I would want to attend.  This updated directly into iCal, so I was quickly able to see that there were far too many interesting sessions for 1 human being to attend.  And about 2 days before the event, they made an iPhone app available that hooked into the same scheduler, and also provides maps and things.&lt;br /&gt;&lt;br /&gt;Winner: Apple for capabilities, PLMW for timeline&lt;br /&gt;Loser: SWW, but might have been my fault&lt;br /&gt;&lt;br /&gt;Registration and Schwag&lt;br /&gt;&lt;br /&gt;The day before things kick off, you go to the registration tables to get your various badges and things.  Registration is registration, so I can't say that there is anything particularly noteworthy about any of them, so let's focus on schwag.&lt;br /&gt;&lt;br /&gt;PLM World started off pretty well for me, then quickly went downhill.  The very first conference I attended, they handed out Palm Pilots to all attendees.  The next year was a memory stick.  The next, a shirt.  The next, either another shirt, or nothing, I don't remember.  Meanwhile, they found a sponsor to hand out a horribly crappy bag.  Think of a re-usable canvas grocery bag, only much, much, much cheaper.  That became a standard for several years, and they mostly handed it to you to keep track of all of the sponsor ads and other miscellaneous crap they also handed out.  The bag was really only useful for carrying this stuff back to the hotel room, never to be touched again.&lt;br /&gt;&lt;br /&gt;SWW was a shirt and a backpack.  I still wear the shirt periodically to this day, and would be using the backpack if I didn't already have a great one.  It is a pretty decent backpack.&lt;br /&gt;&lt;br /&gt;Apple is a shirt and a backpack.  It's more of a stylish backpack than a particularly useful one (cue Apple haters...).  Shirt's not bad.&lt;br /&gt;&lt;br /&gt;I should point out that I am actually carrying around a shoulder laptop bag that I got from PLM World.  But, this was one of the gifts I received for being a presenter, and not part of the general handout.  It's an odd bag, and has proven to be surprisingly useful at events like this, but I'm not going to count it since it is a special case.&lt;br /&gt;&lt;br /&gt;Winner: SWW&lt;br /&gt;Loser: Recent PLMW&lt;br /&gt;&lt;br /&gt;Identification&lt;br /&gt;&lt;br /&gt;PLMW has long gone with the fairly standard huge identification lanyard.  Ugly, but always functional.  Large, easy to read letters so you could catch the names of people without too much trouble, handy little slots for business cards and pens.  No complaints.  SWW took a slightly different route, with a clear plastic holder.  But they added two particularly useful innovations.  First, attached to my name card was a printout of the sessions I had indicated I was interested in during registration.  THAT was handy.  Second, they managed to cram the entire conference agenda into a small booklet that slid into a slot on the back of the ID holder.  This is noteworthy because PLMW's conference agenda is a freaking Sears catalog.  Apple has gone with a plastic card about the size of a credit card.  The text is rather small, and the card has a gift for rotating around so the wrong side is facing out, so you can't see names anyway.  About the only thing positive I can say about it is that it's easy to stuff in a pocket when leaving the conference center so that you aren't displaying your "please rob me, I'm from out of town" bullseye.&lt;br /&gt;&lt;br /&gt;Winner: Tie between PLMW and SWW, slight edge to SWW&lt;br /&gt;Loser: WWDC&lt;br /&gt;&lt;br /&gt;Innovations&lt;br /&gt;&lt;br /&gt;Again, PLMW got off to a great start.  They handed out the Palm Pilots because they included conference software that listed the sessions and locations.  They never handed out such devices again, and I don't believe they offered the conference software again (I have missed the last two, so they may have updated in the meantime).  SWW didn't really go for technology in this area, but their conference agenda was very clever, and I don't mean the small one that was in the ID pouch.  They produced a spiral-bound version that was roughly half as thick as PLMW's catalog, despite having significantly more content, AND the back half of that contained blank engineering paper for doodling or taking notes.  You almost didn't need to carry anything else around with you.  I'm not going to give Apple too much credit for having an iPhone app; after all, it's a developer conference, with particular focus on the iPhone.  It would be more significant if there wasn't an iPhone app.  But I'm still going to give credit for providing the calendar subscription.  It's a nice touch.&lt;br /&gt;&lt;br /&gt;Winner: Each, for different reasons&lt;br /&gt;Loser: Recent PLMW&lt;br /&gt;&lt;br /&gt;Keynotes&lt;br /&gt;&lt;br /&gt;PLMW keynotes were spectacularly dull and uninteresting.  I cannot think of a single positive thing to say about any of them.  I don't think they understood their audience.  The presentations were always geared towards my boss, or even my boss's boss, but neither of them were at the conference.  I was.  The keynote did not once speak to me.  SWW was like a breath of fresh air.  The keynote was interesting, entertaining, and relevant to me.  Not my boss, not my VP, me.  It was a highly charged, highly motivating experience.  How does that compare to one of Apple's legendary keynotes?  I didn't actually care to wait in line to see the keynote (not sure if I would have felt differently if el-Steve-o was presenting), I just wanted breakfast.  But you had to wait in line to get inside to get to the conference food, so I bailed and went to Starbucks.  The line was already several blocks long, and by the time I came back was nearly wrapped all the way around the conference center.  I believe the line came full circle by the time they started letting people in.  As is, I was in line for roughly 45 minutes, and got shuffled into an overflow room 15 minutes after the keynote had started.  Was it worth it?  Meh.  Oh, don't get me wrong, they announced some cool stuff.  But I really didn't leave with a sense of "Oh wow, I want to go write some code!"  I totally wanted to go design something after the SWW keynote.  It's not the same audience, not really even the same purpose, but I'm afraid to say that SWW out-keynoted Apple.&lt;br /&gt;&lt;br /&gt;Winner: SWW&lt;br /&gt;Loser: PLMW&lt;br /&gt;&lt;br /&gt;Meals&lt;br /&gt;&lt;br /&gt;PLMW has always had decent catering.  The crowd is shuffled into a large room with plenty of tables, all pre-stocked with silverware, condiments, and usually a choice of water or tea.  (Hopefully) soon after being seated, food would be served, and an eager wait staff would whisk finished plates away.  The food was usually fancy, but not really my thing.  I won't say it was bad, just that I would have preferred a bit more of a common menu.  As I type this, I can't remember what SWW meals were like, so it must not have been noteworthy in either direction.  WWDC's meals are pre-packaged.  They seem to offer 3 choices - yesterday was ham &amp; cheese sandwich, chicken wrap, or vegetarian - and you move pretty quickly through a line to get what you want.  Food quality isn't bad, though it is significantly lower quality than PLMW.  But you can grab and go.  Generally there are better things to do at a conference like this than to wait on your food to be served, so I think that's an advantage.  &lt;br /&gt;&lt;br /&gt;Winner: WWDC for speed, PLMW for quality&lt;br /&gt;Loser: PLMW often had long food delays&lt;br /&gt;&lt;br /&gt;Snacks-n-Things&lt;br /&gt;&lt;br /&gt;PLMW did a pretty good job of leaving out water bottles on a regular basis, and there would be sweets in the afternoon.  They typically did a pretty good job of covering a wide area in the conference center, so you wouldn't miss out on a cookie just because you were on the wrong floor.  SWW stepped this up a notch, providing custom-labeled water bottles constantly, and if anything the snacks were even better than PLMW's.  I kept reading tips for WWDC recommending that you grab the water bottles from your hotel room ($3 for a modest bottle).  Surely this is unnecessary, I thought.  They have to provide water bottles.&lt;br /&gt;&lt;br /&gt;They don't provide water bottles!  Apparently if you know where to look, you can find some office-style water jugs, with paper cups.  During meals and during snacks, they basically wheel out these barrels filled with ice and a couple flavors of canned drinks, and I've seen some varieties of juices in plastic bottles.  But not much water.  And let's talk quantities.  Maybe I was just in the wrong place at the wrong time, but when I wandered out for a snack, there was exactly one table available with cookies and M&amp;Ms.  One table.  This is the largest conference I've attended.  One table.  Twice the size of PLMW, edging out SWW by a bit.  One table.  And of course, hungry software developers lacking in people skills descended upon this table like a pack of wild dogs.  I've never seen so much interest in M&amp;Ms, and I struggle to think of the last time I saw such a poorly organized means of delivering food.  The CAD conferences typically naturally form lines and efficiently retrieve food.  This was almost scary.&lt;br /&gt;&lt;br /&gt;Winner: SWW, CAD users&lt;br /&gt;Loser: WWDC, software developers&lt;br /&gt;&lt;br /&gt;Signage&lt;br /&gt;&lt;br /&gt;For as long as I can remember, PLMW has used custom-printed foam board propped up on easels for session topic signs.  I was amazed when I attended SWW, and they had big LCD screens outside of each room indicating the next session subject.  Apple has, of course, stepped that up with a level of style.&lt;br /&gt;&lt;br /&gt;Winner: WWDC&lt;br /&gt;Loser: PLMW&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Granted, I'm not even half way through WWDC yet, but based upon initial impressions, I'd have to say that SolidWorks World will hold onto its coveted title of "Most Impressive Conference Brian Has Attended".  Seemingly everyone involved genuinely cared that the attendees had a great - not merely good, or acceptable, but great - time, and SolidWorks was willing to throw around some serious dollars.  PLM World has been hamstrung with budget issues, no doubt in part to being a separate entity, and being roughly half (in terms of number of attendees) the size of the other two.  WWDC is falling somewhere in between, although definitely closer to the SWW end of the spectrum.  Apple is throwing some money around, but I'm not certain that it is winding up in the right places, and I'm not getting quite the same level of enthusiasm.  Maybe it is social differences - although engineers and programmers aren't too far apart on the introverted nerd scale - maybe it is the always-present Non Disclosure Agreement hanging over everything here.  Not sure, but SWW got "it", whatever "it" was.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38820386-1300985727730460102?l=clingingtoideas.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/LE-57eMwoVvVoAsbo3ng_nLoFKY/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/LE-57eMwoVvVoAsbo3ng_nLoFKY/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/LE-57eMwoVvVoAsbo3ng_nLoFKY/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/LE-57eMwoVvVoAsbo3ng_nLoFKY/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ClingingToIdeas/~4/SB50xZBrK4Q" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://clingingtoideas.blogspot.com/feeds/1300985727730460102/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=38820386&amp;postID=1300985727730460102" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/1300985727730460102?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/1300985727730460102?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ClingingToIdeas/~3/SB50xZBrK4Q/conference-comparison.html" title="Conference Comparison" /><author><name>The Slick One</name><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="31" height="32" src="http://1.bp.blogspot.com/_nJq3n1l5xlM/SmYOXU5LAyI/AAAAAAAAABU/-SvDv814nmI/S220/DSC_1796.jpg" /></author><thr:total>1</thr:total><feedburner:origLink>http://clingingtoideas.blogspot.com/2009/06/conference-comparison.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUAFRXw-eip7ImA9WxJQGEo.&quot;"><id>tag:blogger.com,1999:blog-38820386.post-8023357820426387605</id><published>2009-06-01T13:35:00.001-04:00</published><updated>2009-06-01T13:35:14.252-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-06-01T13:35:14.252-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="personal" /><title>It Has Begun</title><content type="html">So, Brian... what have you been up to lately?  Quite a bit, thanks for asking.&lt;br /&gt;&lt;br /&gt;On November 17, 2008, I placed an order for &lt;a href="http://www.amazon.com/gp/product/1430216263/ref=ox_ya_oh_product"&gt;Beginning iPhone Development: Exploring the iPhone SDK&lt;/a&gt;, by Jeff LaMarche and Dave Mark.  I had no way of knowing that just a few days later, I would lose my job as a Mechanical Engineer.  In pretty much every way except financially, this has turned out to be one of the best series of events in my life.  Rather than moping around about losing my job (did enough of that the last time I lost my job), I threw myself into the book, determined to make myself into an iPhone programmer.&lt;br /&gt;&lt;br /&gt;I am not a total beginner, but not too far from it.  Roughly two years ago, I attended a programming class at &lt;a href="http://www.bignerdranch.com/"&gt;Big Nerd Ranch&lt;/a&gt;, one of the best Mac programming instructional organizations in the world.  I toyed a bit with Mac programming for a little while after that, but for various reasons didn't keep much momentum.  When Apple made it possible for anybody to create programs for the iPhone, I jumped at the chance to dive back in to programming.  Unfortunately, I am still a beginner, and Apple was still new at supporting this particular ecosystem.  The examples and documentation were hard to understand, tutorials non-existent, and the available tools didn't work as well as their Mac OS counterparts.  As Apple continued to release updates to the developer tools, I downloaded each one hoping that things would finally get to a point where I could figure them out, but it just wasn't working.&lt;br /&gt;&lt;br /&gt;Thank you, Jeff and Dave.&lt;br /&gt;&lt;br /&gt;At last, a well-written book appeared that would pave the way for what hopefully turns out to be my new career.  I spent the next several weeks patiently and diligently working through the examples in the book.  Somewhere around Chapter 9, I decided that I probably knew enough basics to start working on my own program.  I started putting together small sample programs, testing different functions that would be components of an application that I wanted to make, and proved to myself that I could make it happen.  Looking back at my project history, somewhere around mid-February is when I started to create my real program.&lt;br /&gt;&lt;br /&gt;I'm planning to do a separate blog post detailing the creation of my program, so for now let's just say that my initial estimate of 6-8 weeks to completion was, um, inaccurate.  I'm sure that someone who knew what they were doing could have completed the program in half that time.  I slammed into speed bumps and brick walls constantly, rewrote the entire thing from scratch at least 3 times, and spent many a late night trying to squash bugs.  I have learned that I am unable to get to sleep if my program crashes.  My learning curve has been more of a jagged, rocky, and steep cliff face.&lt;br /&gt;&lt;br /&gt;On May 28, 2009, Apple approved my program - &lt;a href="http://www.briterideas.com/BriTer_Ideas/SlickShopper.html"&gt;SlickShopper&lt;/a&gt; - for sale on the App Store.  I was really hoping to get it onto the store in March.  And then I was really hoping to get it on the store in April.  And then I was hoping I would just finish the darned thing.  I finally bit the bullet and submitted the app into the approval process, and then nervously waited for what turned out to be 7 business days, meanwhile watching a competitor conduct a major advertising campaign.  But it finally happened.  My program exists.  It is available for purchase.  People who aren't friends or family have bought it.&lt;br /&gt;&lt;br /&gt;I am an iPhone developer.&lt;br /&gt;&lt;br /&gt;This is by no means the end of the story.  For one thing, SlickShopper only sells for a dollar, so it's going to require a truckload of unit sales to provide useful income.  I'm not expecting that to happen.  For another, my wife will be losing her job in just a few weeks, which will drastically cut down on the amount of time I can do this iPhone thing before I have to go bring in real money elsewhere.  And SlickShopper isn't done.  There are several features I still want to add, and the main reason they aren't there yet is that I just plain don't know how to do them.  So I have some learning to do.  This doesn't take into account what my paying customers might want the program to do that I haven't even thought of yet.&lt;br /&gt;&lt;br /&gt;Step 1 was to ship software.  Now that I've made that happen, I can figure out what step 2 will be.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38820386-8023357820426387605?l=clingingtoideas.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/fd7yVWjzyS7D-MxePkSrt2K1AB8/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/fd7yVWjzyS7D-MxePkSrt2K1AB8/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/fd7yVWjzyS7D-MxePkSrt2K1AB8/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/fd7yVWjzyS7D-MxePkSrt2K1AB8/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ClingingToIdeas/~4/Qh03JwhifX0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://clingingtoideas.blogspot.com/feeds/8023357820426387605/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=38820386&amp;postID=8023357820426387605" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/8023357820426387605?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/8023357820426387605?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ClingingToIdeas/~3/Qh03JwhifX0/it-has-begun.html" title="It Has Begun" /><author><name>The Slick One</name><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="31" height="32" src="http://1.bp.blogspot.com/_nJq3n1l5xlM/SmYOXU5LAyI/AAAAAAAAABU/-SvDv814nmI/S220/DSC_1796.jpg" /></author><thr:total>1</thr:total><feedburner:origLink>http://clingingtoideas.blogspot.com/2009/06/it-has-begun.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkMDSX89fyp7ImA9WxVVF04.&quot;"><id>tag:blogger.com,1999:blog-38820386.post-1240836922045898322</id><published>2009-03-10T21:21:00.001-04:00</published><updated>2009-03-10T21:21:18.167-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-03-10T21:21:18.167-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="personal" /><title>What Was That Noise?</title><content type="html">What noise?  Oh, that?  &lt;em&gt;That&lt;/em&gt; was the sound of the other shoe dropping.&lt;br /&gt;&lt;br /&gt;Loyal readers (both of you!) know that I have have been unemployed since late November.  I'll talk more about what I've been doing in the meantime at a later date, but basically I haven't been trying all that hard to find another job.  That may need to change sooner than I had hoped.&lt;br /&gt;&lt;br /&gt;Teri was informed on Friday that her position will not be renewed this summer.  It's not a 100% done deal, but close enough that there's no real point in hoping that she'll keep her job.  She will finish up at the end of June.  I suppose we could view this as she is getting nearly 4 months severance, but she still has to work for it.  Heh.&lt;br /&gt;&lt;br /&gt;Although it's always shocking to learn of job loss, it wasn't a complete surprise.  The university, like everyone else, has been facing budgetary issues.  Also, in response to being questioned about job security, her boss hinted a few weeks ago that she should keep her eyes and ears open.  That's what we in the business call "a sign".&lt;br /&gt;&lt;br /&gt;This will be the first time Teri has lost her job, and it's just a great big happy freaking coincidence that it happens when I am also jobless.  So this drastically reduces the amount of time that our savings will last.  With Teri employed, our pile 'o cash can go for roughly 3 years.  With no income, that drops to about 9-10 months, or roughly a year from today.  So we still have time to find jobs, but a lot less of it.&lt;br /&gt;&lt;br /&gt;Semi-related side note: If you have a nice, perfectly functioning - and paid off - automobile, and are thinking about selling it, buying something cheaper, and using the difference to pay off credit cards, DON'T DO IT!  Nothing sucks like have to pay for repairs on a POS car when you don't have a job.  Reliable transportation is very important.  Also, if you think that used cars devalue slower than new cars, um, no.  My POS car is worth roughly half of what I paid for it not even 9 months ago.  Did I mention repair costs?&lt;br /&gt;&lt;br /&gt;Anyway, Teri has spent the last few days applying for other jobs.  Basically none of them are local.  So chances are, our lives are about to change.  Once we are both jobless, aside from a couple of friends, the only thing keeping us here is the house.  And as one of those friends said: "Gotta eat!"  Damn straight.  So the house will most likely go up for sale and we'll move.  No idea where or when yet.  The good news is the pretty much everything Teri is looking at comes with a decent pay increase.  How the cost of living will increase is yet to be determined.&lt;br /&gt;&lt;br /&gt;I've started waking up my headhunter network, so I'll probably go off and do some contract jobs in the meantime to bring in some money (so I can buy a better car!).  Depending on the timing of when Teri finds a job and where it is, or when I do, we may be spending a fair amount of time apart this year.&lt;br /&gt;&lt;br /&gt;As I was typing this, it occurred to me that I've been in Cincinnati for almost 11 years.  This puts it 2nd on my list of "places I've lived the longest" (cities, not specific addresses).  I was moved away from Anderson when I was 12, so if we're still here next year, I'll tie that record.&lt;br /&gt;&lt;br /&gt;Go, economy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38820386-1240836922045898322?l=clingingtoideas.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/DU9SSXspOSJ-Pl7UX37CAHlY8sY/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/DU9SSXspOSJ-Pl7UX37CAHlY8sY/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/DU9SSXspOSJ-Pl7UX37CAHlY8sY/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/DU9SSXspOSJ-Pl7UX37CAHlY8sY/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ClingingToIdeas/~4/iNUPe9D5hXk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://clingingtoideas.blogspot.com/feeds/1240836922045898322/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=38820386&amp;postID=1240836922045898322" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/1240836922045898322?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/1240836922045898322?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ClingingToIdeas/~3/iNUPe9D5hXk/what-was-that-noise.html" title="What Was That Noise?" /><author><name>The Slick One</name><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="31" height="32" src="http://1.bp.blogspot.com/_nJq3n1l5xlM/SmYOXU5LAyI/AAAAAAAAABU/-SvDv814nmI/S220/DSC_1796.jpg" /></author><thr:total>1</thr:total><feedburner:origLink>http://clingingtoideas.blogspot.com/2009/03/what-was-that-noise.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ck8HSXw6fip7ImA9WxVWFEw.&quot;"><id>tag:blogger.com,1999:blog-38820386.post-2952271596040396216</id><published>2009-02-23T12:40:00.001-05:00</published><updated>2009-02-23T12:40:38.216-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-02-23T12:40:38.216-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="personal" /><title>The (Official) End of an Era</title><content type="html">Although I lost my job back in November, the severance package worked out to be 3 month's pay.  Rather than giving me that money in a single lump sum, the company elected to simply continue paying me for 3 more months.  I deposited the final check yesterday.&lt;br /&gt;&lt;br /&gt;Up till now, I've basically had an extended paid vacation.  Now, I lose money.  Well, technically, March 20 is the first date that I will have failed to receive a paycheck, so that is when I will really feel the difference.&lt;br /&gt;&lt;br /&gt;I'll be honest; when I first lost my job, I thought I would have a new one by now.  But the reality is that I just haven't found anything that interesting, and certainly not enough to warrant a lower salary than I previously had.  I had a flurry of phone calls from recruiters in January, and even did a couple of phone interviews, but February has been pretty quiet.  Few of the opportunities have been local, and most of the out-of-area opportunities have been in places with ridiculously high costs of living.  I'm a long, long way from needing a job for the sake of bringing in a paycheck, so I'm not feel overly motivated to go do something that I don't find interesting.  I don't want another job, I want a career.  Only one single opportunity presented to me has represented a potential career, and due to the economy I won't find out if that opportunity still has life until April.&lt;br /&gt;&lt;br /&gt;So I have mostly given up on the job search for the time being.  That's not exclusively my decision, of course, as I haven't been getting too many recruiter phone calls lately.  Instead, I've decided to focus my attention in a different direction.&lt;br /&gt;&lt;br /&gt;Shortly after losing my job, I purchased an iPhone programming book, and have spent the time since trying to become an iPhone programmer.  The learning curve has been brutal, and progress has been slow, but nonetheless I am making progress on my first iPhone app.  I figure I'm another week or two away from having all of the raw functions that I want, then I will need to spend some time refining the interface.  I have recently applied for the Apple iPhone developer program, which will allow me to conduct beta testing on phones, and eventually allow me to post my app on the store.  If all goes well (and so far, it hasn't), I hope to have my first app posted by the end of March.&lt;br /&gt;&lt;br /&gt;Assuming I do eventually get the app onto the store, I will then watch what happens for a few months.  If it looks like I can bring in some money this way, then I may just give up on engineering altogether and focus on programming.  Maybe I can make my own new career, rather than hoping someone else will provide me a good opportunity.  I have plenty of time to find out.  Wish me luck.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38820386-2952271596040396216?l=clingingtoideas.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/RTCI2NnkXnSpokZ_MlizN48aKEI/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/RTCI2NnkXnSpokZ_MlizN48aKEI/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/RTCI2NnkXnSpokZ_MlizN48aKEI/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/RTCI2NnkXnSpokZ_MlizN48aKEI/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ClingingToIdeas/~4/St1JJnlr6QY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://clingingtoideas.blogspot.com/feeds/2952271596040396216/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=38820386&amp;postID=2952271596040396216" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/2952271596040396216?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/2952271596040396216?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ClingingToIdeas/~3/St1JJnlr6QY/official-end-of-era.html" title="The (Official) End of an Era" /><author><name>The Slick One</name><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="31" height="32" src="http://1.bp.blogspot.com/_nJq3n1l5xlM/SmYOXU5LAyI/AAAAAAAAABU/-SvDv814nmI/S220/DSC_1796.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://clingingtoideas.blogspot.com/2009/02/official-end-of-era.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CU4NSHk-eSp7ImA9WxVQEEw.&quot;"><id>tag:blogger.com,1999:blog-38820386.post-1925159428599944549</id><published>2009-01-26T18:29:00.002-05:00</published><updated>2009-01-26T18:53:19.751-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-01-26T18:53:19.751-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="ideas" /><category scheme="http://www.blogger.com/atom/ns#" term="around the web" /><category scheme="http://www.blogger.com/atom/ns#" term="solidworks" /><title>The Poorly Argued Point</title><content type="html">Hey, this might be my first ever rebuttal-to-another-blog post.  A post by &lt;a href="http://p-hamilton.blogspot.com/"&gt;Paul Hamilton&lt;/a&gt; showed up in my feed reader.  I don't know who he is or why I'm watching his blog, but his profile indicates he is CAD-related, so I must have read a previous posting and subscribed.&lt;br /&gt;&lt;br /&gt;Today Paul &lt;a href="http://p-hamilton.blogspot.com/2009/01/reducing-costs-with-explicit-history.html"&gt;argues&lt;/a&gt; that costs can be saved with history-free CAD modeling.  This &lt;em&gt;might&lt;/em&gt; ultimately be a true sentiment, but Paul argues it badly.  I see that a previous post of his argues the benefits of history-based modeling, so maybe he is just playing devil's advocate this time around, which would explain the weakness of some of his points.  &lt;br /&gt;&lt;br /&gt;The first set of ten points addresses improvements in effeciency.  Let's take a closer look.&lt;br /&gt;&lt;br /&gt;&lt;cite&gt;&lt;em&gt;1.  Lower your training costs with history-free modeling.  There is simply less to learn when there is no history tree to create and manage.  Intuitively interact with the geometry, directly.&lt;/em&gt;&lt;/cite&gt;&lt;br /&gt;&lt;br /&gt;Speaking as a former CAD trainer, I suppose there is some slight merit to this point, but it certainly isn't significant.  For an introductory class with 20 chapters, one (maybe two) was spent on history, so for a five day class we are talking about a couple hours, tops.  Significant?  Hardly.  Would the omission of this topic enable dropping to, say, four days?  No.&lt;br /&gt;&lt;br /&gt;With CAD, you have to know how to create stuff, and you have to know how to modify stuff.  I haven't seen anything in the history-free world that suggests the creation tools are really significantly different and/or easier.  Call that a wash.  So how about the modification tools?  Well, you have to know what you are doing in either case.  Even within the realm of a history-free modeler, there will be rules and nuances that will still need to be trained or otherwise learned with experience.&lt;br /&gt;&lt;br /&gt;&lt;cite&gt;&lt;em&gt;2. Get the right people engaged at the right time.  Eliminate CAD knowledge from the criteria for assignments and resource management.&lt;/em&gt;&lt;/cite&gt;&lt;br /&gt;&lt;br /&gt;So, history-free modeling is so easy that anyone could do it, eh?  Right.  This ignores the knowledge required for CAD model creation.&lt;br /&gt;&lt;br /&gt;3D modeling is a mind set much more than a skill set.  It's an ability to visualize the desired item so that you can sit down and make it happen on the computer.  If you can do that, then the tools are just a matter of training.  If you can't, it doesn't matter how easy the software is.  I have encountered everything from the newbie with zero CAD experience to grizzled old veterans, and not once has the ability to interpret a history tree been the deciding factor between "gets it" and "doesn't get it".&lt;br /&gt;&lt;br /&gt;&lt;cite&gt;&lt;em&gt;3. Hire the best designers and engineers, not just CAD jockeys.  Eliminate CAD knowledge from the criteria for hiring/staffing.&lt;/em&gt;&lt;/cite&gt;&lt;br /&gt;&lt;br /&gt;I haven't encountered too many quality designers or engineers who were not highly skilled with computers and the appropriate tools for the job, which should include CAD.  Indeed, at my previous company, most of our best engineers were hired without experience in the specific CAD program (which is history-based), but they did have decent CAD backgrounds, so training wasn't that big of a deal.&lt;br /&gt;&lt;br /&gt;There is nothing, absolutely nothing, about history-free modeling that equates to "nothing to learn", so the notion that you can remove an item from your desired skills hiring checklist is ludicrous.&lt;br /&gt;&lt;br /&gt;Besides, those guys that are "the best" are going to want to be paid accordingly, so how does this help to reduce costs?  This point would make more sense if it argued in favor of hiring lower-caliber people, since you supposedly wouldn't be paying for CAD experience.&lt;br /&gt;&lt;br /&gt;&lt;cite&gt;&lt;em&gt;4. Enhance the concept design process with flexible 3D modeling.  Concept design and history-based modeling are like “oil &amp; water”; they don’t go together very well.&lt;/em&gt;&lt;/cite&gt;&lt;br /&gt;&lt;br /&gt;Since this is about concept design, naturally we are talking about model creation, so refer back to my previous point about the creation tools.  As the modification process goes forward, it is really going to come down to the specifics of what you are doing and what needs to be changed; that it is hard to generalize in favor of either direction.  And really, this point is more about design intent than history modeling.  If you put in a lot of design intent up front, then you are going to be frustrated when a necessary change doesn't jive with that intent.  I'm forced to assume that even history-free modeling possesses some means of capturing design intent - otherwise it would be nearly useless - so this is still a wall you can smack against.  Some changes may be easier to implement in a history-free environment, but I can find just as many cases that would be easier with history.  This simply isn't a good point to generalize about, because it is so case-by-case.&lt;br /&gt;&lt;br /&gt;&lt;cite&gt;&lt;em&gt;5. Improve productivity for each individual by focusing on design, rather than model creation methods, technique and process.  Reduce costs by focusing all effort on product design rather than 3D modeling.&lt;/em&gt;&lt;/cite&gt;&lt;br /&gt;&lt;br /&gt;There aren't creation methods, techniques, and processes in history-free modeling?  All I have to do is think it and the model appears on my screen?  Incidentally, I think that last sentence is a sales slogan for many history-based CAD programs.&lt;br /&gt;&lt;br /&gt;CAD is all about shapes, obviously.  So in the design process, the quality CAD user is thinking to himself "how do I make that shape in this software?"  That thought process still has to exist even in a history-free environment.  There are still steps to take to create the shape.  Maybe one CAD program has a handy command that does what you want in one shot, maybe another CAD program requires two or three steps to accomplish the same thing.  History or not, you still have to know what you are doing, and how to make it happen.&lt;br /&gt;&lt;br /&gt;&lt;cite&gt;&lt;em&gt;6. Repurpose existing data easily with no need to understand model history.  Optimize parts once and reuse to the max.&lt;/em&gt;&lt;/cite&gt;&lt;br /&gt;&lt;br /&gt;Maybe I'm not understanding the point being made here, but how does part reuse correspond in any way to how it was constructed in CAD?  If the 3-spoke widget you designed for Product A might also be used in Product B, then go use it in Product B.  How does history even enter the equation?  Optimization is a function of FEA and testing, not history, so test it against the requirements in Product B and determine if it is appropriate for use.&lt;br /&gt;&lt;br /&gt;This is a data management point more than a CAD modeling point.&lt;br /&gt;&lt;br /&gt;&lt;cite&gt;&lt;em&gt;7. Greatly improve teamwork, team design and interaction with downstream and upstream partners to improve quality, innovation and reduce costs.  By eliminating the history tree, team members can immediately interact with the CAD data.  No need to study the model history.&lt;/em&gt;&lt;/cite&gt;&lt;br /&gt;&lt;br /&gt;This is such a curious sentiment from someone with PLM experience.  The whole bit about teamwork is completely a function of the data management capabilities, and has nothing to do with CAD modeling.  Do you really want your vendor making changes to your model?  Of course not.  But you do want a nice handy way for your vendor to convey those requested changes back to you.  So now we're in the realm of geometry viewers, markup capabilities, and managed permissions.  This has absolutely nothing to do with the CAD creation methodology.&lt;br /&gt;&lt;br /&gt;And again, assuming that even history-free modelers possess design intent capabilities, then you absolutely should study your model before diving right in.&lt;br /&gt;&lt;br /&gt;&lt;cite&gt;&lt;em&gt;8. Get the maximum value from your rich CAD data by making it available, and understandable, to the extended team.&lt;/em&gt;&lt;/cite&gt;&lt;br /&gt;&lt;br /&gt;Why is history-free CAD data rich?  Is an IGES or STEP file rich?  In my world, we call those dumb solids for a reason.  And again, data availability is a function of data management and viewers, and has nothing to do with CAD creation methodology.&lt;br /&gt;&lt;br /&gt;&lt;cite&gt;&lt;em&gt;9. Minimize IT infrastructure load.  Utilizing explicit history-free modeling technology can reduce average file size by 60% to 80%.  Can minimize RAM requirements, storage space requirements and network traffic.  This is made possible by eliminating data space requirements that are typical of the history tree.&lt;/em&gt;&lt;/cite&gt;&lt;br /&gt;&lt;br /&gt;I don't buy this for a second.  With history-free, you are storing all geometry all of the time, and that will always be larger than the instructions to recreate the geometry if necessary.  Which occupies more space, the house or the blueprints?  If you want to say that instructions + geometry &gt; geometry alone, fine, but at least you have the option to strip away the geometry and go with the instructions alone.&lt;br /&gt;&lt;br /&gt;The stated percentages are going to be a function of how much geometry the history-based modeler stores, and this will be a function of each software's implementation.  If the history-based modeler stores only the final geometry, then the resulting file size should be within a tiny percentage of the history-free's model.&lt;br /&gt;&lt;br /&gt;&lt;cite&gt;&lt;em&gt;10. Improved general performance and load/store times for each CAD user by taking advantage of the lightweight footprint of explicit history-free modeling.&lt;/em&gt;&lt;/cite&gt;&lt;br /&gt;&lt;br /&gt;Lightweight models are not unique to history-free modelers.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;So 10 ways to improve efficiency with history-free modelers, of which most having nothing to do with the CAD modeling paradigm, and what's left really doesn't create a convincing argument.  If anything, several of these points are fine arguments for the implementation of a decent data management system, which of course has nothing to do with how the CAD model was created.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Paul then goes on with 10 ways to reduce waste.  He makes better points here, so let's take a look.&lt;br /&gt;&lt;br /&gt;&lt;cite&gt;&lt;em&gt;1. Eliminate the waste of history tree management and structuring.  Focus on the task of design.  And don’t make the incorrect assumption that you cannot capture design intent without a history tree.&lt;/em&gt;&lt;/cite&gt;&lt;br /&gt;&lt;br /&gt;Ok, so he confirms that there is indeed design intent in a history-free modeling paradigm.  This alone kills any implication that there is nothing to worry about in such an environment.  I'm willing to grant that history tree manipulation can be a big deal, but this is also a factor of each software's implementation.  Figuring out the design intent is going to be the key task, and my gut says that this is more easily done with a history tree, but I'm willing to be proven wrong.&lt;br /&gt;&lt;br /&gt;&lt;cite&gt;&lt;em&gt;2. Eliminate the need to rebuild models, something that is too common with history-based tools. (especially if you do concept design with them)&lt;/em&gt;&lt;/cite&gt;&lt;br /&gt;&lt;br /&gt;So, with number 12 we finally land on a completely legitimate point, no question.  About the only thing I'd like to add is that the time spent on rebuilding is highly dependent on software implementation, and hardware capabilities.  For example, SolidWorks smokes I-DEAS on pure rebuild performance, due simply to having a newer, faster algorithm.  On the flip side, I-DEAS provides some methods such that a complete tree doesn't need to be rebuilt (however, there is some of that history tree management involved).  I'm going to guess that on average, if the hardware in question has the necessary guts to do live changes on raw geometry, then it can probably burn through a history rebuild without too much trouble also.&lt;br /&gt;&lt;br /&gt;&lt;cite&gt;&lt;em&gt;3. Eliminate the need to create and manage standards and best practices for creating and managing history trees / history-based models.&lt;/em&gt;&lt;/cite&gt;&lt;br /&gt;&lt;br /&gt;Although I can think of some general best practices off the top of my head - mostly dependent on the software implementation - I can't remember ever feeling burdened by them, nor were they ever enforced.  Start with a coordinate system (manual in I-DEAS, automatically there in SolidWorks), put fillets last, that's about it.&lt;br /&gt;&lt;br /&gt;&lt;cite&gt;&lt;em&gt;4. Add intelligence (features, parameters, …) to models and assemblies only when needed.  History-based systems may force the addition of this intelligence whether it will be used or not.  They will force relationships (parent/child), again whether this added information is actually useful or not.&lt;/em&gt;&lt;/cite&gt;&lt;br /&gt;&lt;br /&gt;Parent/child is about the only legitimate difference between history-based and history-free.  Aside from that, you can do unconstrained modeling in most history-based programs.  "Only when needed" is a comment on design intent, which exists in both paradigms, so I don't see much of a point here.&lt;br /&gt;&lt;br /&gt;Also, the implication that there are no parent/child relationships in history-free modelers is false.  There may be fewer of them, and they may not be as rigid, but they are there.  Some operations, like fillets and draft angles, have to occur after other geometry is created.  You can't fillet something before the geometry is created, and if some of the participating geometry is removed, the fillet can't exist.  That is a parent/child relationship.&lt;br /&gt;&lt;br /&gt;&lt;cite&gt;&lt;em&gt;5. Upward compatibility can be a big issue with history-based modeling and can result in rework and duplication of effort.   There is no compatibility issue when working with geometry; i.e. explicit history-free modeling.&lt;/em&gt;&lt;/cite&gt;&lt;br /&gt;&lt;br /&gt;"No compatibility issue" is certainly a bold statement, and given the various issues I've seen with IGES files over the years, one that I'm willing to believe could be proven false easily.  While it may be true that there is less to break with a history-free model, to suggest it is unbreakable is placing far too much faith in our robotic overlords.  It implies that nothing more needs to be fixed, or no more performance needs to be improved, in the code which is never the case.  Messing with code carries along with it the possibility for breaking with legacy data.&lt;br /&gt;&lt;br /&gt;&lt;cite&gt;&lt;em&gt;6. Greatly reduce the effort of data exchange with suppliers and vendors.  History trees are proprietary.&lt;/em&gt;&lt;/cite&gt;&lt;br /&gt;&lt;br /&gt;Pretty much all of the vendors I've ever worked with wanted either whatever their native software uses, or IGES or STEP files which are easily generated by history-based modelers.  While history trees may be proprietary, so are file formats, so unless these history-free modelers are working with something like IGES as their native format, this is a moot point at best.  Unless someone developed the holy grail of open, widely-recognized and utilized 3D formats when I wasn't paying attention, there is still the potential for translation and compatibility issues, regardless of the modeling paradigm.&lt;br /&gt;&lt;br /&gt;&lt;cite&gt;&lt;em&gt;7. Eliminate the need for other team members to study the history tree of other team member models just to make use of them.&lt;/em&gt;&lt;/cite&gt;&lt;br /&gt;&lt;br /&gt;I'll say it again: you still have to study the design intent.  History is merely an element of the design intent.  I have yet to encounter an environment where design intent is completely unnecessary and completely undesired.&lt;br /&gt;&lt;br /&gt;&lt;cite&gt;&lt;em&gt;8. Designers will spend an estimated 25% of their “CAD time” managing and manipulating the history tree and related attributes and data.  Your designers can be 25% more productive by eliminating this activity.&lt;/em&gt;&lt;/cite&gt;&lt;br /&gt;&lt;br /&gt;This sounds like a made up statistic, because there is zero chance I've spent anywhere close to 25% of my time on history manipulation, but let's take it at face value.  What percentage of &lt;em&gt;that&lt;/em&gt; time is spent on interpreting design intent, an activity that will still need to occur in the history-free environment?  I'd be willing to bet that the difference will be made up in the additional time spent in the history-free environment, where the user does not have the history tree to provide additional information about the design intent.&lt;br /&gt;&lt;br /&gt;&lt;cite&gt;&lt;em&gt;9. Greatly reduce the need to recreate CAD data just to get it into an editable format&lt;/em&gt;&lt;/cite&gt;&lt;br /&gt;&lt;br /&gt;Under what circumstances?  2D -&gt; 3D would be no different.  Changing CAD programs?  The file will still need to be exported and converted (again, if there is some commonly-accepted history-free file format that all history-free modelers are using and can easily exchange, I'm willing to be educated).  After we decide that history actually was kinda nice and we need to go back to it?  Oops, it's all gone, have to start over...&lt;br /&gt;&lt;br /&gt;&lt;cite&gt;&lt;em&gt;10. Reduce the time consumed in the change cycle, but eliminating the need to study the model creation history.  Focus on the process of change.&lt;/em&gt;&lt;/cite&gt;&lt;br /&gt;&lt;br /&gt;How many bullet items need this same point?  And I'll keep repeating mine: you still. have. to. study. the. design. intent. first.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;I can't shake the feeling that Paul really doesn't believe that much about what he wrote, but felt like he needed to make a counterpoint to his previous article.  I'm not sure what his motivation was, but as far as sales pitches go, this was not convincing.&lt;br /&gt;&lt;br /&gt;Incidentally, the gist of each of his points - make CAD available to more people (or everyone), easier to learn, easier to do, focus on design and not CAD - are almost verbatim the same things that the SolidWorks folks say, and SolidWorks is - wait for it - history-based.  Lack of parent/child relationships and lack of rebuilding are about the only legitimate points to differentiate history-free, and the rebuilding one is the only one that is hands-down a weakness of history-based.  Parent/child will depend on the situation.  &lt;br /&gt;&lt;br /&gt;Really though, I see nothing here justifying a history-free approach that would not also be on the order of magnitude of simply switching to a different CAD program.  Performance improvements here, easier to learn there, less expensive over there in the corner, and so on.  In my case going from I-DEAS to SolidWorks, I'm going from old, Unix-based, expensive software that hasn't significantly been enhanced in years, to relatively new and cheap Windows-based (actually, maybe that should be &lt;strong&gt;despite&lt;/strong&gt; being Windows-based...) software that is being rigorously and regularly updated.  There's going to be a huge amount of win there, no question.  That most likely would have also been true had we gone with SolidEdge or Inventor instead.  In fact, nearly every point that Paul made could apply to our transition to SolidWorks.  So why would I want to go with a history-free approach, and give up the things that history does provide?  No thanks.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38820386-1925159428599944549?l=clingingtoideas.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/u_YKap1q93FD22bBK4EOdxLDlOU/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/u_YKap1q93FD22bBK4EOdxLDlOU/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/u_YKap1q93FD22bBK4EOdxLDlOU/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/u_YKap1q93FD22bBK4EOdxLDlOU/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ClingingToIdeas/~4/sFGWgiS4t10" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://clingingtoideas.blogspot.com/feeds/1925159428599944549/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=38820386&amp;postID=1925159428599944549" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/1925159428599944549?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/1925159428599944549?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ClingingToIdeas/~3/sFGWgiS4t10/poorly-argued-point.html" title="The Poorly Argued Point" /><author><name>The Slick One</name><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="31" height="32" src="http://1.bp.blogspot.com/_nJq3n1l5xlM/SmYOXU5LAyI/AAAAAAAAABU/-SvDv814nmI/S220/DSC_1796.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://clingingtoideas.blogspot.com/2009/01/poorly-argued-point.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkQARXc_eSp7ImA9WxVSEkU.&quot;"><id>tag:blogger.com,1999:blog-38820386.post-899815763783598547</id><published>2009-01-06T18:15:00.002-05:00</published><updated>2009-01-06T18:25:44.941-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-01-06T18:25:44.941-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="ideas" /><title>More Macro Tidbits</title><content type="html">If you notice (or care) that the blog looks different, I changed to a template that isn't fixed-width.  The other template kept wrapping code snippets, which I find can be confusing.  One of these days I'll get around to making my own template, but for now this was the least ugly variable-width one that Blogger provides.  If code wraps now, stretch your browser window.  Or get a bigger screen.&lt;br /&gt;&lt;br /&gt;First, I'd like to do my best to answer a question asked about one of my previous posts.  An anonymous person (leave names, people, please!) asked:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;What is hard for me for doing macros is the format? How and when to use () spaces and such.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;This is basically about syntax, so let's focus there.  For the most part, syntax is absolutely critical in pretty much any programming language.  Computers are very good at doing exactly what we ask them to do, but not so good at doing what we really wanted them to do.  The difference is in the syntax.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Syntax Your Brain&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;There are a couple of nuances in the I-DEAS macro language to be aware of.  For one, although you can run a single program that will bounce back and forth between Modeler and Drafting, they actually have separate languages.  The languages are very similar, and understanding one for the most part will help to understand the other, but they are distinct.  I've spent most of my time in the Drafting macro language and found it to be far more forgiving than the Modeler one.  This may be related to the fact that Drafting has excellent documentation and Modeler doesn't.  A quick sample of the difference between the languages:&lt;br&gt;&lt;br /&gt;Drafting:&lt;br /&gt;&lt;blockquote&gt;C: This line is a comment&lt;/blockquote&gt;&lt;br /&gt;Modeler:&lt;br /&gt;&lt;blockquote&gt;C : This line is a comment&lt;/blockquote&gt;&lt;br /&gt;Note the space before the colon in Modeler.  This isn't necessarily a huge deal, although it would be nice if they were the same, but the main problem is that Modeler does not provide useful error messages that help direct you to fix a syntax error.  I've had several occasions in Modeler where it turned out that the real problem was I put in an extra space somewhere; it was a big deal in Modeler, but an equivalent situation in Drafting would have been just fine.  Without access to software, I'm not able to generate any sample error messages, but trust that I've spent some quality time arguing with Modeler about what should and shouldn't be legal.  I usually lose.&lt;br /&gt;&lt;br /&gt;The next interesting thing about the macro commands is that they can provide more than a single response when asked a question.  If you read through the documentation, each command help page will provide a full description of the command that looks something like this:&lt;br /&gt;&lt;blockquote&gt;Read Paper Size ( [Xsize], [Ysize], [Units], [DimStd], [Text] )&lt;/blockquote&gt;&lt;br /&gt;I won't recreate the entire page here, but underneath this command is a description of each parameter.  For example, the [Units] parameter is of type Integer, it is an output variable (as opposed to input), and the description reads "Drawing units, where 0 = inch, 1 = metric" so it is basically a boolean.  Xsize and Ysize should be obvious enough, DimStd refers to the dimensional standard (ANSI, ISO, etc) being used, and Text will report a standard size (A,B,C,D) if applicable, or "EXPLICIT" if the size is custom.  So with a single command, you can retrieve a lot of different information in several different forms.&lt;br /&gt;&lt;br /&gt;First, you define your variables, maybe something like this:&lt;br /&gt;&lt;blockquote&gt;# REAL Width_Of_Sheet&lt;br /&gt;# REAL Height_Of_Sheet&lt;br /&gt;# INTEGER Unit_System&lt;br /&gt;# INTEGER Dims_Std&lt;br /&gt;# STRING Sheet_Size&lt;/blockquote&gt;&lt;br /&gt;(The necessary variable types were in the help document for the command) Then you plug those variables into the command like so:&lt;br /&gt;&lt;blockquote&gt;# Read Paper Size (Width_Of_Sheet, Height_Of_Sheet, Unit_System, Dims_Std, Sheet_Size)&lt;/blockquote&gt;&lt;br /&gt;First of all, note that the [] used in the help document are not actually used in the macro.  It's basically placeholder notation in the help files.  Running this single command will populate all five of these variables with information.  To verify this, you could do the following:&lt;br /&gt;&lt;blockquote&gt;# Show Message ("Width_Of_Sheet is: " + Width_Of_Sheet)&lt;/blockquote&gt;&lt;br /&gt;If it is blank, something went wrong, otherwise you should see the width of your sheet.  You could add four more message commands, one for each variable.  I actually recommend doing so as a troubleshooting tip.&lt;br /&gt;&lt;br /&gt;But what if you don't need all of these values?  Maybe you are only interested in the height of the page.  You can do that, but the kicker is you still have to provide the commas.  This is so that I-DEAS will know which variable you actually want to populate.  Your new command would look like this:&lt;br /&gt;&lt;blockquote&gt;# Read Paper Size (, Height_Of_Sheet,,,)&lt;/blockquote&gt;&lt;br /&gt;I-DEAS now knows that you would like to get information supplied to the second parameter in the command, which happens to be for the height of the sheet.  If that looks odd, basically what you are doing is this:&lt;br /&gt;&lt;blockquote&gt;# Read Paper Size (&lt;strike&gt;Width_Of_Sheet&lt;/strike&gt;, Height_Of_Sheet, &lt;strike&gt;Unit_System&lt;/strike&gt;, &lt;strike&gt;Dims_Std&lt;/strike&gt;, &lt;strike&gt;Sheet_Size&lt;/strike&gt;)&lt;/blockquote&gt;&lt;br /&gt;So if you are ever handed someone else's code, and see all of those crazy commas, what's happening is that the original author did not wish to take advantage of everything the command had to offer.  In case the variable names aren't useful enough on their own, you'll want to visit the help document for the given command to research which parameters are being used or ignored.&lt;br /&gt;&lt;br /&gt;In some cases, if possible, it may be worth the effort to work with a simpler command.  In this particular case, since what I want is the size of the drawing, I could also use this command:&lt;br /&gt;&lt;blockquote&gt;Inquire Drawing Information ([dname], [sizex], [sizey], [partNumber], [version], [revision], [libStatus], [writepriv])&lt;/blockquote&gt;&lt;br /&gt;Here I have eight different parameters to play with, but I really don't care about the version, or the revision, etc.  Read Paper Size may be a better, more concise, option.  It all depends on what information you will need later.  If the revision will be useful, then Read Paper Size will not be enough on it's own, and you are going to wind up querying the same information again when you Inquire Drawing Information.  Not every situation has the choice between multiple commands, however, so generally you are looking for the only one that will provide the information you need (and feeling pretty lucky if you find one).&lt;br /&gt;&lt;br /&gt;I'm afraid I don't have any other real tangible tips regarding syntax.  Refer to the documentation and/or existing programs as much as possible for guidance.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;I Object&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;I'm still relatively new to object oriented programming (OOP), having only dabbled a bit as I've tried to learn Mac/iPhone programming over the last couple of years.  I still struggle with some concepts here and there, but for the most I think I "get it": modular, reusable code.  The I-DEAS macro language is not object oriented.  But, through some careful planning you can still achieve a mostly modular result.&lt;br /&gt;&lt;br /&gt;I, of course, was not smart enough to think of this when I first started writing my programs.  About the second or third time I write the same basic code to do the same basic thing, it finally set in that I was doing more work than I needed to.  Unfortunately, ripping out existing code to reformat around a modular approach can be challenging, and you risk breaking code that already works.  But in the long run, you do yourself a huge favor by only having to fix code in one place, and the various modules make it easier to create new programs.&lt;br /&gt;&lt;br /&gt;Let's take an easy example, applying the current date.  I have several cases where I would want to do this.  When a user creates a new drawing, I like to automatically provide the creation date on the face of the print.  Same thing for when they add a revision.  No problem, let's dive into the help documents to find a command to help out.  Ah, this looks good:&lt;br /&gt;&lt;blockquote&gt;Inquire Date ( Doption, [Padding], Date )&lt;/blockquote&gt;&lt;br /&gt;Reading through the rest of the document, Doption is basically Date Option (MM-DD-YY or DD-MM-YY, for example), Padding essentially lets you force 2 digits for dates, so 03 for March instead of just 3, and then the Date is the output.  The previous code examples did not include any input parameters, but this one does, which I need to do in order to format the results the way I want.  The only problem is that the actual format I want - MM/YY - is not one of the built-in options.  I do not see any other commands that get the current date, so I'm going to have to find a way to work with this command.  The help document notes that the Date parameter is a text string, and I just happen to know that there are commands for slicing and dicing text strings up, so maybe I can extract the information I need and then build my own date string.&lt;br /&gt;&lt;br /&gt;First, the actual inquiry command:&lt;br /&gt;&lt;blockquote&gt;# Inquire Date ("dmy", 1, cur_date)&lt;/blockquote&gt;&lt;br /&gt;dmy sets the output format to be dd-mm-yy, and the 1 forces each section to be two digits.  This is a key setting, because it will be more complicated to slice my date string up if I don't know exactly which characters I need.  On 1-1-09, the month is the 3rd character, and the year is 5th and 6th.  But on 11-11-09, month becomes the 4th and 5th characters, and the year shifts, too.  By forcing each section to have 2 digits, I know which characters to aim for, regardless of the actual date.  The result is dumped into the cur_date variable.  Now to slice it up.&lt;br /&gt;&lt;br /&gt;The help files have a page called Functions, and it describes the various math and string functions that are available.  Probably the only one that will help here is STRMID(str1,a,b) which is defined as "Access a subset of "str1" beginning at character number "a" and up to and including character number "b" (if a=1, assumes start with first character)".  Take a string, start at one character, go until another, and copy that out to make a new string.  Perfect.  But there is a pesky dash between MM-YY, so I can't just pull it all out in a single string.  Oh well, do it twice, and assign the result to a different variable:&lt;br /&gt;&lt;blockquote&gt;# NewDate = STRMID(cur_date,4,5) + "/" + STRMID(cur_date,7,8)&lt;/blockquote&gt;&lt;br /&gt;I take the 4th and 5th characters for MM, add my slash, then the 7th and 8th characters for YY.  I now have today's date formatted the way I want it.&lt;br /&gt;&lt;br /&gt;So what's the big deal?  It's only two lines of code, how hard is that to copy and paste periodically?  In this case, it's not a big deal.  But if the code were more involved, there are more chances for bugs, and that bug would get repeated every time I copy-n-paste it until I actually fix it, at which point I then need to go revisit every program that contains the code and fix it again.  I know I don't have my entire program suite memorized, so I'll probably forget to fix it somewhere, and good luck if anyone else has to figure it out in my place.  But it doesn't even have to be about bugs.  Let's say that we change our minds about the format of the date, and now I want MM/DD/YY.  It sure would be nice to only have a single place to make that change, and then all of the other programs get it for free.  And that's exactly what I'll do.  Rather than leaving this code buried inside of other programs, I'll pull it out into its own program:&lt;br /&gt;&lt;blockquote&gt;# IF (trace EQ 1) THEN Show Message ("}}}IN{{{ Entering get_date.prg")&lt;br /&gt;&lt;br /&gt;C: --- Determine date&lt;br /&gt;C:&lt;br /&gt;# Inquire Date ("dmy", 1, cur_date)&lt;br /&gt;# IF (trace EQ 1) THEN Show Message ("cur_date is: " + cur_date)&lt;br /&gt;&lt;br /&gt;C: --- Reformat date to MM/YY&lt;br /&gt;C:&lt;br /&gt;# NewDate = STRMID(cur_date,4,5) + "/" + STRMID(cur_date,7,8)&lt;br /&gt;# IF (trace EQ 1) THEN Show Message ("NewDate is: " + NewDate)&lt;br /&gt;&lt;br /&gt;# IF (trace EQ 1) THEN Show Message ("{{{OUT}}} Done finding date...")&lt;br /&gt;&lt;br /&gt;# EXIT&lt;/blockquote&gt;&lt;br /&gt;Anytime that this is run, I know that the variable NewDate will be populated with a date in my desired format.  So in any other program where I would want that date, I just need to run this program like so:&lt;br /&gt;&lt;blockquote&gt;# Execute Macro ("C:\get_date.prg",,)&lt;/blockquote&gt;&lt;br /&gt;That's mostly all that needs to happen.  There are some additional considerations that I'll discuss at a later time, but for the most part this works really well.  I have several of these single-purpose little modules: get user name, get sheet size, get all attributes out of a symbol, check the permissions on a drawing, and so on.  This way I don't have to reinvent the wheel each time I start a new program.  I utilize whatever pieces I already have, and then focus on what will be custom to my new program.  For the most part, once the modules work, I've found them to be very reliable, so if errors crop up, I generally know to focus my attention on other areas of the code.  Or, if there is a problem in a module, (well, for one it's probably causing a problem in a lot of different places) I fix it one time in one place and get all of the downstream benefits.&lt;br /&gt;&lt;br /&gt;So there you have it.  It's certainly not OOP in its purest form, but it is about as close as I could manage within the I-DEAS macro paradigm.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38820386-899815763783598547?l=clingingtoideas.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/7fRFtNQ_x-hJxTkG29ptWbc7t_Q/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/7fRFtNQ_x-hJxTkG29ptWbc7t_Q/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/7fRFtNQ_x-hJxTkG29ptWbc7t_Q/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/7fRFtNQ_x-hJxTkG29ptWbc7t_Q/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ClingingToIdeas/~4/7TbEy_i2F9E" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://clingingtoideas.blogspot.com/feeds/899815763783598547/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=38820386&amp;postID=899815763783598547" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/899815763783598547?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/38820386/posts/default/899815763783598547?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/ClingingToIdeas/~3/7TbEy_i2F9E/more-macro-tidbits.html" title="More Macro Tidbits" /><author><name>The Slick One</name><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="31" height="32" src="http://1.bp.blogspot.com/_nJq3n1l5xlM/SmYOXU5LAyI/AAAAAAAAABU/-SvDv814nmI/S220/DSC_1796.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://clingingtoideas.blogspot.com/2009/01/more-macro-tidbits.html</feedburner:origLink></entry></feed>

