<?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:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>Espresso Served Here</title>
	<atom:link href="http://espresso-served-here.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://espresso-served-here.com</link>
	<description>Mac applications, brewed to perfection</description>
	<pubDate>Tue, 28 Oct 2008 22:33:22 +0000</pubDate>
	<generator>http://wordpress.org/?v=MU</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>I Love Outline Views - Here&#8217;s Mine</title>
		<link>http://espresso-served-here.com/2008/10/24/i-love-outline-views-heres-mine/</link>
		<comments>http://espresso-served-here.com/2008/10/24/i-love-outline-views-heres-mine/#comments</comments>
		<pubDate>Fri, 24 Oct 2008 17:07:33 +0000</pubDate>
		<dc:creator>Jonathan Dann</dc:creator>
		
		<category><![CDATA[Apple]]></category>

		<category><![CDATA[Cocoa]]></category>

		<category><![CDATA[How-to]]></category>

		<category><![CDATA[Mac Stuff]]></category>

		<category><![CDATA[Scribbler]]></category>

		<guid isPermaLink="false">http://jonathandann.wordpress.com/?p=73</guid>
		<description><![CDATA[Apps like Coda are the de-facto standard for good user-interface design nowadays. You can do a lot with the standard Cocoa controls provided by Apple, but you can&#8217;t always get exactly what you want out-of-the-box.  For example, the CSS edit section of Coda has a fantastic way of dividing tasks: the animated outline view.
Rather [...]]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>Apps like <a title="Panic! - Coda" href="http://www.panic.com/coda/" target="_blank">Coda</a> are the de-facto standard for good user-interface design nowadays. You can do a lot with the standard Cocoa controls provided by Apple, but you can&#8217;t always get exactly what you want out-of-the-box.  For example, the CSS edit section of Coda has a fantastic way of dividing tasks: the animated outline view.</p>
<p>Rather than having a monolithic view that has all the controls grouped with dividing lines; each section, &#8220;Text&#8221;, &#8220;Colors and Background&#8221;, &#8220;Dimensions&#8221;, all exist in their own collapsible view. Clicking the separating bar animatedly expands or collapses the view.</p>
<p><a href="http://jonathandann.files.wordpress.com/2008/10/tlanimatingoutlineview.jpg"><img class="aligncenter size-medium wp-image-76" title="TLAnimatingOutlineView" src="http://jonathandann.files.wordpress.com/2008/10/tlanimatingoutlineview.jpg?w=300&#038;h=263" alt="" width="300" height="263" /></a><br />
To make one of these views isn&#8217;t quite as easy as you first think, and you can quickly go down the wrong road in the design. The super-secret trick is to <em>*not*</em> use Core Animation, or more precisely: ignore the lure of <code>NSView</code>s conformance to the <code>NSAnimatablePropertyContainer</code> protocol.  As of Mac OS X 10.5, a few of the properties of a view or window can be animated simply be replacing calls like <code>[view setFrame:newFrame];</code> with <code>[[view animator] setFrame:newFrame];</code>. This is fine when you have one or two views that you want to move but it&#8217;s impossible to coordinate the movements of multiple view using this API.  After using Core Animation, one would expect that after a call such as <code>[[view animator] setFrame:newFrame];</code> requesting the frame from the view would return newFrame. Unfortunatley it returns the original frame of the view. Only when the animation is done, do you get newFrame.  Furthermore, just try setting the delegate of the <code>CABasicAnimation</code> object that handles the implicit animation and getting any form of useful information back. You can&#8217;t. You&#8217;re in for a world of pain if you try.</p>
<p>The solution: use good-ol&#8217; <code>NSViewAnimation</code>.  That way you can create all the dictionaries containing the animations for all the views you want to, then instantiate one <code>NSViewAnimation</code> object to handle the lot and set them off at the same time. Simple.</p>
<h3>The Animating Outline View</h3>
<p>The outline view is made up of a few classes, the <code>TLDisclosureBar</code>, a subclass of the highly-configurable <code>TLGradientView</code>; the <code>TLCollapsibleView</code> which has a <code>TLDisclosureBar</code> and any <code>NSView</code> you like as its subviews. A <code>TLCollapsibleView</code> itself can be used alone, and will animate the collapse/expansion of the <code>NSView</code> subview (which I term the &#8220;detail view&#8221;).</p>
<p>The fun part comes when <code>TLCollapsibleViews</code> are subviews of a <code>TLAnimatingOutlineView</code>. In this case, the <code>TLAnimatingOutlineView</code> takes over all the animations, simply asking the <code>TLCollapsibleView</code> that the user has selected for the <code>NSDictionary</code> object that describes the collapse/expansion animation.  With this information, it constructs the movement animations for all the subviews below the collapsing/expanding view and handles the required change of frame size to accommodate the changing content size. It&#8217;s all quite elegant, if I do say so myself.</p>
<p>Not being content with example code, I&#8217;ve made sure these classes are real-world useful.  This view is integral to BibTeX management in Scribbler so it needs to be good.  Some of the relevant parts of the <code>NSOutlineView</code> API are replicated and the delegate of the <code>TLAnimatingOutlineView</code> gets will/did/expand/collapse notifications, along with allowing the delegate to deny/allow animations, too. Each of the <code>TLCollapsibleView</code>s will also ask their detail views if the collapse/expansion is allowed if the <code>TLCollapsibleDetailView</code> protocol is implemented.  The <code>TLAnimatingOutlineView</code> itself works when in an <code>NSScrollView</code>.</p>
<p>The code, along with an example project, is hosted on Google Code <a href="http://code.google.com/p/tlanimatingoutlineview/">here</a>. It is distributed under the new BSD license.</p>
<p>So why is is not <code>ESAnimatingOutlineView</code>, why the TL namespace? Well, I&#8217;m working on something big, at the same time as writing Scribbler. Start guessing.</p>
<h3>Update</h3>
<p>I&#8217;ve fixed a few bugs in the code, the current version can be checked out of the repository. Fixes include: proper support for hiding subviews of the <code>TLDisclosureBar</code>s when the outline view is resized small, a correction to the autoresizing mask of the <code>TLAnimatingOutlineView</code> itself, declaration of keys that the <code>TLCollapsibleView</code> supplies with the animation dictionaries, the delegate of the outline view and subclasses of <code>TLGradientView</code> are no longer unregistered for notifications that client code specifies (i.e. no longer uses <code>[[NSNotifcationCenter defaultCenter] removeObserver:_delegate];</code>).</p>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/jonathandann.wordpress.com/73/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/jonathandann.wordpress.com/73/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/jonathandann.wordpress.com/73/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/jonathandann.wordpress.com/73/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/jonathandann.wordpress.com/73/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/jonathandann.wordpress.com/73/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/jonathandann.wordpress.com/73/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/jonathandann.wordpress.com/73/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/jonathandann.wordpress.com/73/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/jonathandann.wordpress.com/73/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=espresso-served-here.com&blog=656654&post=73&subd=jonathandann&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://espresso-served-here.com/2008/10/24/i-love-outline-views-heres-mine/feed/</wfw:commentRss>
	
		<media:content url="http://a.wordpress.com/avatar/jpdann-128.jpg" medium="image">
			<media:title type="html">jpdann</media:title>
		</media:content>

		<media:content url="http://jonathandann.files.wordpress.com/2008/10/tlanimatingoutlineview.jpg?w=300" medium="image">
			<media:title type="html">TLAnimatingOutlineView</media:title>
		</media:content>
	</item>
		<item>
		<title>Scribbler&#8217;s Image Editing Opensourced Part 1</title>
		<link>http://espresso-served-here.com/2008/10/06/scribblers-image-editing-opensourced-part-1/</link>
		<comments>http://espresso-served-here.com/2008/10/06/scribblers-image-editing-opensourced-part-1/#comments</comments>
		<pubDate>Sun, 05 Oct 2008 23:00:08 +0000</pubDate>
		<dc:creator>Jonathan Dann</dc:creator>
		
		<category><![CDATA[Cocoa]]></category>

		<category><![CDATA[Scribbler]]></category>

		<category><![CDATA[Core Image]]></category>

		<category><![CDATA[vImage]]></category>

		<guid isPermaLink="false">http://jonathandann.wordpress.com/?p=63</guid>
		<description><![CDATA[Well since I no longer work at the NHS I&#8217;ve been working really hard on one of the great features of Scribbler, image editing.  As something I wanted to put in at the very start, I was really enthusiastic to start using Apple&#8217;s Image Kit. It promised developers the chance to do very little [...]]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>Well since I no longer work at the NHS I&#8217;ve been working really hard on one of the great features of Scribbler, image editing.  As something I wanted to put in at the very start, I was really enthusiastic to start using Apple&#8217;s Image Kit. It promised developers the chance to do very little work and still have fully-fledged image management, provided by IKImageBrowser (very good) and image editing thanks to IKImageView.  Unfortunately, the latter does not live up to the hype. After battling with it for a while and trying to justify to myself that it would suffice for version 1.0 of Scribbler, it was just way too incomplete, buggy, and damn ugly to leave in. I try to work with the idea that if it bugs me, it&#8217;s not good enough.</p>
<h3>Image Loading</h3>
<p>Let me start with IKImageView. It loads images ok, although larger images aren&#8217;t loaded nicely: they&#8217;re drawn incrementally in a <em>really</em> jarring fashion, even if you pass the view a CGImageRef that&#8217;s already in memory. &nbsp;That being said, it does handle quite a few different types of images, so we give it half a point.</p>
<h3>Image Editing</h3>
<p>The main function of such a view is not for viewing images, but for editing them within the application. Many users want to quickly change an image they&#8217;re working with and then get straight back work. &nbsp;Taking our cues from iPhoto we guess that this editing will take the form of colour adjustments rotating and cropping. If we&#8217;re feeling really generous, then we can also include a straightening tool and some zooming (although the latter is relatively superfluous unless we have some sort of pixel-level editing like iPhoto&#8217;s red-eye removal or the touch-up tool).</p>
<p>So we&#8217;ve put IKImageView in our app, and loaded an image, we then want to&nbsp;change the brightness and contrast, so we double-click on the image and open up the shared instance of the IKImageEditPanel (left).</p>
<p><a href="http://jonathandann.files.wordpress.com/2008/10/iphotohud.jpg"></a><a href="http://jonathandann.files.wordpress.com/2008/10/ikimageviewhud.jpg"><img class="alignnone size-medium wp-image-68" title="ikimageviewhud" src="http://jonathandann.files.wordpress.com/2008/10/ikimageviewhud.jpg?w=215&#038;h=300" alt="" width="215" height="300" />&nbsp;&nbsp;</a><img class="alignnone size-medium wp-image-69" title="iphotohud" src="http://jonathandann.files.wordpress.com/2008/10/iphotohud.jpg?w=200&#038;h=300" alt="" width="200" height="300" /></p>
<p>Compared to iPhoto&#8217;s panel (right), this is ridiculously ugly, has no icons, has an Aqua button at the bottom instead of a properly-rendered HUD button. &nbsp;The biggest issue: it appears off-screen <em>all the freaking time</em>. Can you imagine editing a bunch of images and having to manually drag the panel to where you want it each time you open the damn thing up? No amount of setting the frame of the window or teaching it to autosave it&#8217;s position fix this, and even if we could, the first time you run the app it&#8217;s at the bottom of the screen with most of it hidden.</p>
<p>Lastly, you just try setting a new image in the view and getting the edit panel to do anything anymore, as far as I can tell -reloadData is a no-op, it neither reloads the data for the panel, nor makes you more attractive. The only way to fix this is to close the panel, and reopen it (and then moving it so you can see the controls).</p>
<h3>Rotation</h3>
<p>If we want to rotate the image we (in code) select the IKToolModeRotate mode and we get our onscreen compass:</p>
<p><a href="http://jonathandann.files.wordpress.com/2008/10/ikimageviewrotate.jpg"><img class="alignnone size-medium wp-image-70" title="ikimageviewrotate" src="http://jonathandann.files.wordpress.com/2008/10/ikimageviewrotate.jpg?w=300&#038;h=140" alt="" width="300" height="140" /></a></p>
<p>The problem with this is two-fold: click on the image at about 3/4 the way up and the rotation compass is drawn outside the bounds of the view; secondly, the user interaction is all wrong. To rotate you click and hold ok the image and move the mouse around in a manner that&#8217;s so hard to control it&#8217;s useless. Then think about saving the image after a rotation. The view itself has no concept of a canvas, so we can&#8217;t save an angled image on a white background, and we can&#8217;t arbitrarily decide to ignore the user&#8217;s rotation setting an save an un-rotated image (rock - me - hard place). Basically the tool is broken and doesn&#8217;t provide useful functionality to users.</p>
<h3>Cropping</h3>
<p>Then we have IKToolModeCrop. &#8220;Fantastic!&#8221; I hear you cry. &#8220;No longer will my users have to open up another app to crop an image, and I won&#8217;t have to write that code&#8221;. You diligently select a region if the image, then call -setCurrentToolMode: and&#8230;.</p>
<p>Nothing. Nada. Zip.</p>
<p>Reading through the programming guide tells us that Apple have declared the mode and not implemented it (apoordesignersayswhat). So we then have to do it, but can we get the selection rectangle? Of course we can&#8217;t! So that nice crop button we made goes to waste, unless you want to subclass, track the mouse, interrupt -drawRect:, apply the crop using core image (note to all that CICrop filter is <em>not</em> a magic bullet), draw a CGImage with the cropped version, then reset the image. No thanks.  On it&#8217;s own that might be fine, but with all the other rubbish in this class you&#8217;re starting to flog a dead horse.</p>
<p>So that&#8217;s why IKImageView will *not* be making an appearance in Scribbler, and if you find it in any other apps then&#8230; well&#8230; I heard that Windows Photo Viewer Deluxe Family&nbsp;Millennium&nbsp;Media Edition is fantastic.</p>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/jonathandann.wordpress.com/63/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/jonathandann.wordpress.com/63/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/jonathandann.wordpress.com/63/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/jonathandann.wordpress.com/63/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/jonathandann.wordpress.com/63/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/jonathandann.wordpress.com/63/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/jonathandann.wordpress.com/63/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/jonathandann.wordpress.com/63/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/jonathandann.wordpress.com/63/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/jonathandann.wordpress.com/63/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=espresso-served-here.com&blog=656654&post=63&subd=jonathandann&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://espresso-served-here.com/2008/10/06/scribblers-image-editing-opensourced-part-1/feed/</wfw:commentRss>
	
		<media:content url="http://a.wordpress.com/avatar/jpdann-128.jpg" medium="image">
			<media:title type="html">jpdann</media:title>
		</media:content>

		<media:content url="http://jonathandann.files.wordpress.com/2008/10/ikimageviewhud.jpg?w=215" medium="image">
			<media:title type="html">ikimageviewhud</media:title>
		</media:content>

		<media:content url="http://jonathandann.files.wordpress.com/2008/10/iphotohud.jpg?w=200" medium="image">
			<media:title type="html">iphotohud</media:title>
		</media:content>

		<media:content url="http://jonathandann.files.wordpress.com/2008/10/ikimageviewrotate.jpg?w=300" medium="image">
			<media:title type="html">ikimageviewrotate</media:title>
		</media:content>
	</item>
		<item>
		<title>Scribbler</title>
		<link>http://espresso-served-here.com/2008/08/18/scribbler/</link>
		<comments>http://espresso-served-here.com/2008/08/18/scribbler/#comments</comments>
		<pubDate>Mon, 18 Aug 2008 21:07:50 +0000</pubDate>
		<dc:creator>Jonathan Dann</dc:creator>
		
		<category><![CDATA[Scribbler]]></category>

		<guid isPermaLink="false">http://jonathandann.wordpress.com/?p=61</guid>
		<description><![CDATA[I&#8217;ve been putting off writing about Scribbler for a while because it&#8217;s really difficult to put into words properly the ideas I want to convey.  I&#8217;ve become very attached to my little application since its early inception, not only because of the time I&#8217;ve spent, but also for how it has helped me find [...]]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>I&#8217;ve been putting off writing about Scribbler for a while because it&#8217;s really difficult to put into words properly the ideas I want to convey.  I&#8217;ve become very attached to my little application since its early inception, not only because of the time I&#8217;ve spent, but also for how it has helped me find my creative side.  The only reason I haven&#8217;t put screenshots up yet is because I&#8217;ve been talking with a designer about making the icons, and developing the site and brand of Espresso Served Here (need to catch myself each time I write &#8217;severed&#8217;).  They guys I&#8217;ve got to do that part of the app, which is completely beyond me, are really talented and I&#8217;m getting very excited about having all the artwork finished.  As soon as it is ready, I&#8217;ll start posting screenshots.</p>
<p>It was writing my MSc thesis that gave me the desire to write a new LaTeX editor.  I decided that I needed an app that understands how people work with large documents.  Specifically, people work on <em>projects</em>.  Each of these projects can contain many source files: LaTeX source itself, images, BibTeX files and PDFs; and Scribbler knows how to handle them all.  You organise your project in Scribbler just like you&#8217;d organise your iTunes or iPhoto library, while Scribbler takes care of files on your hard drive.  You can make new LaTeX and BibTeX files within Scribbler itself, or simply drag-and-drop a file from any other program to add it to your project.</p>
<p>When writing, having Scribbler keep track of your project soon shows its advantages over keeping each document separate.  To include an image in your LaTeX source, just drop it on the text and Scribbler will write the code for you. You no longer need to care where the image file is in the filesystem, why should you?</p>
<p>Another plus to this approach comes when writing labels and citations.  As Scribbler looks after your project it knows all the \label{} keys you&#8217;ve written and will suggest them for you when you type \ref{} (for the LaTeX-uninitiated, that makes cross-referencing very, very easy).  The same works for all your citations.</p>
<p>One of the things I always found myself doing when writing in LaTeX was editing my images so I decided to add some image-editing functionality for common tasks.  Scribbler allows you to re-colour, crop, rotate and apply filters to your images, so you only need to open up Pixelmator or Photoshop when you really need to.</p>
<p>I&#8217;m really looking forward to showing off Scribbler to you all, and when you use it I&#8217;m sure you&#8217;ll agree that creating beautiful LaTeX documents can be done in a beautiful environment. After all, one of the reasons we all use LaTeX is because we care about how things look.  I&#8217;m working really hard on some features that will blow you away, and I&#8217;ll keep-on making it better.</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/jonathandann.wordpress.com/61/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/jonathandann.wordpress.com/61/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/jonathandann.wordpress.com/61/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/jonathandann.wordpress.com/61/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/jonathandann.wordpress.com/61/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/jonathandann.wordpress.com/61/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/jonathandann.wordpress.com/61/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/jonathandann.wordpress.com/61/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/jonathandann.wordpress.com/61/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/jonathandann.wordpress.com/61/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/jonathandann.wordpress.com/61/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/jonathandann.wordpress.com/61/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=espresso-served-here.com&blog=656654&post=61&subd=jonathandann&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://espresso-served-here.com/2008/08/18/scribbler/feed/</wfw:commentRss>
	
		<media:content url="http://a.wordpress.com/avatar/jpdann-128.jpg" medium="image">
			<media:title type="html">jpdann</media:title>
		</media:content>
	</item>
		<item>
		<title>NSViewController Late Night Cocoa Episode</title>
		<link>http://espresso-served-here.com/2008/07/04/nsviewcontroller-late-night-cocoa-episode/</link>
		<comments>http://espresso-served-here.com/2008/07/04/nsviewcontroller-late-night-cocoa-episode/#comments</comments>
		<pubDate>Fri, 04 Jul 2008 18:26:12 +0000</pubDate>
		<dc:creator>Jonathan Dann</dc:creator>
		
		<category><![CDATA[Cocoa]]></category>

		<guid isPermaLink="false">http://jonathandann.wordpress.com/?p=59</guid>
		<description><![CDATA[You may have seen the articles Cathy Shive and I wrote on her blog about NSViewController and how to get it working well in your apps. Well now you can hear us talk about it! Scotty of the Mac Developer Network kindly had us both on for an episode of Late Night Cocoa, which you [...]]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>You may have seen the <a href="http://katidev.com/blog/2008/04/09/nsviewcontroller-the-new-c-in-mvc-pt-1-of-3/">articles</a> <a href="http://katidev.com">Cathy Shive</a> and I wrote on her blog about <code>NSViewController</code> and how to get it working well in your apps. Well now you can hear us talk about it! Scotty of the <a href="http://www.macdevnet.com">Mac Developer Network</a> kindly had us both on for an episode of Late Night Cocoa, which you can download from iTunes <a href="http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewPodcast?id=213023580">here</a>. It&#8217;ll be on the MacDevNet site too.</p>
<p>I talked about my preferences window system, I&#8217;ll have to wait a few weeks until I can put it up here with a sample app. Work will let up in August and I&#8217;ll have time to iron out a few bugs.</p>
<p>Thanks again to Scotty for having me on the episode, it was awesome!</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/jonathandann.wordpress.com/59/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/jonathandann.wordpress.com/59/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/jonathandann.wordpress.com/59/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/jonathandann.wordpress.com/59/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/jonathandann.wordpress.com/59/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/jonathandann.wordpress.com/59/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/jonathandann.wordpress.com/59/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/jonathandann.wordpress.com/59/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/jonathandann.wordpress.com/59/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/jonathandann.wordpress.com/59/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/jonathandann.wordpress.com/59/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/jonathandann.wordpress.com/59/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=espresso-served-here.com&blog=656654&post=59&subd=jonathandann&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://espresso-served-here.com/2008/07/04/nsviewcontroller-late-night-cocoa-episode/feed/</wfw:commentRss>
	
		<media:content url="http://a.wordpress.com/avatar/jpdann-128.jpg" medium="image">
			<media:title type="html">jpdann</media:title>
		</media:content>
	</item>
		<item>
		<title>Site redesign imminent</title>
		<link>http://espresso-served-here.com/2008/06/27/site-redesign-imminent/</link>
		<comments>http://espresso-served-here.com/2008/06/27/site-redesign-imminent/#comments</comments>
		<pubDate>Fri, 27 Jun 2008 10:11:58 +0000</pubDate>
		<dc:creator>Jonathan Dann</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://jonathandann.wordpress.com/?p=58</guid>
		<description><![CDATA[I&#8217;m slowly getting everything ready to get espresso served here off the ground.  Part of that has been talking to a couple of graphic designers about site design and icon design.  After all, I&#8217;m a programmer and I can&#8217;t draw for the life of me.  Photoshop to me is an app that just puts loads [...]]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>I&#8217;m slowly getting everything ready to get espresso served here off the ground.  Part of that has been talking to a couple of graphic designers about site design and icon design.  After all, I&#8217;m a programmer and I can&#8217;t draw for the life of me.  Photoshop to me is an app that just puts loads of stuff all over my system.</p>
<p>Hopefully sometime in August things will start to take shape.</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/jonathandann.wordpress.com/58/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/jonathandann.wordpress.com/58/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/jonathandann.wordpress.com/58/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/jonathandann.wordpress.com/58/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/jonathandann.wordpress.com/58/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/jonathandann.wordpress.com/58/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/jonathandann.wordpress.com/58/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/jonathandann.wordpress.com/58/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/jonathandann.wordpress.com/58/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/jonathandann.wordpress.com/58/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/jonathandann.wordpress.com/58/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/jonathandann.wordpress.com/58/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=espresso-served-here.com&blog=656654&post=58&subd=jonathandann&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://espresso-served-here.com/2008/06/27/site-redesign-imminent/feed/</wfw:commentRss>
	
		<media:content url="http://a.wordpress.com/avatar/jpdann-128.jpg" medium="image">
			<media:title type="html">jpdann</media:title>
		</media:content>
	</item>
		<item>
		<title>Creating iTunes Scrollers</title>
		<link>http://espresso-served-here.com/2008/06/18/creating-itunes-scrollers/</link>
		<comments>http://espresso-served-here.com/2008/06/18/creating-itunes-scrollers/#comments</comments>
		<pubDate>Wed, 18 Jun 2008 09:02:11 +0000</pubDate>
		<dc:creator>Jonathan Dann</dc:creator>
		
		<category><![CDATA[Apple]]></category>

		<category><![CDATA[Cocoa]]></category>

		<category><![CDATA[How-to]]></category>

		<category><![CDATA[Mac Stuff]]></category>

		<guid isPermaLink="false">http://espresso-served-here.com/?p=55</guid>
		<description><![CDATA[Apple introduced new scrollers in iTunes 7 and then moved on to give us the HUD, which many developers want their own scrollers for too. In Leopard, many of us thought that these would come in a nice, shiny box; but as they didn’t we’re all forced to roll our own. The common method is [...]]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>Apple introduced new scrollers in iTunes 7 and then moved on to give us the HUD, which many developers want their own scrollers for too. In Leopard, many of us thought that these would come in a nice, shiny box; but as they didn’t we’re all forced to roll our own. The common method is to draw all the components in Photoshop and then make a composite image when subclassing, but now with <code>NSGradient</code> and some nice additions to <code>NSBezierPath</code> all these have become quite easy to do, even for those with little artistic ability.<br />
<span id="more-55"></span><br />
My first attempt was to do all of this using <code>NSAffineTransform</code>s, but without being able to mirror a bezier path, or define the the centre of rotation of a path or NSRect it becomes a loathsome task of tweaking and it just never quite works. So all the elements are drawn separately in this example, which is more code, but turned out to be far easier to code.</p>
<p>The important point to realise when subclassing <code>NSScroller</code> is that Apple’s documentation is out of date and some of the methods that we immediately assume we can subclass aren’t called. You can read the details in this thread on the cocoa-dev mailing list. You end up having to also implement -drawRect: and calling these documented (but unused) methods yourself.</p>
<p>The <code>NSScroller</code> consists first of the <code>NSScrollerKnobSlot</code>, the track on which the <code>NSScrollerKnob</code> itself lies. There are 2 (normally) buttons: in the vertical case the up arrow defined as the <code>NSScrollerDecrementLine</code> and the down arrow is the <code>NSScrollerIncrementLine</code>. I’ve implemented a method that checks to see which configuration the user has the scrollbars in, whether they are at the top and bottom or both together at the bottom. It does this by examining the <code>AppleScrollBarVariant</code> key in the global user defaults. If this (AFAICT undocumented) key changes then it calculated which configuration the user has selected. So when using these scrollers, the user can switch the configuration in System Preferences and the change can be seen. In the mode with both together at the bottom of the bar, there is also a top ‘socket’ that’s drawn.</p>
<p>The scrollbars also support horizontal scrolling, and I found that instantiating the vertical scroller without a frame works fine, but the only way to get a horizontal scroller is by using <code>-initWithFrame:</code>. I can only assume that apple are doing something under-the-hood for horizontal ones.</p>
<p>The final hiccup comes when you can to support the last 20% of users that use the scrollbar variant with both buttons at both ends. This is done in terminal and thankfully requires a restart of any program in which you want it to take effect. Problems come when trying to support this case when you want to know which button has been hit so you can draw it highlighted. The documented stuff from apple returns the same value whether the decrement line one end or the other is clicked! Making this impossible without tracking the mouse yourself, and I haven’t had the time to fix that. If I were using a program that didn’t respect my settings (I’m looking at you General Electric), then I’d get angry, so I included an <code>NSScrollView</code> subclass that checks for this case and just uses Apple’s scrollers if the user has selected it.</p>
<p>I think they’re pretty close to the iTunes ones, but mine are resoution-independent, but only Apple can give the perfect solution. You can download the XCode (3.1) project and sample app (10.5-only) <a href="http://rapidshare.com/files/129482909/Scroll_Bar.zip.html">here</a>.</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/jonathandann.wordpress.com/55/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/jonathandann.wordpress.com/55/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/jonathandann.wordpress.com/55/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/jonathandann.wordpress.com/55/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/jonathandann.wordpress.com/55/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/jonathandann.wordpress.com/55/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/jonathandann.wordpress.com/55/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/jonathandann.wordpress.com/55/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/jonathandann.wordpress.com/55/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/jonathandann.wordpress.com/55/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/jonathandann.wordpress.com/55/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/jonathandann.wordpress.com/55/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=espresso-served-here.com&blog=656654&post=55&subd=jonathandann&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://espresso-served-here.com/2008/06/18/creating-itunes-scrollers/feed/</wfw:commentRss>
	
		<media:content url="http://a.wordpress.com/avatar/jpdann-128.jpg" medium="image">
			<media:title type="html">jpdann</media:title>
		</media:content>
	</item>
		<item>
		<title>New Site Address www.espresso-served-here.com</title>
		<link>http://espresso-served-here.com/2008/06/15/new-site-address-wwwespresso-served-herecom/</link>
		<comments>http://espresso-served-here.com/2008/06/15/new-site-address-wwwespresso-served-herecom/#comments</comments>
		<pubDate>Sun, 15 Jun 2008 12:54:24 +0000</pubDate>
		<dc:creator>Jonathan Dann</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://jonathandann.wordpress.com/?p=54</guid>
		<description><![CDATA[Hi all,
Just thought I&#8217;d let you know that you can now reach this website at www.espresso-served-here.com.  I&#8217;m going to slowly be making changes to the site as I develop the brand for my software company.  All previous links around the web to http://jonathandann.wordpress.com will still work though.
Jon
       [...]]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>Hi all,</p>
<p>Just thought I&#8217;d let you know that you can now reach this website at <a href="http://www.espresso-served-here.com">www.espresso-served-here.com</a>.  I&#8217;m going to slowly be making changes to the site as I develop the brand for my software company.  All previous links around the web to <a href="http://jonathandann.wordpress.com">http://jonathandann.wordpress.com</a> will still work though.</p>
<p>Jon</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/jonathandann.wordpress.com/54/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/jonathandann.wordpress.com/54/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/jonathandann.wordpress.com/54/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/jonathandann.wordpress.com/54/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/jonathandann.wordpress.com/54/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/jonathandann.wordpress.com/54/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/jonathandann.wordpress.com/54/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/jonathandann.wordpress.com/54/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/jonathandann.wordpress.com/54/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/jonathandann.wordpress.com/54/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/jonathandann.wordpress.com/54/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/jonathandann.wordpress.com/54/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=espresso-served-here.com&blog=656654&post=54&subd=jonathandann&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://espresso-served-here.com/2008/06/15/new-site-address-wwwespresso-served-herecom/feed/</wfw:commentRss>
	
		<media:content url="http://a.wordpress.com/avatar/jpdann-128.jpg" medium="image">
			<media:title type="html">jpdann</media:title>
		</media:content>
	</item>
		<item>
		<title>NSTreeController and Core Data, Sorted.</title>
		<link>http://espresso-served-here.com/2008/05/13/nstreecontroller-and-core-data-sorted/</link>
		<comments>http://espresso-served-here.com/2008/05/13/nstreecontroller-and-core-data-sorted/#comments</comments>
		<pubDate>Tue, 13 May 2008 20:00:08 +0000</pubDate>
		<dc:creator>Jonathan Dann</dc:creator>
		
		<category><![CDATA[Apple]]></category>

		<category><![CDATA[Cocoa]]></category>

		<category><![CDATA[Mac Stuff]]></category>

		<guid isPermaLink="false">http://jonathandann.wordpress.com/?p=49</guid>
		<description><![CDATA[Having recently taken the plunge into Core Data I decided it was time to rip out all the model code from my current application and replace it with a Core Data version. After about a day I had my app up and running again but with one huge problem, the content of my NSOutlineView always [...]]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>Having recently taken the plunge into Core Data I decided it was time to rip out all the model code from my current application and replace it with a Core Data version. After about a day I had my app up and running again but with one <strong>huge</strong> problem, the content of my <code>NSOutlineView</code> always appeared in a random order. Such is the problem with Core Data that <code>NSManagedObject</code>s store their to-many relationships in an <code>NSSet</code>, <strong>not</strong> an NSArray, which is unordered. So when your <code>NSTreeController</code> tries to display its data it appears in a random order.</p>
<p>This is not nice, imagine if the playlists in your iTunes library always changed their order? It gets even worse if your user wants to use drag and drop. In this case <strong>they</strong> decide the order, and they&#8217;d probably want it to stay that way.<br />
<span id="more-49"></span><br />
The question of how to do this comes up so many times, along with the question of how to use multiple classes for nodes in the tree. Well, here&#8217;s your answer, and I&#8217;ve made <a href="http://www.mediafire.com/?wxex0yecjz3">a sample Xcode project</a> to demonstrate. (<a href="http://rapidshare.com/files/120385221/SortedTree.zip.html">alt link</a>)</p>
<p><strong>The Model</strong><br />
<a href="http://jonathandann.files.wordpress.com/2008/05/abstracttreemodel.png"><img class="alignleft size-medium wp-image-50" src="http://jonathandann.files.wordpress.com/2008/05/abstracttreemodel.png?w=300&#038;h=278" alt="Multiple-entity model that can be used in an NSTreeController" width="300" height="278" /></a><br />
The model I&#8217;ve set up has a single abstract entity called <code>TreeNode</code> which has a boolean <code>isLeaf</code> attribute, an <code>NSString *displayName</code>, and an <code>NSNumber *sortIndex</code> (which is the one of the main reasons I&#8217;m writing this). It also has a to-many <code>children</code> relationship that has an inverse to-one <code>parent</code> relationship. Then there are two other entities: <code>Group</code> and <code>Leaf</code> both of which inherit from <code>TreeNode</code>. The <code>Group</code> entity has a few other boolean attributes that make writing an <code>NSOutlineView</code> delegate class really simple. The <code>Leaf</code> doesn&#8217;t have any of its own attributes yet, that&#8217;s for you guys to ponder.</p>
<p><strong>The Tree Controller</strong><br />
The tree controller is our custom <code>ESTreeController</code> class. It is set up in the nib and has only 2 bindings, those of the <code>@"managedObjectContext"</code> and the <code>@"sortDescriptors"</code>. It&#8217;s (obviously) operating in entity mode which is set to our abstract entity of <code>TreeNode</code>, and the children and leaf keypaths are set to attributes of the model: the oh-so-inventively named, <code>children</code> and <code>isLeaf</code>.</p>
<p><strong>The Outline View</strong><br />
The outline view is also a subclass of <code>NSOutlineView</code>, but only to support expanded-state saving. The <code>NSTableColumn</code> in the view has only a <strong>single binding</strong>: the <code>@"value"</code> binding is bound to the tree controller&#8217;s <code>@"arrangedObjects.displayName"</code>.</p>
<p>The rest of the UI only exists for demonstration purposes (so yeah its ugly), so we can see which <code>ESTreeController</code> method is invoked by the class&#8217; standard actions.</p>
<p><strong>The Sort Index</strong><br />
The <code>@"sortIndex"</code> attribute is the key to keeping the tree sorted, and it&#8217;s persistent allowing the sort to be maintained across sessions. It is simply an unsigned integer. Not having to apply a unique value to each node in the tree makes this a whole lot easier, nodes only require to have a unique number that defines their location within their own group. All we have to do is keep the sort index as the last index of the corresponding <code>NSTreeNode</code>&#8217;s index path.</p>
<p>There are 3 places this must be updated: on insertion, removal and movement. Functionality for this is provided by a single method <code>-updateSortOrderOfModelObjects</code>, which takes the last index of the tree node&#8217;s index path and sets it as the sort index of the representedObject. Simple. We make sure this is done correctly by overriding:</p>
<p><code>- (void)insertObject:(id)object atArrangedObjectIndexPath:(NSIndexPath *)indexPath;<br />
- (void)insertObjects:(NSArray *)objects atArrangedObjectIndexPaths:(NSArray *)indexPaths;<br />
- (void)removeObjectAtArrangedObjectIndexPath:(NSIndexPath *)indexPath;<br />
- (void)removeObjectsAtArrangedObjectIndexPaths:(NSArray *)indexPaths;<br />
- (void)moveNode:(NSTreeNode *)node toIndexPath:(NSIndexPath *)indexPath;<br />
- (void)moveNodes:(NSArray *)nodes toIndexPath:(NSIndexPath *)startingIndexPath;</code></p>
<p>The tree controller is then bound to an <code>NSSortDescriptor</code> with keypath <code>@"sortIndex"</code>.</p>
<p><strong>Points To Note</strong><br />
<code>NSTreeController</code>&#8217;s <code>-add:</code>, <code>-addChild:</code>, <code>-insert:</code>, <code>-insertChild:</code> and <code>remove:</code> all call the plural forms of the above methods, and proceed to try and add a <code>TreeNode</code> entity to the tree. This can cause problems in the outline view&#8217;s delegate as this expects either <code>Leaf</code> or <code>Group</code> entities (try them in the sample project!), overriding these is usually necessary, which is what&#8217;s done with the <code>-newLeaf:</code> and <code>-newGroup:</code> actions.</p>
<p>The project also includes a bunch of categories that make life so much easier when working with <code>NSIndexPath</code>, <code>NSTreeController</code> and <code>NSTreeNode</code>.</p>
<p><strong>Drag and Drop</strong><br />
<a href="http://jonathandann.files.wordpress.com/2008/05/sortedtreedraganddrop1.png"><img class="alignleft size-medium wp-image-52" src="http://jonathandann.files.wordpress.com/2008/05/sortedtreedraganddrop1.png?w=211&#038;h=183" alt="" width="211" height="183" /></a></p>
<p><code>-outlineView:writeItems: toPasteboard:</code><br />
Write the index paths of the dragged items to the pasteboard as <code>NSData</code> objects.</p>
<p><code>-outlineView:validateDrop:proposedItem:proposedChildIndex:</code><br />
Determine if the drop location is valid, not allowing drops on a leaf node (for the case of iTunes playlists being allowed to do this wouldn&#8217;t make sense), or if one of the dragged nodes is a group and the proposed location is on of its own descendants.</p>
<p><code>-outlineView:acceptDrop:item:childIndex:</code><br />
Accept the drop and move the nodes for the dragged index paths. The edge case is is when dropping on the root of the tree. In this case the proposed parent of the drop location is <code>nil</code> and generating the index path for insertion therefore returns <code>nil</code>. We check for a <code>nil</code> parent and create a blank <code>NSIndexPath</code> if this is the case.</p>
<p><strong>Summary</strong><br />
With the (relatively small) amount of code we have an <code>NSOutlineView</code> powered by Core Data and <code>NSTreeController</code> with some great features:<br />
1) Drag and drop!<br />
2) Persistent state saving!<br />
3) Multiple entities in the tree (extend <em>ad infinitum</em>)<br />
4) Sorting!<br />
5) Some indispensable categories that make these classes <em>so</em> much easier to use (<code>-treeNodeForObject:</code> is great).</p>
<p><a href="http://www.mediafire.com/?wxex0yecjz3">Download</a> the Xcode project (requires Mac OS X 10.5)</p>
<p><strong>Update</strong><br />
I&#8217;ve realised that there is some repeated code in the extensions, for my own work I&#8217;ve replaced the implementation of <code>-flattenedContent</code> with this:<br />
<code>- (NSArray *)flattenedContent;<br />
{<br />
return [[self flattenedNodes] valueForKey:@&#8221;representedObject&#8221;];<br />
}</code></p>
<p>There are also these I&#8217;ve found useful:<br />
<code>- (void)setSelectedNode:(NSTreeNode *)node;<br />
{<br />
[self setSelectionIndexPath:[node indexPath]];<br />
}</code></p>
<p>- (void)setSelectedObject:(id)object;<br />
{<br />
[self setSelectedNode:[self treeNodeForObject:object]];<br />
}</p>
<p>- (NSIndexPath *)indexPathToObject:(id)object;<br />
{<br />
return [[self treeNodeForObject:object] indexPath];<br />
}</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/jonathandann.wordpress.com/49/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/jonathandann.wordpress.com/49/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/jonathandann.wordpress.com/49/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/jonathandann.wordpress.com/49/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/jonathandann.wordpress.com/49/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/jonathandann.wordpress.com/49/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/jonathandann.wordpress.com/49/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/jonathandann.wordpress.com/49/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/jonathandann.wordpress.com/49/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/jonathandann.wordpress.com/49/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/jonathandann.wordpress.com/49/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/jonathandann.wordpress.com/49/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=espresso-served-here.com&blog=656654&post=49&subd=jonathandann&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://espresso-served-here.com/2008/05/13/nstreecontroller-and-core-data-sorted/feed/</wfw:commentRss>
	
		<media:content url="http://a.wordpress.com/avatar/jpdann-128.jpg" medium="image">
			<media:title type="html">jpdann</media:title>
		</media:content>

		<media:content url="http://jonathandann.files.wordpress.com/2008/05/abstracttreemodel.png?w=300" medium="image">
			<media:title type="html">Multiple-entity model that can be used in an NSTreeController</media:title>
		</media:content>

		<media:content url="http://jonathandann.files.wordpress.com/2008/05/sortedtreedraganddrop1.png?w=211" medium="image" />
	</item>
		<item>
		<title>I&#8217;m on the Mac Developer Network!</title>
		<link>http://espresso-served-here.com/2008/04/22/im-on-the-mac-developer-network/</link>
		<comments>http://espresso-served-here.com/2008/04/22/im-on-the-mac-developer-network/#comments</comments>
		<pubDate>Tue, 22 Apr 2008 21:09:10 +0000</pubDate>
		<dc:creator>Jonathan Dann</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://jonathandann.wordpress.com/?p=48</guid>
		<description><![CDATA[I recently did a post on NSViewController with Cathy Shive, and I realised today that  the post was link to on the MacDevNet (Thanks Scotty!).  I&#8217;m actually feeling like a developer for once.
Welcome all, nice to see you.  Sit down, have a cup of coffee (espresso is served here), and I&#8217;ll write [...]]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>I recently did a <a href="http://katidev.com/blog/2008/04/17/nsviewcontroller-the-new-c-in-mvc-pt-2-of-3/">post</a> on NSViewController with <a href="http://katidev.com/blog/">Cathy Shive</a>, and I realised today that  the post was link to on the <a href="http://www.macdevnet.com/">MacDevNet</a> (Thanks <a href="http://www.mamooba.com/">Scotty</a>!).  I&#8217;m actually feeling like a developer for once.</p>
<p>Welcome all, nice to see you.  Sit down, have a cup of coffee (espresso is served here), and I&#8217;ll write some more in a bit.</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/jonathandann.wordpress.com/48/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/jonathandann.wordpress.com/48/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/jonathandann.wordpress.com/48/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/jonathandann.wordpress.com/48/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/jonathandann.wordpress.com/48/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/jonathandann.wordpress.com/48/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/jonathandann.wordpress.com/48/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/jonathandann.wordpress.com/48/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/jonathandann.wordpress.com/48/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/jonathandann.wordpress.com/48/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/jonathandann.wordpress.com/48/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/jonathandann.wordpress.com/48/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=espresso-served-here.com&blog=656654&post=48&subd=jonathandann&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://espresso-served-here.com/2008/04/22/im-on-the-mac-developer-network/feed/</wfw:commentRss>
	
		<media:content url="http://a.wordpress.com/avatar/jpdann-128.jpg" medium="image">
			<media:title type="html">jpdann</media:title>
		</media:content>
	</item>
		<item>
		<title>Using NSTreeController</title>
		<link>http://espresso-served-here.com/2008/04/06/using-nstreecontroller/</link>
		<comments>http://espresso-served-here.com/2008/04/06/using-nstreecontroller/#comments</comments>
		<pubDate>Sun, 06 Apr 2008 13:32:45 +0000</pubDate>
		<dc:creator>Jonathan Dann</dc:creator>
		
		<category><![CDATA[Apple]]></category>

		<category><![CDATA[Cocoa]]></category>

		<guid isPermaLink="false">http://jonathandann.wordpress.com/?p=46</guid>
		<description><![CDATA[A common UI concept in Mac development is that of the source list, which everybody knows from iTunes, iPhoto, etc. To do this, NSTreeController can come in particular handy, although it can come with a bit of a headache as the API is somewhat lacking and a proper model is essential to making this easy [...]]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>A common UI concept in Mac development is that of the source list, which everybody knows from iTunes, iPhoto, etc. To do this, NSTreeController can come in particular handy, although it can come with a bit of a headache as the API is somewhat lacking and a proper model is essential to making this easy to use.  I&#8217;m going to go over the solution that&#8217;s worked for me.<br />
<span id="more-46"></span><br />
The starting point is the design of the model, we&#8217;ll begin by constructing a &#8216;node&#8217; class that will represent a single item in our tree.  A node will need a few properties to work with the tree controller well.  The first is a &#8216;children&#8217; property that&#8217;ll be an NSMutableArray intended to hold a series of our nodes.  The second is a BOOL, &#8216;isLeaf&#8217; that we&#8217;ll toggle depending on whether we want the node to have the possibility of children or not.  Think of leaves as files and the opposite (I&#8217;ll call them groups) as folders, if you like.</p>
<p>Here&#8217;s how the code will look:</p>
<pre name="code" class="cpp">
@interface ESNode : NSObject  {
 @protected
 ESNode *_parent;
 NSString *_nodeName;
 NSMutableArray *_children;
 BOOL _isLeaf;
}
@property(copy) NSString *nodeName;
@property(copy) NSMutableArray *children;
@property(assign) ESNode *parent;
@property(assign) BOOL isLeaf;
</pre>
<p>The problem is that objective-c 2.0 properties don&#8217;t have a mutableCopy keyword, so we&#8217;ll have to add the accessors ourselves, the isLeaf setter will do some magic too.</p>
<pre name="code" class="cpp">

@synthesize nodeName = _nodeName;
@synthesize parent = _parent;
@dynamic isLeaf;
@dynamic children;

- (void)setIsLeaf:(BOOL)flag;
{
 _isLeaf = flag;
 if (_isLeaf)
 	self.children = [NSMutableArray arrayWithObject:self];
 else
 	self.children = [NSMutableArray array];
}
- (BOOL)isLeaf;
{
 return _isLeaf;
}

- (NSMutableArray *)children;
{
 return _children;
}

- (void)setChildren:(NSMutableArray *)newChildren;
{
 if (_children == newChildren)
 	return;
 [_children release];
 _children = [newChildren mutableCopy];
}

- (NSUInteger)countOfChildren;
{
 if (self.isLeaf)
 	return 0;
 return [self.children count];
}

- (void)insertObject:(id)object inChildrenAtIndex:(NSUInteger)index;
{
 if (self.isLeaf)
 	return;
 [self.children insertObject:object atIndex:index];
}

- (void)removeObjectFromChildrenAtIndex:(NSUInteger)index;
{
 if (self.isLeaf)
 	return;
 [self.children removeObjectAtIndex:index];
}

- (id)objectInChildrenAtIndex:(NSUInteger)index;
{
 if (self.isLeaf)
 	return nil;
 return [self.children objectAtIndex:index];
}

- (void)replaceObjectInChildrenAtIndex:(NSUInteger)index withObject:(id)object;
{
 if (self.isLeaf)
 	return;
 [self.children replaceObjectAtIndex:index withObject:object];
}
</pre>
<p>So we have a typical mutable array accessor and all the indexed accessors that allow you to place nodes anywhere in the children array. If you need to see more about why these are good to put in, then read the documentation on <a href="http://developer.apple.com/documentation/Cocoa/Conceptual/KeyValueCoding/Concepts/AccessorConventions.html#//apple_ref/doc/uid/20002174">Key-Value Coding</a>.  We&#8217;ve also included a &#8216;parent&#8217; property that is non-retained so all nodes can know about their parent object and a &#8216;nodeName&#8217; which I commonly use to identify my nodes in the tree.  The &#8216;isLeaf&#8217; accessor will create either an empty mutable array ready for some children nodes or an array containing &#8217;self&#8217; if &#8217;self&#8217; has isLeaf set to YES.  The only caveat here is is you adopt the NSCopying or NSCoding protocols then you have to set isLeaf <strong>before</strong> setting the children array.</p>
<p>In the implementation of our ESNode class we&#8217;ll include a few useful methods for searching and filtering the tree:</p>
<pre name="code" class="cpp">

- (NSArray *)descendants;
{
 NSMutableArray *descendantsArray = [NSMutableArray array];

for (ESNode *node in self.children) {
 	[descendantsArray addObject:node];
 	if (!node.isLeaf)
 		[descendantsArray addObjectsFromArray:[node descendants]];
 }

return [[descendantsArray copy] autorelease]; // return immutable
}

- (NSArray *)leafDescendants;
{
 NSMutableArray *leafsArray = [NSMutableArray array];

for (ESNode *node in self.children) {
 	if (node.isLeaf)
 		[leafsArray addObject:node];
 	else
 		[leafsArray addObjectsFromArray:[node leafDescendants]];
 }

return [[leafsArray copy] autorelease]; // return immutable
}

- (NSArray *)groupDescendants;
{
 NSMutableArray *groupsArray = [NSMutableArray array];

for (ESNode *node in self.children) {
 	if (!node.isLeaf) {
 		[groupsArray addObject:node];
 		[groupsArray addObject:[node groupDescendants]];
 	}
 }

return [[groupsArray copy] autorelease]; // return immutable
}
</pre>
<p>These allow us to get any of our node objects and construct an array of their descendants down to the bottom of the tree.  The final methods are our -init, -copyWithZone:, -initWithCoder: and -encodeWithCoder:</p>
<pre name="code" class="cpp">

- (id)init;
{
 if (![super init])
 	return nil;
 self.nodeName = [NSString stringWithString:@&quot;untitled&quot;];
 self.isLeaf = NO; // this will also set the children array to [NSArray array].
 self.parent = nil;
 return self;
}

- (id)initGroup;
{
 return [self init];
}

- (id)initLeaf;
{
 if (![self init])
 	return nil;
 self.isLeaf = YES; // this will set the children array to an NSArray containing self.
 return self;
}

- (id)copyWithZone:(NSZone *)zone;
{
 ESNode *copy = [[[self class] allocWithZone:zone] init];
 if (!copy)
 	return nil;
 copy.nodeName = self.nodeName;
 copy.isLeaf = self.isLeaf;
 if (!self.isLeaf)
 	copy.children = self.children;
 return copy;
}

- (NSArray *)keysForEncoding;
{
 return [NSArray arrayWithObjects:@&quot;isLeaf&quot;,@&quot;children&quot;,@&quot;parent&quot;,nil];
}

-(id)initWithCoder:(NSCoder *)coder;
{
 if (![self init])
 	return nil;

for (NSString *key in self.keysForEncoding) {
 	[self setValue:[coder decodeObjectForKey:key] forKey:key];
 }

return self;
}

- (void)encodeWithCoder:(NSCoder *)coder;
{
 for (NSString *key in self.keysForEncoding)
 	[coder encodeObject:[self valueForKey:key] forKey:key];
}

- (void)setNilValueForKey:(NSString *)key;
{
 if ([key isEqualToString:@&quot;isLeaf&quot;])
 	self.isLeaf = NO;
 else
 	[super setNilValueForKey:key];
}
</pre>
<p>The nice thing about doing your NSCoding in this way is that concrete subclasses of ESNode can simply implement a single method to adopt NSCoding:</p>
<pre name="code" class="cpp">

- (NSArray *)keysForEncoding;
{
 return [[super keysForEncoding] arrayByAddingObjectsFromArray:[NSArray arrayWithObjects:@&quot;key1&quot;,@&quot;key2&quot;,@&quot;key3&quot;,nil]];
}
</pre>
<p>Ok, so we have our node class and now we can use it with NSTreeController.  In your AppController class (this may be anything, for example an NSDocument subclass) create an NSMutableArray instance variable and call it treeContent. This array will hold instances of our ESNodes (or a subclass of ESNode if you want to extend it).</p>
<p>In the next part I&#8217;m assuming you&#8217;re used to Interface Builder and your app controller is either the File&#8217;s Owner of your nib, or is instantiated in the nib. Create an instance of NSTreeController in Interface Builder and bind the @&#8221;contentArray&#8221; to the keypath to the &#8216;treeContent&#8217; array.  This tree controller will be powering an NSOutlineView.  Click through on your NSOutlineView instance until you can edit the bindings for the NSTableColumn and bind the @&#8221;value&#8221; of the table column in the outline view to the tree controller&#8217;s @&#8221;arrangedObjects&#8221; controller key with the keypath @&#8221;nodeName&#8221;. You&#8217;ll also have to set the tree controller&#8217;s children keypath to @&#8221;children&#8221; and the leaf keypath to @&#8221;isLeaf&#8221;.</p>
<p>In the -init method of the class that contains your treeContent array, create a bunch of ESNodes and fill the tree, set some children and whatnot and all of this will appear in your NSOutlineView when you run the program.</p>
<p>So once your tree is created, whether by you in the -init method or by your user at runtime, you&#8217;ll most likely want to search through the tree from time to time. In your app controller you can generate a &#8216;flat&#8217; version of the tree with the following method:</p>
<pre name="code" class="cpp">

- (NSArray *)discreteProjectContent;
{
 NSMutableArray *discreteProjectContent = [NSMutableArray array];
 for (ESSourceNode *item in self.projectContent) {
 	[discreteProjectContent addObject:item];
 	if (!item.isLeaf)
 		[discreteProjectContent addObjectsFromArray:[item descendants]];
 }
 return [[discreteProjectContent copy] autorelease]; // return immutable
}
</pre>
<p>Try it out. You get all the nodes in the tree in one long array, you can then search through the array for the item you want, for example by nodeName, and get a pointer to any item in the tree.</p>
<p>For really great additions to NSTreeController I point you in the direction of <a href="http://www.wilshipley.com/blog/2006/04/pimp-my-code-part-10-whining-about.html">Wil Shipley&#8217;s blog</a>, which has a great few NSTreeController extensions. When used in conjunction with the code above, you can get a pointer to an item in the tree and call his -indexPathToObject: method and you can then select any object in the tree.</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/jonathandann.wordpress.com/46/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/jonathandann.wordpress.com/46/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/jonathandann.wordpress.com/46/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/jonathandann.wordpress.com/46/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/jonathandann.wordpress.com/46/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/jonathandann.wordpress.com/46/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/jonathandann.wordpress.com/46/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/jonathandann.wordpress.com/46/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/jonathandann.wordpress.com/46/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/jonathandann.wordpress.com/46/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/jonathandann.wordpress.com/46/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/jonathandann.wordpress.com/46/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=espresso-served-here.com&blog=656654&post=46&subd=jonathandann&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://espresso-served-here.com/2008/04/06/using-nstreecontroller/feed/</wfw:commentRss>
	
		<media:content url="http://a.wordpress.com/avatar/jpdann-128.jpg" medium="image">
			<media:title type="html">jpdann</media:title>
		</media:content>
	</item>
	</channel>
</rss>