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

<channel>
	<title>SteveX Compiled &#187; Development</title>
	<atom:link href="http://blog.stevex.net/category/development/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.stevex.net</link>
	<description>Software development and other notes.</description>
	<lastBuildDate>Tue, 24 Jan 2012 00:39:30 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Building a Dynamic UIActionSheet</title>
		<link>http://blog.stevex.net/2011/12/building-a-dynamic-uiactionsheet/</link>
		<comments>http://blog.stevex.net/2011/12/building-a-dynamic-uiactionsheet/#comments</comments>
		<pubDate>Sat, 03 Dec 2011 16:37:56 +0000</pubDate>
		<dc:creator>stevex</dc:creator>
				<category><![CDATA[Apple]]></category>
		<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://blog.stevex.net/?p=1923</guid>
		<description><![CDATA[Most of the examples I see for UIActionSheet (the iOS pop-up menu) show static choices and checking for which one was picked by using constant values. That&#8217;s not going to work if your menu needs to be dynamic. For example, if you want to have an option to Email or Print an item, you might [...]]]></description>
			<content:encoded><![CDATA[<p>Most of the examples I see for UIActionSheet (the iOS pop-up menu) show static choices and checking for which one was picked by using constant values.  That&#8217;s not going to work if your menu needs to be dynamic.</p>
<p>For example, if you want to have an option to Email or Print an item, you might want this on a UIActionSheet menu.  But if either of these things aren&#8217;t available (for example, you may be running on a device that doesn&#8217;t support printing, or there may not be an email account configured) then you shouldn&#8217;t show these items. </p>
<p>Here&#8217;s one way to do this.</p>
<p>Start out by creating localized string variables for the menu items.</p>
<div class="codecolorer-container objc dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:600px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="objc codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/"><span style="color: #400080;">NSString</span></a> <span style="color: #002200;">*</span>MPActionMenuCancelItem <span style="color: #002200;">=</span> NSLocalizedString<span style="color: #002200;">&#40;</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;Cancel&quot;</span>, <span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;Cancel item in Action Sheet&quot;</span><span style="color: #002200;">&#41;</span>;<br />
<a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/"><span style="color: #400080;">NSString</span></a> <span style="color: #002200;">*</span>MPActionMenuDeleteItem <span style="color: #002200;">=</span> NSLocalizedString<span style="color: #002200;">&#40;</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;Delete&quot;</span>, <span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;Delete item in Action Sheet&quot;</span><span style="color: #002200;">&#41;</span>;<br />
<a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/"><span style="color: #400080;">NSString</span></a> <span style="color: #002200;">*</span>MPActionMenuEmailItem <span style="color: #002200;">=</span> NSLocalizedString<span style="color: #002200;">&#40;</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;Email&quot;</span>, <span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;Email item in Action Sheet&quot;</span><span style="color: #002200;">&#41;</span>;<br />
<a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/"><span style="color: #400080;">NSString</span></a> <span style="color: #002200;">*</span>MPActionMenuPrintItem <span style="color: #002200;">=</span> NSLocalizedString<span style="color: #002200;">&#40;</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;Print&quot;</span>, <span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;Print item in Action Sheet&quot;</span><span style="color: #002200;">&#41;</span>;</div></td></tr></tbody></table></div>
<p>Note that I&#8217;m using NSLocalizedString here and not simply using a string value.</p>
<p>Now you need to dynamically build the UIActionSheet.  Here&#8217;s code to do that:</p>
<div class="codecolorer-container objc dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:600px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br /></div></td><td><div class="objc codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span>IBAction<span style="color: #002200;">&#41;</span>actionSelected<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><span style="color: #a61390;">id</span><span style="color: #002200;">&#41;</span>sender <br />
<span style="color: #002200;">&#123;</span><br />
&nbsp; &nbsp; UIActionSheet <span style="color: #002200;">*</span>actions <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span><span style="color: #002200;">&#91;</span>UIActionSheet alloc<span style="color: #002200;">&#93;</span> initWithTitle<span style="color: #002200;">:</span><span style="color: #a61390;">nil</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;delegate<span style="color: #002200;">:</span>self <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cancelButtonTitle<span style="color: #002200;">:</span><span style="color: #a61390;">nil</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;destructiveButtonTitle<span style="color: #002200;">:</span><span style="color: #a61390;">nil</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; otherButtonTitles<span style="color: #002200;">:</span><span style="color: #a61390;">nil</span><span style="color: #002200;">&#93;</span>;<br />
<br />
&nbsp; &nbsp; <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span><span style="color: #002200;">&#91;</span>MFMailComposeViewController canSendMail<span style="color: #002200;">&#93;</span><span style="color: #002200;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #002200;">&#91;</span>actions addButtonWithTitle<span style="color: #002200;">:</span>MPActionMenuEmailItem<span style="color: #002200;">&#93;</span>;<br />
<br />
&nbsp; &nbsp; <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span><span style="color: #002200;">&#91;</span>UIPrintInteractionController canPrintURL<span style="color: #002200;">:</span><span style="color: #002200;">&#91;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSURL_Class/"><span style="color: #400080;">NSURL</span></a> fileURLWithPath<span style="color: #002200;">:</span>imageData.path<span style="color: #002200;">&#93;</span><span style="color: #002200;">&#93;</span><span style="color: #002200;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #002200;">&#91;</span>actions addButtonWithTitle<span style="color: #002200;">:</span>MPActionMenuPrintItem<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; actions.destructiveButtonIndex <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>actions addButtonWithTitle<span style="color: #002200;">:</span>MPActionMenuDeleteItem<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; actions.cancelButtonIndex <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>actions addButtonWithTitle<span style="color: #002200;">:</span>MPActionMenuCancelItem<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span style="color: #002200;">&#91;</span>actions showFromBarButtonItem<span style="color: #002200;">:</span>sender animated<span style="color: #002200;">:</span><span style="color: #a61390;">YES</span><span style="color: #002200;">&#93;</span>;<br />
<span style="color: #002200;">&#125;</span></div></td></tr></tbody></table></div>
<p>Notice how email and printing are only included if supported.  </p>
<p>It&#8217;s convenient that addButtonWithTitle: returns the button index that was added, so we can assign the button indexes for those directly.  </p>
<p>When the user picks an item from the UIActionSheet, we need to figure out what they picked and act on it.</p>
<div class="codecolorer-container objc dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:600px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br /></div></td><td><div class="objc codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span>actionSheet<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span>UIActionSheet <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>actionSheet clickedButtonAtIndex<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span>NSInteger<span style="color: #002200;">&#41;</span>buttonIndex<br />
<span style="color: #002200;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>buttonIndex <span style="color: #002200;">==</span> actionSheet.cancelButtonIndex<span style="color: #002200;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #a61390;">return</span>;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>buttonIndex <span style="color: #002200;">==</span> actionSheet.destructiveButtonIndex<span style="color: #002200;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #002200;">&#91;</span>self deleteItem<span style="color: #002200;">&#93;</span>;<br />
&nbsp;<br />
&nbsp; &nbsp; <a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/"><span style="color: #400080;">NSString</span></a> <span style="color: #002200;">*</span>clicked <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>actionSheet buttonTitleAtIndex<span style="color: #002200;">:</span>buttonIndex<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span><span style="color: #002200;">&#91;</span>clicked compare<span style="color: #002200;">:</span>MPActionMenuEmailtem<span style="color: #002200;">&#93;</span> <span style="color: #002200;">==</span> NSOrderedSame<span style="color: #002200;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #002200;">&#91;</span>self emailItem<span style="color: #002200;">&#93;</span>;<br />
<br />
&nbsp; &nbsp; <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span><span style="color: #002200;">&#91;</span>clicked compare<span style="color: #002200;">:</span>MPActionMenuPrintItem<span style="color: #002200;">&#93;</span> <span style="color: #002200;">==</span> NSOrderedSame<span style="color: #002200;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #002200;">&#91;</span>self printItem<span style="color: #002200;">&#93;</span>;<br />
<span style="color: #002200;">&#125;</span></div></td></tr></tbody></table></div>
<p>I thought about building a table of indices to action values or something more complex like that, because comparing the selected string somehow just doesn&#8217;t feel right.  That may be my years as a Windows developer showing through, because this way is straightforward, and works great.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.stevex.net/2011/12/building-a-dynamic-uiactionsheet/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>UITableView, Multi Select and Swipe To Delete</title>
		<link>http://blog.stevex.net/2011/11/uitableview-swipe-to-delete/</link>
		<comments>http://blog.stevex.net/2011/11/uitableview-swipe-to-delete/#comments</comments>
		<pubDate>Tue, 29 Nov 2011 18:15:44 +0000</pubDate>
		<dc:creator>stevex</dc:creator>
				<category><![CDATA[Adobe]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[iOS]]></category>

		<guid isPermaLink="false">http://blog.stevex.net/?p=1907</guid>
		<description><![CDATA[I was having a problem recently with a UITableView, where the swipe-to-delete gesture wasn&#8217;t working. The documentation is pretty clear that your table will get the swipe to delete behaviour if you implement the tableView:commitEditingStyle:forRowAtIndexPath: method in your UITableViewDataSource delegate. So, in other words, implementing this one method in your data source should be enough [...]]]></description>
			<content:encoded><![CDATA[<p>I was having a problem recently with a UITableView, where the swipe-to-delete gesture wasn&#8217;t working.  </p>
<p>The documentation is pretty clear that your table will get the swipe to delete behaviour if you implement the <code class="codecolorer text dawn"><span class="text">tableView:commitEditingStyle:forRowAtIndexPath:</span></code> method in your <code class="codecolorer text dawn"><span class="text">UITableViewDataSource</span></code> delegate.</p>
<p>So, in other words, implementing this one method in your data source should be enough to enable swiping to delete a row:</p>
<div class="codecolorer-container objc dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:600px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br /></div></td><td><div class="objc codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span>tableView<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span>UITableView <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>tableView commitEditingStyle<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span>UITableViewCellEditingStyle<span style="color: #002200;">&#41;</span>editingStyle forRowAtIndexPath<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSIndexPath_Class/"><span style="color: #400080;">NSIndexPath</span></a> <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>indexPath<br />
<span style="color: #002200;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>editingStyle <span style="color: #002200;">==</span> UITableViewCellEditingStyleDelete<span style="color: #002200;">&#41;</span> <br />
&nbsp; &nbsp; <span style="color: #002200;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #11740a; font-style: italic;">// Delete the row from the data source.</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #002200;">&#91;</span>self.model deleteItemAtIndex<span style="color: #002200;">:</span>indexPath.row<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #11740a; font-style: italic;">// And from the table itself </span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #002200;">&#91;</span>tableView deleteRowsAtIndexPaths<span style="color: #002200;">:</span><span style="color: #002200;">&#91;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/"><span style="color: #400080;">NSArray</span></a> arrayWithObject<span style="color: #002200;">:</span>indexPath<span style="color: #002200;">&#93;</span> withRowAnimation<span style="color: #002200;">:</span>UITableViewRowAnimationFade<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; <span style="color: #002200;">&#125;</span> <br />
<span style="color: #002200;">&#125;</span></div></td></tr></tbody></table></div>
<p>But this wasn&#8217;t working for me.</p>
<p>After a bit of digging, I found the problem.  My table supports an edit mode that the user flips into by pressing an Edit button in the toolbar.  When in edit mode, the user can tap on multiple cells and then tap a trashcan at the bottom to delete all the selected cells.  This works nicely, but interferes with the swipe-to-delete gesture, in that swipe-to-delete isn&#8217;t supported when multiple selection is enabled in edit mode.</p>
<p>What I wanted was single selection when not in edit mode, and multiple selection when in edit mode.  This was easy enough to achieve:</p>
<div class="codecolorer-container objc dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:600px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br /></div></td><td><div class="objc codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #002200;">-</span><span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span>setEditing<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><span style="color: #a61390;">BOOL</span><span style="color: #002200;">&#41;</span>editing animated<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><span style="color: #a61390;">BOOL</span><span style="color: #002200;">&#41;</span>animated <br />
<span style="color: #002200;">&#123;</span><br />
&nbsp; &nbsp; self.tableView.allowsMultipleSelectionDuringEditing <span style="color: #002200;">=</span> editing;<br />
&nbsp; &nbsp; <span style="color: #002200;">&#91;</span>super setEditing<span style="color: #002200;">:</span>editing animated<span style="color: #002200;">:</span>animated<span style="color: #002200;">&#93;</span>;<br />
<span style="color: #002200;">&#125;</span></div></td></tr></tbody></table></div>
<p>Make sure the table is set for Single Selection During Editing in Interface Builder, and that&#8217;s it.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.stevex.net/2011/11/uitableview-swipe-to-delete/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>MealPlan for the PlayBook</title>
		<link>http://blog.stevex.net/2011/11/mealplan-for-the-playbook/</link>
		<comments>http://blog.stevex.net/2011/11/mealplan-for-the-playbook/#comments</comments>
		<pubDate>Thu, 10 Nov 2011 03:44:59 +0000</pubDate>
		<dc:creator>stevex</dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://blog.stevex.net/?p=1895</guid>
		<description><![CDATA[I like writing software to scratch a personal itch, and answering the &#8220;what&#8217;s for dinner&#8221; question has always been something I figured computers could help with. I had a PlayBook, development tools, and a desire for an app that would make it easy to quickly put together a plan for the meals for the week. [...]]]></description>
			<content:encoded><![CDATA[<p>I like writing software to scratch a personal itch, and answering the &#8220;what&#8217;s for dinner&#8221; question has always been something I figured computers could help with. </p>
<p>I had a PlayBook, development tools, and a desire for an app that would make it easy to quickly put together a plan for the meals for the week.  Not the sort of in-depth grocery list tracking app that most of these turn into, but something that makes it easy to say &#8220;we&#8217;re having burgers on Tuesday&#8221;.</p>
<p>What I created is an app called <a href="https://appworld.blackberry.com/webstore/content/32936?lang=en">MealPlan</a>:</p>
<p><a href="http://blog.stevex.net/files/2011/11/screenshot.png" onclick="window.open('http://blog.stevex.net/files/2011/11/screenshot.png','popup','width=1026,height=601,scrollbars=no,resizable=yes,toolbar=no,directories=no,location=no,menubar=no,status=yes,left=0,top=0');return false"><img src="http://blog.stevex.net/files/2011/11/screenshot-tm.jpg" height="374" width="640" border="1" hspace="4" vspace="4" alt="Screenshot" /></a><br />
The UI is simple and easy to understand.  The ratings have generally been pretty good, with most of the negative commenters asking for more features (which IMHO is a pretty good place to start).  I&#8217;m going to work on a bug fix release now that I have time available to work on it.  I just recently set a price on the app, $1.99; it&#8217;s been free since I didn&#8217;t want any conflict with my work at Adobe, but now that I&#8217;m no longer with Adobe, I can charge for it.  The free ride is over.  :)</p>
<p>If you&#8217;ve got a PlayBook, and ever have a tough time with the question &#8220;What&#8217;s for dinner?&#8221; have a look at <a href="https://appworld.blackberry.com/webstore/content/32936?lang=en">MealPlan</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.stevex.net/2011/11/mealplan-for-the-playbook/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SmugMug Uploader Take 2</title>
		<link>http://blog.stevex.net/2009/03/smugmug-uploader-take-2/</link>
		<comments>http://blog.stevex.net/2009/03/smugmug-uploader-take-2/#comments</comments>
		<pubDate>Wed, 11 Mar 2009 11:10:30 +0000</pubDate>
		<dc:creator>stevex</dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://blog.stevex.net/?p=1342</guid>
		<description><![CDATA[I posted my AIR-based SmugMug Uploader about a year ago, and back then, I had rolled my own version of a SmugMug API client. Then I found the as3smugmuglib project on Google Code. I changed the app to work with that API instead of my own code. Because I used Cairngorm when I built the [...]]]></description>
			<content:encoded><![CDATA[<p>I posted my <a href="http://blog.stevex.net/index.php/2008/03/26/using-the-smugmug-api-from-adobe-air/">AIR-based SmugMug Uploader</a> about a year ago, and back then, I had rolled my own version of a SmugMug API client.</p>
<p>Then I found the <a href="http://code.google.com/p/as3smugmuglib/">as3smugmuglib</a> project on Google Code.  I changed the app to work with that API instead of my own code.</p>
<p>Because I used Cairngorm when I built the original app, the protocol code was isolated into the business classes (org.stevex.smug.business).  The refactoring had little impact on the rest of the project.</p>
<p>I cleaned up a couple of minor bugs as well, so here&#8217;s a new release.  It still doesn&#8217;t have any of the features that I wanted when I started this project (like a way to automatically upload new photos, or even batch uploading) but as usual I&#8217;m hoping to get back to it soon.</p>
<p>Here&#8217;s a zip file with the complete source:  <a href='http://blog.stevex.net/files/2009/03/smug1.zip'>Smug1.zip</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.stevex.net/2009/03/smugmug-uploader-take-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Bandwidth Donation Protocol</title>
		<link>http://blog.stevex.net/2008/12/bandwidth-donation-protocol/</link>
		<comments>http://blog.stevex.net/2008/12/bandwidth-donation-protocol/#comments</comments>
		<pubDate>Wed, 31 Dec 2008 02:59:06 +0000</pubDate>
		<dc:creator>stevex</dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://blog.stevex.net/?p=1260</guid>
		<description><![CDATA[Bandwidth is both expensive and cheap. It&#8217;s cheap at a lot of hosts and for a lot of consumers, because providers expect we&#8217;re not going to use what we&#8217;re allocated, and yet expensive when purchased in bulk, where the assumption must be that it will all be used. Every time I hear about how expensive [...]]]></description>
			<content:encoded><![CDATA[<p>Bandwidth is both expensive and cheap.  It&#8217;s cheap at a lot of hosts and for a lot of consumers, because providers expect we&#8217;re not going to use what we&#8217;re allocated, and yet expensive when purchased in bulk, where the assumption must be that it will all be used.</p>
<p>Every time I hear about how expensive it is to run a successful podcast (like the NPR podcasts, some of which are begging for money to pay for $150k annual bandwidth bills), I think to myself, &#8220;I have spare bandwidth; I&#8217;d like to donate some&#8221;.  But there&#8217;s no mechanism for me to do that.</p>
<p>And why not?  I don&#8217;t think it would be that hard to do.  Let&#8217;s take a rough cut at it.</p>
<p>The goal is for a provider to be able to share a particular piece of content, say a podcast, with as many people as possible, and accept donated bandwidth for distributing it.</p>
<p>One possibility is BitTorrent, which does a fairly good job of this, but would require a custom client.  The biggest podcast client in the world is iTunes, and it doesn&#8217;t support BitTorrent, so any podcast that doesn&#8217;t want to lose most of its audience will stick with HTTP as the distribution protocol.  We need a way of distributing URLs that will load balance to the donated bandwidth.</p>
<p>As a donator, here&#8217;s what I want to be able to do:  Go to, say, the NPR website, and click on the &#8220;donate bandwidth&#8221; link.  At this link I&#8217;d enter the URL for my Bandwidth Donation Protocol (BDP) service, the amount of bandwidth I want to donate (in megabytes or gigabytes) and a key or password.  NPR&#8217;s service would then add me to it&#8217;s bandwidth donator pool.</p>
<p>Now when they have a podcast they want to post, they generate a URL for that podcast.  Say it&#8217;s:</p>
<p>http://npr.org/content/hypotheticalpodcast.mp3</p>
<p>Before using my donated bandwidth, they need to upload the content to me.  Their server contacts my server and says &#8220;here&#8217;s hypotheticalpodcast.mp3, and I&#8217;d like you to allocate 1 gigabyte worth of bandwidth to it&#8221;.  In return, my server would generate, say, 200 unique URLs  and return them to NPR.  Now for the next 200 requests for this content, they can 301 redirect to my URLs and distribute the content at no cost to them.  </p>
<p>Why generate the 200 URLs?  Why not just use the same URL and stop answering after 200 requests?  Because a single user downloading content might request the URL more than once (for example, picking up ranges of content), so the URL can cut off after a reasonable amount of time, but not be vulnerable to abuse by giving out the URL 500 times when only authorized to do so 200 times.</p>
<p>I&#8217;ve wanted to implement something like this for years but never had the time, so I&#8217;m throwing it out there:  What do you think?  Would it work?  Would it help the podcasters being crushed by their own success? </p>
]]></content:encoded>
			<wfw:commentRss>http://blog.stevex.net/2008/12/bandwidth-donation-protocol/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Outsourcing Commenting</title>
		<link>http://blog.stevex.net/2008/11/outsourcing-commenting/</link>
		<comments>http://blog.stevex.net/2008/11/outsourcing-commenting/#comments</comments>
		<pubDate>Sat, 15 Nov 2008 14:15:24 +0000</pubDate>
		<dc:creator>stevex</dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://blog.stevex.net/?p=1231</guid>
		<description><![CDATA[There&#8217;s a service called Disqus that provides hosted commenting for your blog or website. My Ottawa Events site was recently hit by a deluge of spam &#8211; so much so that I had to disable commenting completely. I discovered Discus through Dave Winer&#8217;s blog, and it seems like it&#8217;s worth a shot, so I&#8217;ve enabled [...]]]></description>
			<content:encoded><![CDATA[<p>There&#8217;s a service called <a href="http://www.disqus.com">Disqus</a> that provides hosted commenting for your blog or website.</p>
<p>My Ottawa Events site was recently hit by a deluge of spam &#8211; so much so that I had to disable commenting completely.  I discovered Discus through <a href="http://www.scripting.com">Dave Winer&#8217;s blog</a>, and it seems like it&#8217;s worth a shot, so I&#8217;ve enabled it on the Ottawa Events site.</p>
<p>I like the idea that I can sign up for an account with Disqus and that account works on any blog or site that uses them for commenting.  These sorts of sites that provide hosted services to webmasters tend to get bought up by someone if they do a good job (as happened to FeedBurner, for example) so I&#8217;m not too worried about my comments disappearing, although that is a risk I take by letting someone else host them.  Ottawa Events content is time sensitive, and the comments aren&#8217;t valuable anymore once the event has passed, so if Disqus did disappear, I wouldn&#8217;t lose a lot of valuable content.</p>
<p>There are two services that Disqus could be providing.  One is simply managing commenting, which they&#8217;re doing, but the other is filtering spam.  I don&#8217;t know how they do at that job, but if Disqus just becomes another spam target, their value disappears.  Hopefully they&#8217;re well aware of that.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.stevex.net/2008/11/outsourcing-commenting/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Disable (and Document) Menu Items</title>
		<link>http://blog.stevex.net/2008/07/disable-and-document-menu-items/</link>
		<comments>http://blog.stevex.net/2008/07/disable-and-document-menu-items/#comments</comments>
		<pubDate>Tue, 01 Jul 2008 17:25:47 +0000</pubDate>
		<dc:creator>stevex</dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://blog.stevex.net/?p=1170</guid>
		<description><![CDATA[Joel posted a short article: Don&#8217;t Hide or Disable Menu Items. I think it&#8217;s bad advice. Consider Paste or Undo. If you click the Edit menu, you can tell from the enabled state of the Paste item whether or not there&#8217;s something on the clipboard that can be pasted. Same with Undo. If Save is [...]]]></description>
			<content:encoded><![CDATA[<p>Joel posted a short article:  <a href="http://www.joelonsoftware.com/items/2008/07/01.html">Don&#8217;t Hide or Disable Menu Items</a>.  I think it&#8217;s bad advice.</p>
<p>Consider Paste or Undo.  If you click the Edit menu, you can tell from the enabled state of the Paste item whether or not there&#8217;s something on the clipboard that can be pasted.  Same with Undo.  If Save is disabled it means there have been no changes since the last save.  The enabled state of these items conveys information.</p>
<p>I&#8217;d rather click Edit and see Undo greyed out, than click Edit, Undo, and have a dialog pop up telling me that there&#8217;s nothing I can undo right now.</p>
<p>It would be nice if your Help system included a description of all the menu items, and when they are available.  An lot of the documentation I&#8217;ve seen includes descriptions of menu items that add no information (&#8220;Undo:  Undoes the last operation&#8221;) and leave out valuable information like under what circumstance that item might not be available.<br />
It&#8217;s unlike Joel to post bad advice.  Perhaps he need a little <a href="http://en.wikipedia.org/wiki/Search_engine_optimization">SEO</a> and posting something obviously wrong would be a great way to get a lot of people to link to him with corrections?</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.stevex.net/2008/07/disable-and-document-menu-items/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Managing Coder Estimates</title>
		<link>http://blog.stevex.net/2008/05/managing-coder-estimates/</link>
		<comments>http://blog.stevex.net/2008/05/managing-coder-estimates/#comments</comments>
		<pubDate>Thu, 22 May 2008 02:51:45 +0000</pubDate>
		<dc:creator>stevex</dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://blog.stevex.net/index.php/2008/05/21/managing-coder-estimates/</guid>
		<description><![CDATA[I just submitted a little job to RentACoder (converting an ASP.NET site to PHP for easier hosting &#8211; I&#8217;m not much of a PHP developer) and part of their new wizard has a bit about estimates. You can supply a deadline, or you can ask the bidders to estimate how long the job will take, [...]]]></description>
			<content:encoded><![CDATA[<p>I just submitted a little job to <a href="http://www.rentacoder.com/">RentACoder</a> (converting an ASP.NET site to PHP for easier hosting &#8211; I&#8217;m not much of a PHP developer) and part of their new wizard has a bit about estimates.  You can supply a deadline, or you can ask the bidders to estimate how long the job will take, based on your specifications.</p>
<p>But there&#8217;s a warning:</p>
<blockquote><p>Even the best coders do not estimate accurately.  If you don&#8217;t understand and account for this, it will probably cause you unnecessary problems.</p>
<p>Do you understand that it&#8217;s wisest to multiply any coder estimate by either 5 or 2 to get a more realistic estimate?</p></blockquote>
<p>I like that.  Multiply by either 5 or 2.  Pick one.  That&#8217;s about as accurate a method as any.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.stevex.net/2008/05/managing-coder-estimates/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>The Future of IntelliSense</title>
		<link>http://blog.stevex.net/2008/03/the-future-of-intellisense/</link>
		<comments>http://blog.stevex.net/2008/03/the-future-of-intellisense/#comments</comments>
		<pubDate>Sun, 02 Mar 2008 14:12:23 +0000</pubDate>
		<dc:creator>stevex</dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://blog.stevex.net/index.php/2008/03/02/the-future-of-intellisense/</guid>
		<description><![CDATA[Here&#8217;s a blog post by Jim Springfield at Microsoft about IntelliSense in Visual Studio 10.&#160; I like to hear that IntelliSense is going to be fixed, but I&#8217;ve been hearing that for many versions of Visual Studio now, and I have a hard time believing it.&#160; IntelliSense for C++ code has always been unreliable in [...]]]></description>
			<content:encoded><![CDATA[<p>Here&#8217;s a blog post by Jim Springfield at Microsoft about <a href="http://blogs.msdn.com/vcblog/archive/2008/02/29/intellisense-part-2-the-future.aspx">IntelliSense in Visual Studio 10</a>.&nbsp; </p>
<p>I like to hear that IntelliSense is going to be fixed, but I&#8217;ve been hearing that for many versions of Visual Studio now, and I have a hard time believing it.&nbsp; IntelliSense for C++ code has always been unreliable in the projects I&#8217;ve used it on (generally massive MFC applications).</p>
<p>Remember a product called <a href="http://www.lockergnome.com/windows/2004/07/26/microsoft-lookout-for-outlook/">Lookout</a>?&nbsp; It was a very fast, reliable search engine for Outlook.&nbsp; Microsoft bought it.&nbsp; I don&#8217;t know why they bought it, because they obviously didn&#8217;t use it &#8211; with Office 2007 on Vista, searching email is not fast and not reliable.&nbsp; </p>
<p>For C++ users of Visual Studio there&#8217;s a tool called <a href="http://www.wholetomato.com/products/default.asp">Visual Assist X</a>, which is a fast, reliable IntelliSense replacement for Visual Studio.&nbsp; It extends Visual Studio in a lot of ways, many of which don&#8217;t require any changes to your workflow.&nbsp; For example, if you have a pointer and you type &#8220;pMyPtr.&#8221; as soon as you type the period, it will be replaced with a &#8220;-&gt;&#8221;.</p>
<p>One particular improvement VAssistX makes to IntelliSense that I find very useful, is you can type &#8220;pMyWnd-&gt;size&#8221; and list that appears as you type &#8220;size&#8221; includes all the relevant symbols that contain the word &#8220;size&#8221;, not just ones that start with it.</p>
<p>I&#8217;d love to see VAssistX become obsolete with Visual Studio 10, but I&#8217;m not holding my breath.&nbsp; I just hope Microsoft doesn&#8217;t buy them, so that if Microsoft&#8217;s improvements don&#8217;t deliver, Whole Tomato still can.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.stevex.net/2008/03/the-future-of-intellisense/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Don&#8217;t Make Me Think</title>
		<link>http://blog.stevex.net/2008/02/dont-make-me-think/</link>
		<comments>http://blog.stevex.net/2008/02/dont-make-me-think/#comments</comments>
		<pubDate>Fri, 29 Feb 2008 03:43:24 +0000</pubDate>
		<dc:creator>stevex</dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://blog.stevex.net/index.php/2008/02/28/dont-make-me-think/</guid>
		<description><![CDATA[I just finished reading Steve Krug&#8217;s book Don&#8217;t Make Me Think!: A Common Sense Approach to Web Usability on Safari.&#160; The first edition was written in 2000, but it&#8217;s amazing how much of it still applies.&#160; And not just to web usability. The book is about web design, and how to create sites that users [...]]]></description>
			<content:encoded><![CDATA[<p>I just finished reading Steve Krug&#8217;s book <a href="http://search.safaribooksonline.com/0321344758">Don&#8217;t Make Me Think!: A Common Sense Approach to Web Usability</a> on <a href="http://www.safaribooksonline.com/">Safari</a>.&nbsp; The first edition was written in 2000, but it&#8217;s amazing how much of it still applies.&nbsp; And not just to web usability.</p>
<p>The book is about web design, and how to create sites that users can use without thinking about them.&nbsp; Kind of like how you walk into a room and reach for the lightswitch without first looking for it, because it&#8217;s usually in the same spot, there are strong conventions that, if followed, will enable users to use your site instinctively.&nbsp; If a box looks like a search box, users will assume it&#8217;s a search box and type stuff into it.&nbsp; Get it wrong and they&#8217;ll have to think about it, and web users aren&#8217;t good at that.</p>
<p>How much of this is true in software? </p>
<p>Users are usually much more invested in the software they&#8217;re using than in most of the websites they encounter, but that doesn&#8217;t mean as UI designers we shouldn&#8217;t try to learn some of the same lessons.&nbsp; Both Microsoft and Apple published UI guidelines that helped define conventions that software developers have been using for the last 20 or so years to create applications that most users can find their way around with the lights out.</p>
<p>I have two comments on this.&nbsp; The first is that from this perspective, the current trend away from standard user interfaces is disturbing.&nbsp; IE7 takes away the menu bar in favour of a collection of toolbar buttons that each drop down a selection of menu items.&nbsp; Office 2007 provides the ribbon bar.&nbsp; iTunes has a lot of quirky UI behavour.&nbsp; Plain old applications are passé.&nbsp; </p>
<p>In some ways this is like trying to find new places to put the light switch.&nbsp; </p>
<p>My other comment, and the one I&#8217;m really more interested in (since I don&#8217;t think I&#8217;m about to stop the swing from consistent UI design to style driven UI design on my own) is that the ways Steve does usability testing can be applied to software as well.</p>
<p>Good UI should also be something you don&#8217;t have to think about.&nbsp; Unless you&#8217;re doing something completely new (and you probably aren&#8217;t), a dialog should fit the pattern of other similar dialogs well enough that the user already knows how to use it.&nbsp; And the best way to determine whether or not you&#8217;re hitting this mark is usability testing.</p>
<p>A phenomenon that Steve talks about is one where companies try to do higher quality usability testing and end up going overboard.&nbsp; By spending money on testers and studies and finding the right sample of users to test with, and coming up with extensive scripts and processes, the process becomes onerous.&nbsp; Not something you can do on a whim.&nbsp; So they end up not doing it very often.&nbsp; That&#8217;s typical of every company I&#8217;ve worked for.</p>
<p>But that&#8217;s how you should be doing it.&nbsp; Mock up a dialog, find a few users, and show it to them.&nbsp; Ask them to use it, and see how it goes.&nbsp; Do this often, and in a more or less ad-hoc manner.&nbsp; If you have questions about how new UI should work, prototype it, and then put it in front of some users with roughly the amount of computer skill you expect your target users to have, and see how they use it.&nbsp; Lather, rinse, repeat. </p>
<p>If you don&#8217;t approach it this way, you won&#8217;t do as much testing, and imperfect usability testing is still way better than none.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.stevex.net/2008/02/dont-make-me-think/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

