<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/rss2full.xsl" type="text/xsl" media="screen"?><?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/itemcontent.css" type="text/css" media="screen"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">
  <channel>
    <title>Pioneering Software! : </title>
    <link>http://blog.pioneeringsoftware.co.uk</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>innovation | quality | care</description>
    <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/PioneeringSoftware" type="application/rss+xml" /><item>
      <title>Gloss-Caustic Shading</title>
      <description>&lt;p&gt;Matt Gallagher recently wrote a very useful article about &lt;a href="http://cocoawithlove.com/2008/09/drawing-gloss-gradients-in-coregraphics.html"&gt;drawing gloss gradients using Core Graphics&lt;/a&gt;. In his article, Matt describes how to reproduce the oft-seen glossy gradient effect. Thanks Matt! It&amp;#8217;s a nice article. &amp;#8220;Cocoa with Love&amp;#8221; lovingly provides the working source code. This little article aims to complement Matt&amp;#8217;s work.&lt;/p&gt;

&lt;p&gt;I&amp;#8217;ve also re-factored the software and packaged the result within an Objective-C class called &lt;code&gt;RRGlossCausticShader&lt;/code&gt;. This packaging automatically adds support for key-value coding and observing. Bindings then let you easily wrap the class within a little application able to adjust the many parameters interactively.&lt;/p&gt;

&lt;h1&gt;Show me the money&lt;/h1&gt;

&lt;p&gt;Before going any further, you might first want to see the results. I know &lt;em&gt;I&lt;/em&gt; would! &lt;a href="http://blog.pioneeringsoftware.co.uk/files/GlossCausticShader.zip"&gt;Download&lt;/a&gt; the project to play with the shader. Compile and run the application using Xcode. The main window appears as follows. Inspector panels also appear from where you can interactively adjust the many shading parameters.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://blog.pioneeringsoftware.co.uk/files/2008-12-10_gloss_caustic_shader_window.png" alt="Gloss Caustic Shader Window"/&gt;&lt;/p&gt;

&lt;p&gt;Gloss above, caustic below. Those are the two halves of the shading. Gloss appears in the top half, caustic in the bottom half. Within each half, an exponential function blends a given non-caustic base colour with whiteness above and with &lt;em&gt;matching&lt;/em&gt; caustic colour below. The main window displays these basic elements of the shading process: the shading itself (left), non-caustic base colour (top right) and coefficient of the exponent function (bottom right).&lt;/p&gt;

&lt;h1&gt;Re-factoring&lt;/h1&gt;

&lt;p&gt;My version of Matt&amp;#8217;s work includes some re-factoring. Rather than just the one functional interface, the version that you&amp;#8217;ll find within the project involves three modular utility classes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;RRGlossCausticShader&lt;/code&gt; encapsulates shading&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RRCausticColorMatcher&lt;/code&gt; finds caustic matches for given colours&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RRExponentialFunction&lt;/code&gt; wraps the exponential maths&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RRLuminanceFromRGBComponents&lt;/code&gt; returns luminance from RGB triples&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This re-factoring has some benefits. First, it promotes re-use. Other requirements may call for RGB-to-luminance conversion, or a generic exponential function for example; or you might want to sub-class the caustic colour matcher and add some customisation. Separating out the individual sub-requirements adds some extra scope for growth. I think it also makes the source code somewhat easier to follow and therefore comprehend. But that might just be my own personal opinion. Your mileage may vary, as they say!&lt;/p&gt;

&lt;p&gt;Anyway, the purpose of this article is to outline some of the differences in implementation and the underlying thinking. Comments welcome, of course.&lt;/p&gt;

&lt;h1&gt;Twinkle, twinkle&lt;/h1&gt;

&lt;p&gt;The gloss effect on the upper half of the gradient derives from the base colour&amp;#8217;s luminosity. So obtaining luminance from a given Red, Green and Blue is part of the glossing requirement.&lt;/p&gt;

&lt;p&gt;As you can see below, I replace Matt&amp;#8217;s conversion with the one described at &lt;a href="http://www.opengl.org/resources/code/samples/advanced/advanced97/notes/node140.html"&gt;OpenGL&lt;/a&gt;. The difference is small, almost non-existent. Only, the comment that &amp;#8220;Haeberli notes that [the YIQ colour conversion values used by Matt] are incorrect in a linear RGB colour space&amp;#8221; convinced me to make the small change. Just call me fussy.&lt;/p&gt;

&lt;div class="textmate-source dawn"&gt;

&lt;pre class="textmate-source"&gt;&lt;span class="source source_objc"&gt;&lt;span class="support support_type support_type_quartz"&gt;CGFloat&lt;/span&gt;&lt;span class="meta meta_function meta_function_c"&gt;&lt;span class="punctuation punctuation_whitespace punctuation_whitespace_function punctuation_whitespace_function_leading punctuation_whitespace_function_leading_c"&gt; &lt;/span&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_c"&gt;RRLuminanceFromRGBComponents&lt;/span&gt;&lt;span class="meta meta_parens meta_parens_c"&gt;(&lt;span class="storage storage_modifier storage_modifier_c"&gt;const&lt;/span&gt; &lt;span class="support support_type support_type_quartz"&gt;CGFloat&lt;/span&gt; *rgb)&lt;/span&gt;
&lt;span class="meta meta_block meta_block_c"&gt;{
    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; 0.3086 + 0.6094 + 0.0820 = 1.0
&lt;/span&gt;    &lt;span class="keyword keyword_control keyword_control_c"&gt;return&lt;/span&gt; &lt;span class="constant constant_numeric constant_numeric_c"&gt;0.3086f&lt;/span&gt;*rgb&lt;span class="meta meta_bracketed meta_bracketed_objc"&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;[&lt;/span&gt;&lt;span class="constant constant_numeric constant_numeric_c"&gt;0&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;]&lt;/span&gt;&lt;/span&gt; + &lt;span class="constant constant_numeric constant_numeric_c"&gt;0.6094f&lt;/span&gt;*rgb&lt;span class="meta meta_bracketed meta_bracketed_objc"&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;[&lt;/span&gt;&lt;span class="constant constant_numeric constant_numeric_c"&gt;1&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;]&lt;/span&gt;&lt;/span&gt; + &lt;span class="constant constant_numeric constant_numeric_c"&gt;0.0820f&lt;/span&gt;*rgb&lt;span class="meta meta_bracketed meta_bracketed_objc"&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;[&lt;/span&gt;&lt;span class="constant constant_numeric constant_numeric_c"&gt;2&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;]&lt;/span&gt;&lt;/span&gt;;
}&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Notice a few other things. I&amp;#8217;m using &lt;code&gt;CGFloat&lt;/code&gt; type for floats because this corresponds exactly with Apple&amp;#8217;s usage for colour components. Can we always assume that &lt;code&gt;CGFloat&lt;/code&gt; equals &lt;code&gt;float&lt;/code&gt;? No. In fact, &lt;code&gt;CGFloat&lt;/code&gt; is sometimes &lt;code&gt;double&lt;/code&gt;! This happens when 64-bit compiling for instance. Argument &lt;code&gt;const&lt;/code&gt;-correctness is another feature.&lt;/p&gt;

&lt;h1&gt;Caustic nature&lt;/h1&gt;

&lt;p&gt;The lower half of the gradient blends the base colour into shades of yellow (yellow is the default caustic hue). The lower end of the gradient starts to look more-and-more tinted towards the yellow. However, the precise colour depends upon the non-caustic base colour. For blue colours, for example, the default caustic switches to magenta. Blue looks better fading toward magenta, rather than yellow. In all cases though, the amount of caustic to non-caustic blending varies according to the cosine of the distance between the caustic and non-caustic hues.&lt;/p&gt;

&lt;p&gt;So rather than having a function handle the caustic colour matching and implementing the adjustable parameters as manifest constants, the new version implements a &amp;#8220;colour matching&amp;#8221; class. Interface listed below. The class implements adjustable parameters as read-write class properties with all the necessary setters and getters. This approach buys some advantages. It automatically adds support for key-value coding and observing. In practice, it means that user-interface controls can bind to these properties using Cocoa Bindings.&lt;/p&gt;

&lt;div class="textmate-source dawn"&gt;

&lt;pre class="textmate-source"&gt;&lt;span class="source source_objc"&gt;&lt;span class="meta meta_interface-or-protocol meta_interface-or-protocol_objc"&gt;&lt;span class="storage storage_type storage_type_objc"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_storage punctuation_definition_storage_type punctuation_definition_storage_type_objc"&gt;@&lt;/span&gt;interface&lt;/span&gt; &lt;span class="entity entity_name entity_name_type entity_name_type_objc"&gt;RRCausticColorMatcher&lt;/span&gt; &lt;span class="punctuation punctuation_definition punctuation_definition_entity punctuation_definition_entity_other punctuation_definition_entity_other_inherited-class punctuation_definition_entity_other_inherited-class_objc"&gt;:&lt;/span&gt; &lt;span class="entity entity_other entity_other_inherited-class entity_other_inherited-class_objc"&gt;NSObject&lt;/span&gt;&lt;span class="meta meta_divider meta_divider_objc"&gt;
&lt;/span&gt;&lt;span class="meta meta_scope meta_scope_interface meta_scope_interface_objc"&gt;&lt;span class="meta meta_block meta_block_c"&gt;{
    &lt;span class="support support_type support_type_quartz"&gt;CGFloat&lt;/span&gt; causticHue;
        &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; Yellow by default.
&lt;/span&gt;    &lt;span class="support support_type support_type_quartz"&gt;CGFloat&lt;/span&gt; graySaturationThreshold;
        &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; Saturation level at which colours appear grey. Below this level,
&lt;/span&gt;        &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; matcher response snaps to pure caustic.
&lt;/span&gt;    &lt;span class="support support_type support_type_quartz"&gt;CGFloat&lt;/span&gt; causticSaturationForGrays;
        &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; Defines the caustic saturation for grey colours. Grey colours fall
&lt;/span&gt;        &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; below the grey saturation threshold. When saturation drops too low,
&lt;/span&gt;        &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; everything looks grey.
&lt;/span&gt;    &lt;span class="support support_type support_type_quartz"&gt;CGFloat&lt;/span&gt; redHueThreshold;
        &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; Colours at this threshold and above match to default caustics rather
&lt;/span&gt;        &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; than default magenta for blues.
&lt;/span&gt;    &lt;span class="support support_type support_type_quartz"&gt;CGFloat&lt;/span&gt; blueHueThreshold;
        &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; Triggers a switch to magenta caustics. Hues at blue and beyond
&lt;/span&gt;        &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; display magenta-modulated caustics by default.
&lt;/span&gt;    &lt;span class="support support_type support_type_quartz"&gt;CGFloat&lt;/span&gt; blueCausticHue;
        &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; Magenta by default. Magenta caustics for blue colours.
&lt;/span&gt;    &lt;span class="support support_type support_type_quartz"&gt;CGFloat&lt;/span&gt; causticFractionDomainFactor;
        &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; Expands or contracts the caustic fraction's domain. With factor equal
&lt;/span&gt;        &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; to 1, non-caustic and caustic hues blend according to the cosine of
&lt;/span&gt;        &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; their difference. Smaller the difference, greater the amount of
&lt;/span&gt;        &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; caustic hue. Defaults to 1.4 meaning that the point of absolutely no
&lt;/span&gt;        &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; caustic blending occurs at 1/1.4 difference from caustic hue. Try
&lt;/span&gt;        &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; plotting cos(x*pi*1.4) in the -1,1 interval.
&lt;/span&gt;    &lt;span class="support support_type support_type_quartz"&gt;CGFloat&lt;/span&gt; causticFractionRangeFactor;
        &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; Scales the caustic fraction which without a factor outputs a blending
&lt;/span&gt;        &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; fraction between 0 and 1 in favour of the caustic blend. Defaults to
&lt;/span&gt;        &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; 0.6 which scales down the amount of caustic hue-and-brightness by
&lt;/span&gt;        &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; that amount.
&lt;/span&gt;}&lt;/span&gt;

&lt;span class="meta meta_function meta_function_objc"&gt;- &lt;span class="meta meta_return-type meta_return-type_objc"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;(&lt;/span&gt;&lt;span class="support support_class support_class_cocoa"&gt;NSColor&lt;/span&gt; *&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;)&lt;/span&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_objc"&gt;matchForColor&lt;/span&gt;&lt;/span&gt;&lt;span class="meta meta_argument-type meta_argument-type_objc"&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc"&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc"&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;(&lt;/span&gt;&lt;span class="support support_class support_class_cocoa"&gt;NSColor&lt;/span&gt; *&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;)&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_objc"&gt;aColor&lt;/span&gt;&lt;/span&gt;;&lt;/span&gt;
    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; Matches the given colour. Answers a matching caustic colour. The result
&lt;/span&gt;    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; shifts hue and brightness towards yellow. Saturation remains unchanged.
&lt;/span&gt;&lt;span class="meta meta_function meta_function_objc"&gt;- &lt;span class="meta meta_return-type meta_return-type_objc"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;(&lt;/span&gt;&lt;span class="storage storage_type storage_type_c"&gt;void&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;)&lt;/span&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_objc"&gt;matchForHSB&lt;/span&gt;&lt;/span&gt;&lt;span class="meta meta_argument-type meta_argument-type_objc"&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc"&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc"&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;(&lt;/span&gt;&lt;span class="storage storage_modifier storage_modifier_c"&gt;const&lt;/span&gt; &lt;span class="support support_type support_type_quartz"&gt;CGFloat&lt;/span&gt; *&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;)&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_objc"&gt;hsb&lt;/span&gt;&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc"&gt;caustic&lt;/span&gt;&lt;span class="meta meta_argument-type meta_argument-type_objc"&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc"&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc"&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;(&lt;/span&gt;&lt;span class="support support_type support_type_quartz"&gt;CGFloat&lt;/span&gt; *&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;)&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_objc"&gt;outHSB&lt;/span&gt;&lt;/span&gt;;&lt;/span&gt;
    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; Does the work.
&lt;/span&gt;
&lt;span class="meta meta_property-with-attributes meta_property-with-attributes_objc"&gt;&lt;span class="keyword keyword_other keyword_other_property keyword_other_property_objc"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_keyword punctuation_definition_keyword_objc"&gt;@&lt;/span&gt;property&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;(&lt;/span&gt;&lt;span class="keyword keyword_other keyword_other_property keyword_other_property_attribute"&gt;assign&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class="support support_type support_type_quartz"&gt;CGFloat&lt;/span&gt; causticHue;
&lt;span class="meta meta_property-with-attributes meta_property-with-attributes_objc"&gt;&lt;span class="keyword keyword_other keyword_other_property keyword_other_property_objc"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_keyword punctuation_definition_keyword_objc"&gt;@&lt;/span&gt;property&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;(&lt;/span&gt;&lt;span class="keyword keyword_other keyword_other_property keyword_other_property_attribute"&gt;assign&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class="support support_type support_type_quartz"&gt;CGFloat&lt;/span&gt; graySaturationThreshold;
&lt;span class="meta meta_property-with-attributes meta_property-with-attributes_objc"&gt;&lt;span class="keyword keyword_other keyword_other_property keyword_other_property_objc"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_keyword punctuation_definition_keyword_objc"&gt;@&lt;/span&gt;property&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;(&lt;/span&gt;&lt;span class="keyword keyword_other keyword_other_property keyword_other_property_attribute"&gt;assign&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class="support support_type support_type_quartz"&gt;CGFloat&lt;/span&gt; causticSaturationForGrays;
&lt;span class="meta meta_property-with-attributes meta_property-with-attributes_objc"&gt;&lt;span class="keyword keyword_other keyword_other_property keyword_other_property_objc"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_keyword punctuation_definition_keyword_objc"&gt;@&lt;/span&gt;property&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;(&lt;/span&gt;&lt;span class="keyword keyword_other keyword_other_property keyword_other_property_attribute"&gt;assign&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class="support support_type support_type_quartz"&gt;CGFloat&lt;/span&gt; redHueThreshold;
&lt;span class="meta meta_property-with-attributes meta_property-with-attributes_objc"&gt;&lt;span class="keyword keyword_other keyword_other_property keyword_other_property_objc"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_keyword punctuation_definition_keyword_objc"&gt;@&lt;/span&gt;property&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;(&lt;/span&gt;&lt;span class="keyword keyword_other keyword_other_property keyword_other_property_attribute"&gt;assign&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class="support support_type support_type_quartz"&gt;CGFloat&lt;/span&gt; blueHueThreshold;
&lt;span class="meta meta_property-with-attributes meta_property-with-attributes_objc"&gt;&lt;span class="keyword keyword_other keyword_other_property keyword_other_property_objc"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_keyword punctuation_definition_keyword_objc"&gt;@&lt;/span&gt;property&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;(&lt;/span&gt;&lt;span class="keyword keyword_other keyword_other_property keyword_other_property_attribute"&gt;assign&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class="support support_type support_type_quartz"&gt;CGFloat&lt;/span&gt; blueCausticHue;
&lt;span class="meta meta_property-with-attributes meta_property-with-attributes_objc"&gt;&lt;span class="keyword keyword_other keyword_other_property keyword_other_property_objc"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_keyword punctuation_definition_keyword_objc"&gt;@&lt;/span&gt;property&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;(&lt;/span&gt;&lt;span class="keyword keyword_other keyword_other_property keyword_other_property_attribute"&gt;assign&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class="support support_type support_type_quartz"&gt;CGFloat&lt;/span&gt; causticFractionDomainFactor;
&lt;span class="meta meta_property-with-attributes meta_property-with-attributes_objc"&gt;&lt;span class="keyword keyword_other keyword_other_property keyword_other_property_objc"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_keyword punctuation_definition_keyword_objc"&gt;@&lt;/span&gt;property&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;(&lt;/span&gt;&lt;span class="keyword keyword_other keyword_other_property keyword_other_property_attribute"&gt;assign&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class="support support_type support_type_quartz"&gt;CGFloat&lt;/span&gt; causticFractionRangeFactor;

&lt;/span&gt;&lt;span class="storage storage_type storage_type_objc"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_storage punctuation_definition_storage_type punctuation_definition_storage_type_objc"&gt;@&lt;/span&gt;end&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h1&gt;Exponentially&lt;/h1&gt;

&lt;p&gt;Shading employs an exponential function. As the gradient progresses from 0 to 0.5, gloss white blending increases &lt;em&gt;exponentially&lt;/em&gt; in the 0,1 interval. Progressing through the bottom half of the gradient from 0.5 to 1, caustic blending increases &lt;em&gt;exponentially&lt;/em&gt;. The implementation re-factors the &lt;a href="http://mathworld.wolfram.com/ExponentialFunction.html"&gt;exponential function&lt;/a&gt;. Interface listing below. It becomes a C-style object class and thereby minimises its dependencies: just plain C types and standard library &lt;code&gt;math.h&lt;/code&gt; and hence very suitable for repeated invocations from the &lt;em&gt;bowels&lt;/em&gt; of a shading function.&lt;/p&gt;

&lt;div class="textmate-source dawn"&gt;

&lt;pre class="textmate-source"&gt;&lt;span class="source source_objc"&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; Encapsulates an optimising generic Exponential Function where
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt;      y=1-(exp(x*-c)-exp(-c))/(1-exp(-c))
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; and where 0&amp;lt;c is a general coefficient describing the exponential
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; curvature. The function's input domain lies within the 0..1 interval, its
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; output range likewise. The implementation optimises by pre-computing those
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; constant terms depending only on the coefficient whenever the coefficient
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; changes. Repeated evaluation takes less computing time thereafter.
&lt;/span&gt;&lt;span class="storage storage_type storage_type_c"&gt;struct&lt;/span&gt; RRExponentialFunction
&lt;span class="meta meta_block meta_block_c"&gt;{
    &lt;span class="storage storage_type storage_type_c"&gt;float&lt;/span&gt; coefficient;
    &lt;span class="storage storage_type storage_type_c"&gt;float&lt;/span&gt; exponentOfMinusCoefficient;
    &lt;span class="storage storage_type storage_type_c"&gt;float&lt;/span&gt; oneOverOneMinusExponentOfMinusCoefficient;
}&lt;/span&gt;;

&lt;span class="storage storage_type storage_type_c"&gt;typedef&lt;/span&gt; &lt;span class="storage storage_type storage_type_c"&gt;struct&lt;/span&gt; RRExponentialFunction RRExponentialFunction;

&lt;span class="storage storage_type storage_type_c"&gt;void&lt;/span&gt;&lt;span class="meta meta_function meta_function_c"&gt;&lt;span class="punctuation punctuation_whitespace punctuation_whitespace_function punctuation_whitespace_function_leading punctuation_whitespace_function_leading_c"&gt; &lt;/span&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_c"&gt;RRExponentialFunctionSetCoefficient&lt;/span&gt;&lt;span class="meta meta_parens meta_parens_c"&gt;(RRExponentialFunction *f, &lt;span class="storage storage_type storage_type_c"&gt;float&lt;/span&gt; c)&lt;/span&gt;;&lt;/span&gt;
&lt;span class="storage storage_type storage_type_c"&gt;float&lt;/span&gt;&lt;span class="meta meta_function meta_function_c"&gt;&lt;span class="punctuation punctuation_whitespace punctuation_whitespace_function punctuation_whitespace_function_leading punctuation_whitespace_function_leading_c"&gt; &lt;/span&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_c"&gt;RRExponentialFunctionEvaluate&lt;/span&gt;&lt;span class="meta meta_parens meta_parens_c"&gt;(RRExponentialFunction *f, &lt;span class="storage storage_type storage_type_c"&gt;float&lt;/span&gt; x)&lt;/span&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h1&gt;Shading&lt;/h1&gt;

&lt;p&gt;And finally, the &amp;#8220;gloss-caustic shader&amp;#8221; class brings all the disparate pieces together. The design aims for reusability. Idea is that you instantiate a shader, set up the parameters such as non-caustic colour or any other necessary adjustments to defaults, then re-use it over-and-over whenever you need to draw the shading.&lt;/p&gt;

&lt;div class="textmate-source dawn"&gt;

&lt;pre class="textmate-source"&gt;&lt;span class="source source_objc"&gt;&lt;span class="meta meta_interface-or-protocol meta_interface-or-protocol_objc"&gt;&lt;span class="storage storage_type storage_type_objc"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_storage punctuation_definition_storage_type punctuation_definition_storage_type_objc"&gt;@&lt;/span&gt;interface&lt;/span&gt; &lt;span class="entity entity_name entity_name_type entity_name_type_objc"&gt;RRGlossCausticShader&lt;/span&gt; &lt;span class="punctuation punctuation_definition punctuation_definition_entity punctuation_definition_entity_other punctuation_definition_entity_other_inherited-class punctuation_definition_entity_other_inherited-class_objc"&gt;:&lt;/span&gt; &lt;span class="entity entity_other entity_other_inherited-class entity_other_inherited-class_objc"&gt;NSObject&lt;/span&gt;&lt;span class="meta meta_divider meta_divider_objc"&gt;
&lt;/span&gt;&lt;span class="meta meta_scope meta_scope_interface meta_scope_interface_objc"&gt;&lt;span class="meta meta_block meta_block_c"&gt;{
    &lt;span class="storage storage_type storage_type_c"&gt;struct&lt;/span&gt; RRGlossCausticShaderInfo *info;
    RRCausticColorMatcher *matcher;
}&lt;/span&gt;

&lt;span class="meta meta_function meta_function_objc"&gt;- &lt;span class="meta meta_return-type meta_return-type_objc"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;(&lt;/span&gt;&lt;span class="storage storage_type storage_type_c"&gt;void&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;)&lt;/span&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_objc"&gt;drawShadingFromPoint&lt;/span&gt;&lt;/span&gt;&lt;span class="meta meta_argument-type meta_argument-type_objc"&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc"&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc"&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;(&lt;/span&gt;&lt;span class="support support_type support_type_cocoa"&gt;NSPoint&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;)&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_objc"&gt;startingPoint&lt;/span&gt;&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc"&gt;toPoint&lt;/span&gt;&lt;span class="meta meta_argument-type meta_argument-type_objc"&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc"&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc"&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;(&lt;/span&gt;&lt;span class="support support_type support_type_cocoa"&gt;NSPoint&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;)&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_objc"&gt;endingPoint&lt;/span&gt;&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc"&gt;inContext&lt;/span&gt;&lt;span class="meta meta_argument-type meta_argument-type_objc"&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc"&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc"&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;(&lt;/span&gt;CGContextRef&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;)&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_objc"&gt;aContext&lt;/span&gt;&lt;/span&gt;;&lt;/span&gt;

&lt;span class="meta meta_function meta_function_objc"&gt;- &lt;span class="meta meta_return-type meta_return-type_objc"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;(&lt;/span&gt;&lt;span class="storage storage_type storage_type_c"&gt;void&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;)&lt;/span&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_objc"&gt;update&lt;/span&gt;&lt;/span&gt;;&lt;/span&gt;
    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; Send -update after changing one or more parameters. Setters do not
&lt;/span&gt;    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; automatically update the shader. This is by design. It applies to the
&lt;/span&gt;    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; caustic colour matcher too. Change anything? Send an update. Otherwise,
&lt;/span&gt;    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; if the setters automatically update, multiple changes trigger unnecessary
&lt;/span&gt;    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; multiple updates. It's a small optimisation.
&lt;/span&gt;    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; Updating follows the dependency chain. Caustic colour depends on
&lt;/span&gt;    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; non-caustic colour along with all the caustic colour matcher's tuneable
&lt;/span&gt;    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; configuration settings. Updating also re-computes the gloss. Gloss
&lt;/span&gt;    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; derives from non-caustic colour luminance, among other things.
&lt;/span&gt;
&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt;---------------------------------------------------------------------- setters
&lt;/span&gt;
&lt;span class="meta meta_function meta_function_objc"&gt;- &lt;span class="meta meta_return-type meta_return-type_objc"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;(&lt;/span&gt;&lt;span class="storage storage_type storage_type_c"&gt;void&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;)&lt;/span&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_objc"&gt;setExponentialCoefficient&lt;/span&gt;&lt;/span&gt;&lt;span class="meta meta_argument-type meta_argument-type_objc"&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc"&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc"&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;(&lt;/span&gt;&lt;span class="storage storage_type storage_type_c"&gt;float&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;)&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_objc"&gt;c&lt;/span&gt;&lt;/span&gt;;&lt;/span&gt;
&lt;span class="meta meta_function meta_function_objc"&gt;- &lt;span class="meta meta_return-type meta_return-type_objc"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;(&lt;/span&gt;&lt;span class="storage storage_type storage_type_c"&gt;void&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;)&lt;/span&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_objc"&gt;setNoncausticColor&lt;/span&gt;&lt;/span&gt;&lt;span class="meta meta_argument-type meta_argument-type_objc"&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc"&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc"&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;(&lt;/span&gt;&lt;span class="support support_class support_class_cocoa"&gt;NSColor&lt;/span&gt; *&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;)&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_objc"&gt;aColor&lt;/span&gt;&lt;/span&gt;;&lt;/span&gt;
    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; Converts aColor to device RGB colour space. The resulting colour
&lt;/span&gt;    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; components become the new non-caustic colour. This setter, like all
&lt;/span&gt;    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; others, does not automatically readjust the dependencies. Invoke -update
&lt;/span&gt;    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; after adjusting one or more settings.
&lt;/span&gt;&lt;span class="meta meta_function meta_function_objc"&gt;- &lt;span class="meta meta_return-type meta_return-type_objc"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;(&lt;/span&gt;&lt;span class="storage storage_type storage_type_c"&gt;void&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;)&lt;/span&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_objc"&gt;setGlossReflectionPower&lt;/span&gt;&lt;/span&gt;&lt;span class="meta meta_argument-type meta_argument-type_objc"&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc"&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc"&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;(&lt;/span&gt;&lt;span class="support support_type support_type_quartz"&gt;CGFloat&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;)&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_objc"&gt;powerLevel&lt;/span&gt;&lt;/span&gt;;&lt;/span&gt;
    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; Assigns a new power level to the gloss reflection.
&lt;/span&gt;&lt;span class="meta meta_function meta_function_objc"&gt;- &lt;span class="meta meta_return-type meta_return-type_objc"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;(&lt;/span&gt;&lt;span class="storage storage_type storage_type_c"&gt;void&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;)&lt;/span&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_objc"&gt;setGlossStartingWhite&lt;/span&gt;&lt;/span&gt;&lt;span class="meta meta_argument-type meta_argument-type_objc"&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc"&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc"&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;(&lt;/span&gt;&lt;span class="support support_type support_type_quartz"&gt;CGFloat&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;)&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_objc"&gt;whiteLevel&lt;/span&gt;&lt;/span&gt;;&lt;/span&gt;
    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; White levels range between 0 and 1 inclusive. Gloss starting white levels
&lt;/span&gt;    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; typically have higher values compared to ending white level.
&lt;/span&gt;&lt;span class="meta meta_function meta_function_objc"&gt;- &lt;span class="meta meta_return-type meta_return-type_objc"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;(&lt;/span&gt;&lt;span class="storage storage_type storage_type_c"&gt;void&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;)&lt;/span&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_objc"&gt;setGlossEndingWhite&lt;/span&gt;&lt;/span&gt;&lt;span class="meta meta_argument-type meta_argument-type_objc"&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc"&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc"&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;(&lt;/span&gt;&lt;span class="support support_type support_type_quartz"&gt;CGFloat&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;)&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_objc"&gt;whiteLevel&lt;/span&gt;&lt;/span&gt;;&lt;/span&gt;

&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt;---------------------------------------------------------------------- getters
&lt;/span&gt;
&lt;span class="meta meta_function meta_function_objc"&gt;- &lt;span class="meta meta_return-type meta_return-type_objc"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;(&lt;/span&gt;&lt;span class="storage storage_type storage_type_c"&gt;float&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;)&lt;/span&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_objc"&gt;exponentialCoefficient&lt;/span&gt;&lt;/span&gt;;&lt;/span&gt;
&lt;span class="meta meta_function meta_function_objc"&gt;- &lt;span class="meta meta_return-type meta_return-type_objc"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;(&lt;/span&gt;&lt;span class="support support_class support_class_cocoa"&gt;NSColor&lt;/span&gt; *&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;)&lt;/span&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_objc"&gt;noncausticColor&lt;/span&gt;&lt;/span&gt;;&lt;/span&gt;
    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; Returns the non-caustic colour.
&lt;/span&gt;&lt;span class="meta meta_function meta_function_objc"&gt;- &lt;span class="meta meta_return-type meta_return-type_objc"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;(&lt;/span&gt;&lt;span class="support support_type support_type_quartz"&gt;CGFloat&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;)&lt;/span&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_objc"&gt;glossReflectionPower&lt;/span&gt;&lt;/span&gt;;&lt;/span&gt;
&lt;span class="meta meta_function meta_function_objc"&gt;- &lt;span class="meta meta_return-type meta_return-type_objc"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;(&lt;/span&gt;&lt;span class="support support_type support_type_quartz"&gt;CGFloat&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;)&lt;/span&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_objc"&gt;glossStartingWhite&lt;/span&gt;&lt;/span&gt;;&lt;/span&gt;
&lt;span class="meta meta_function meta_function_objc"&gt;- &lt;span class="meta meta_return-type meta_return-type_objc"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;(&lt;/span&gt;&lt;span class="support support_type support_type_quartz"&gt;CGFloat&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;)&lt;/span&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_objc"&gt;glossEndingWhite&lt;/span&gt;&lt;/span&gt;;&lt;/span&gt;

&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; Key-value coding automatically gives access to colour matching for caustic
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; colours. Special note though, changing caustic matcher thresholds does not
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; (repeat not) automatically adjust the shader's caustic colour. You need to
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; update the shader when ready.
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; Currently, the matcher property offers read-only access. You cannot set the
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; matcher! However, perhaps future versions will allow setting in order to
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; override the default caustic matching behaviour. Developers might want to
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; customise the colour matching algorithmically as well as by tweaking
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; parameters.
&lt;/span&gt;&lt;span class="meta meta_property-with-attributes meta_property-with-attributes_objc"&gt;&lt;span class="keyword keyword_other keyword_other_property keyword_other_property_objc"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_keyword punctuation_definition_keyword_objc"&gt;@&lt;/span&gt;property&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;(&lt;/span&gt;&lt;span class="keyword keyword_other keyword_other_property keyword_other_property_attribute"&gt;readonly&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;)&lt;/span&gt;&lt;/span&gt; RRCausticColorMatcher *matcher;

&lt;/span&gt;&lt;span class="storage storage_type storage_type_objc"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_storage punctuation_definition_storage_type punctuation_definition_storage_type_objc"&gt;@&lt;/span&gt;end&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;The &lt;a href="http://blog.pioneeringsoftware.co.uk/files/GlossCausticShader.zip"&gt;sample project&lt;/a&gt; explains how to use the shader. I hope it&amp;#8217;s clear enough. The sources have an MIT license. I haven&amp;#8217;t tried it out on iPhone or pre-Leopard Mac, so can&amp;#8217;t make any comments about portability except to say that porting &lt;em&gt;should&lt;/em&gt; be fairly straightforward. But no doubt you&amp;#8217;ve heard that before!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/PioneeringSoftware/~4/480651694" height="1" width="1"/&gt;</description>
      <pubDate>Tue, 09 Dec 2008 21:25:45 -0600</pubDate>
      <guid isPermaLink="false">urn:uuid:490e8910-8601-452c-939d-54252a354a2b</guid>
      <comments>http://blog.pioneeringsoftware.co.uk/2008/12/09/gloss-caustic-shading#comments</comments>
      <category>AppKit</category>
      <trackback:ping>http://blog.pioneeringsoftware.co.uk/trackbacks?article_id=gloss-caustic-shading&amp;day=09&amp;month=12&amp;year=2008</trackback:ping>
      <link>http://feeds.feedburner.com/~r/PioneeringSoftware/~3/480651694/gloss-caustic-shading</link>
    <feedburner:origLink>http://blog.pioneeringsoftware.co.uk/2008/12/09/gloss-caustic-shading</feedburner:origLink></item>
    <item>
      <title>Outline view, tree controller and itemForPersistentObject</title>
      <description>&lt;p&gt;This is the scenario: your user interface comprises an outline view and a hierarchical data model. You want the outline view to display the hierarchy &lt;em&gt;and&lt;/em&gt; remember the expansion state automatically in preferences. Hence when the user re-runs the application, the items that were expanded are still expanded, and vice versa: what was collapsed remains so. Outline view, tree controller, hierarchical model, bindings. That&amp;#8217;s the recipe.&lt;/p&gt;

&lt;p&gt;According to some, storing expansion state of an outline view when used with a tree controller is not just difficult: it&amp;#8217;s &lt;a href="http://www.cocoadev.com/index.pl?OutlineViewCoreDataBindings"&gt;impossible&lt;/a&gt;! But is it? No, is the simple answer. It&amp;#8217;s actually quite easy. In this article I introduce a new helper class called &lt;code&gt;RROutlineViewExpandedItemsAutosaver&lt;/code&gt;! Original aren&amp;#8217;t I? It does not involve sub-classing or access to private methods. The solution presented uses only documented interfaces and only requires a small stateless class instance for handling all outline view auto-saving technicalities. It does assume Core Data use for modelling. But you can easily adapt the technique for other data-model implementations.&lt;/p&gt;

&lt;p&gt;It&amp;#8217;s impossible?&lt;/p&gt;

&lt;p&gt;At first it seems so. For a start, if you read the documentation for &lt;code&gt;setAutosaveExpandedItems:&lt;/code&gt; you can see that you also need to implement two data-source methods: &lt;code&gt;outlineView:itemForPersistentObject:&lt;/code&gt; and &lt;code&gt;outlineView:persistentObjectForItem:&lt;/code&gt;. Goes without saying, you also need a data source implementing those methods. Otherwise it does not work. In fact, it&amp;#8217;s not exactly clear what these two necessary methods are supposed to do. The document talks about translating to and from an &amp;#8220;archived object.&amp;#8221; What does that mean exactly?&lt;/p&gt;

&lt;p&gt;You can find a number of posts at CocoaBuilder on this issue. &lt;a href="http://www.cocoabuilder.com/archive/message/cocoa/2005/5/7/135189" title="NSOutlineView, NSTreeController and itemForPersistentObject"&gt;Keith Blount&lt;/a&gt; started a thread in 2005; &lt;a href="http://www.cocoabuilder.com/archive/message/cocoa/2006/4/17/161214" title="NSOutlineView setAutosaveExpandedItems"&gt;Nick Briggs&lt;/a&gt; asked about it again in 2006. But no answers forthcoming! Some discussion exists and some solutions have been posted at CocoaDev (&lt;a href="http://www.cocoadev.com/index.pl?OutlineViewCoreDataBindings"&gt;OutlineViewCoreDataBindings&lt;/a&gt; and &lt;a href="http://www.cocoadev.com/index.pl?NSOutlineViewStateSavingWithNSTreeController"&gt;NSOutlineViewStateSavingWithNSTreeController&lt;/a&gt;). But there is nothing definitive. Plus, the solution supplied relies on private methods and sub-classing. Messy!&lt;/p&gt;

&lt;h1&gt;Start with a test&lt;/h1&gt;

&lt;p&gt;In the spirit of &lt;a href="http://en.wikipedia.org/wiki/Agile_software_development"&gt;Agile&lt;/a&gt;, let&amp;#8217;s start with a test that fails.&lt;/p&gt;

&lt;p&gt;Apple&amp;#8217;s Developer Connection provides sample code. Their &lt;a href="http://developer.apple.com/samplecode/AbstractTree/"&gt;AbstractTree&lt;/a&gt; demonstrates Core Data, use of bindings along with an &lt;code&gt;NSTreeController&lt;/code&gt;. Download the sample &lt;a href="http://developer.apple.com/samplecode/AbstractTree/AbstractTree.zip"&gt;here&lt;/a&gt; as a ZIP file. It contains two subdirectories: &lt;code&gt;AbstractTree&lt;/code&gt; and &lt;code&gt;Tutorial&lt;/code&gt;. The tutorial is pretty good. It should help you get up-to-speed if you need some familiarity with the basic technology. Build and run the project living under the &lt;code&gt;AbstractTree&lt;/code&gt; folder. It presents you with a window comprising an empty outline view and two buttons.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://blog.pioneeringsoftware.co.uk/files/2008-09-10_empty_outline_view_with_two_buttons.png" alt="Empty outline view with two buttons"/&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Add&lt;/strong&gt; a few times. You get a series of nested nodes named &amp;#8220;untitled node&amp;#8221;. Quit the application, re-run and you find that, although data saved, the expansion state of the outline view resets. You have to manually expand each item. Of course, you can Option-click the disclosure triangles to auto-expand an item and all its sub-items. But that&amp;#8217;s not really what we want. We want the expand or collapse state of each element displayed in the outline view to &lt;em&gt;persist&lt;/em&gt;! That&amp;#8217;s the goal.&lt;/p&gt;

&lt;p&gt;So far so good. We have a test case that fails. Just what we wanted.&lt;/p&gt;

&lt;h1&gt;Outline view&amp;#8217;s auto-save name&lt;/h1&gt;

&lt;p&gt;Open the sample&amp;#8217;s &lt;code&gt;MainMenu.xib&lt;/code&gt;. Select the &lt;code&gt;NSOutlineView&lt;/code&gt;. Set its &lt;strong&gt;Autosave&lt;/strong&gt; name to (say) &lt;code&gt;Nodes&lt;/code&gt;. Exact value does not matter. This is just a little piece of text for constructing the preferences key. The application&amp;#8217;s defaults will contain an array containing the expanded items. The key will become &amp;#8220;NSOutlineView Items Nodes&amp;#8221; where &lt;code&gt;Nodes&lt;/code&gt; corresponds the the specified &lt;strong&gt;Autosave&lt;/strong&gt; name.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://blog.pioneeringsoftware.co.uk/files/2008-09-10_autosave_name_of_nodes.png" alt="Autosave name of Nodes"/&gt;&lt;/p&gt;

&lt;p&gt;Leave the &lt;strong&gt;Autosave Expanded Items&lt;/strong&gt; check box &lt;em&gt;unchecked&lt;/em&gt;! Strange but important. If you check this box, the framework resolves the expand-collapse question straightaway. It needs to wait until bindings have been fully established and the data store has been added. The application will instead enable this option programmatically when the application finishes launching. Not before.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://blog.pioneeringsoftware.co.uk/files/2008-09-10_autosave_expanded_items_check_box_unchecked.png" alt="Autosave Expanded Items check box unchecked"/&gt;&lt;/p&gt;

&lt;h1&gt;Add the new class&lt;/h1&gt;

&lt;p&gt;Add the interface header, &lt;code&gt;RROutlineViewExpandedItemsAutosaver.h&lt;/code&gt;, to the project. Contents as follows. Download header and module sources &lt;a href="http://blog.pioneeringsoftware.co.uk/files/RROutlineViewExpandedItemsAutosaver.zip"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;div class="textmate-source dawn"&gt;

&lt;pre class="textmate-source"&gt;&lt;span class="source source_objc"&gt;&lt;span class="meta meta_preprocessor meta_preprocessor_c meta_preprocessor_c_include"&gt;#&lt;span class="keyword keyword_control keyword_control_import keyword_control_import_include keyword_control_import_include_c"&gt;import&lt;/span&gt; &lt;span class="string string_quoted string_quoted_other string_quoted_other_lt-gt string_quoted_other_lt-gt_include string_quoted_other_lt-gt_include_c"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_c"&gt;&amp;lt;&lt;/span&gt;AppKit/AppKit.h&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_c"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt;------------------------------------------------------------------------------
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; RROutlineViewExpandedItemsAutosaver
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt;------------------------------------------------------------------------------
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; This class acts as a "dummy" outline-view data source. Dummy because it does
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; not source any data. But you wire it up as though it did. In reality, its
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; sole purpose is to enable automatic saving of an outline view's expanded
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; items. It sounds counter-intuitive, but you can apply bindings through a tree
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; controller and at the very same time hook up a data source (such as one of
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; these) in order to provide the necessary persistence translations.
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt;
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; Connect an outline view to an instance of this class. Note, you can connect
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; multiple outlines views to the same instance! The instance carries no
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; state. Message interactions with outline views pass the outline view
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; instance. So no state needed.
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt;
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; The implementation makes a number of assumptions. It assumes you connect
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; using bindings to a Core Data model. Hence for every item, it sends [[item
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; representedObject] objectID] which assumes that the represented object
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; responds to -objectID. Core Data's managed objects respond with a unique
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; object identifier.
&lt;/span&gt;
&lt;span class="meta meta_interface-or-protocol meta_interface-or-protocol_objc"&gt;&lt;span class="storage storage_type storage_type_objc"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_storage punctuation_definition_storage_type punctuation_definition_storage_type_objc"&gt;@&lt;/span&gt;interface&lt;/span&gt; &lt;span class="entity entity_name entity_name_type entity_name_type_objc"&gt;RROutlineViewExpandedItemsAutosaver&lt;/span&gt; &lt;span class="punctuation punctuation_definition punctuation_definition_entity punctuation_definition_entity_other punctuation_definition_entity_other_inherited-class punctuation_definition_entity_other_inherited-class_objc"&gt;:&lt;/span&gt; &lt;span class="entity entity_other entity_other_inherited-class entity_other_inherited-class_objc"&gt;NSObject&lt;/span&gt;&lt;span class="meta meta_divider meta_divider_objc"&gt;
&lt;/span&gt;&lt;span class="meta meta_scope meta_scope_interface meta_scope_interface_objc"&gt;&lt;/span&gt;&lt;span class="storage storage_type storage_type_objc"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_storage punctuation_definition_storage_type punctuation_definition_storage_type_objc"&gt;@&lt;/span&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Add the source module &lt;code&gt;RROutlineViewExpandedItemsAutosaver.m&lt;/code&gt;. Contents follow.&lt;/p&gt;

&lt;div class="textmate-source dawn"&gt;

&lt;pre class="textmate-source"&gt;&lt;span class="source source_objc"&gt;&lt;span class="meta meta_preprocessor meta_preprocessor_c meta_preprocessor_c_include"&gt;#&lt;span class="keyword keyword_control keyword_control_import keyword_control_import_include keyword_control_import_include_c"&gt;import&lt;/span&gt; &lt;span class="string string_quoted string_quoted_double string_quoted_double_include string_quoted_double_include_c"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_c"&gt;"&lt;/span&gt;RROutlineViewExpandedItemsAutosaver.h&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_c"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class="meta meta_implementation meta_implementation_objc"&gt;&lt;span class="storage storage_type storage_type_objc"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_storage punctuation_definition_storage_type punctuation_definition_storage_type_objc"&gt;@&lt;/span&gt;implementation&lt;/span&gt; &lt;span class="entity entity_name entity_name_type entity_name_type_objc"&gt;RROutlineViewExpandedItemsAutosaver&lt;/span&gt;
&lt;span class="meta meta_scope meta_scope_implementation meta_scope_implementation_objc"&gt;
&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt;------------------------------------------------------------------------------
&lt;/span&gt;&lt;span class="meta meta_section"&gt;&lt;span class="meta meta_preprocessor meta_preprocessor_c"&gt;#&lt;span class="keyword keyword_control keyword_control_import keyword_control_import_pragma keyword_control_import_pragma_c"&gt;pragma mark&lt;/span&gt; &lt;span class="meta meta_toc-list meta_toc-list_pragma-mark meta_toc-list_pragma-mark_c"&gt;Outline View Data Source&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt;------------------------------------------------------------------------------
&lt;/span&gt;
&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; Although linked to the core-data context via bindings and a tree controller,
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; the outline view also has an instance of this object connected to the outline
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; view's data source.
&lt;/span&gt;
&lt;span class="meta meta_function-with-body meta_function-with-body_objc"&gt;&lt;span class="meta meta_function meta_function_objc"&gt;- &lt;span class="meta meta_return-type meta_return-type_objc"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;(&lt;/span&gt;&lt;span class="storage storage_type storage_type_id storage_type_id_objc"&gt;id&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;)&lt;/span&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_objc"&gt;outlineView&lt;/span&gt;&lt;/span&gt;&lt;span class="meta meta_argument-type meta_argument-type_objc"&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc"&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc"&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;(&lt;/span&gt;&lt;span class="support support_class support_class_cocoa"&gt;NSOutlineView&lt;/span&gt; *&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;)&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_objc"&gt;outlineView&lt;/span&gt;&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc"&gt;itemForPersistentObject&lt;/span&gt;&lt;span class="meta meta_argument-type meta_argument-type_objc"&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc"&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc"&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;(&lt;/span&gt;&lt;span class="storage storage_type storage_type_id storage_type_id_objc"&gt;id&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;)&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_objc"&gt;object&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class="meta meta_block meta_block_c"&gt;{
    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; Iterate all the items. This is not straightforward because the outline
&lt;/span&gt;    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; view items are nested. So you cannot just iterate the rows. Rows
&lt;/span&gt;    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; correspond to root nodes only. The outline view interface does not
&lt;/span&gt;    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; provide any means to query the hidden children within each collapsed row
&lt;/span&gt;    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; either. However, the root nodes do respond to -childNodes. That makes it
&lt;/span&gt;    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; possible to walk the tree.
&lt;/span&gt;    &lt;span class="support support_class support_class_cocoa"&gt;NSMutableArray&lt;/span&gt; *items = &lt;span class="meta meta_bracketed meta_bracketed_objc"&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;[&lt;/span&gt;&lt;span class="support support_class support_class_cocoa"&gt;NSMutableArray&lt;/span&gt; &lt;span class="meta meta_function-call meta_function-call_objc"&gt;&lt;span class="support support_function support_function_any-method support_function_any-method_objc"&gt;array&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;]&lt;/span&gt;&lt;/span&gt;;
    &lt;span class="support support_type support_type_cocoa support_type_cocoa_leopard"&gt;NSInteger&lt;/span&gt; i, rows = &lt;span class="meta meta_bracketed meta_bracketed_objc"&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;[&lt;/span&gt;outlineView &lt;span class="meta meta_function-call meta_function-call_objc"&gt;&lt;span class="support support_function support_function_any-method support_function_any-method_objc"&gt;numberOfRows&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;]&lt;/span&gt;&lt;/span&gt;;
    &lt;span class="keyword keyword_control keyword_control_c"&gt;for&lt;/span&gt;&lt;span class="meta meta_initialization meta_initialization_c"&gt; &lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_c"&gt;(&lt;/span&gt;&lt;/span&gt;i = &lt;span class="constant constant_numeric constant_numeric_c"&gt;0&lt;/span&gt;; i &amp;lt; rows; i++)
    &lt;span class="meta meta_block meta_block_c"&gt;{
        &lt;span class="meta meta_bracketed meta_bracketed_objc"&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;[&lt;/span&gt;items &lt;span class="meta meta_function-call meta_function-call_objc"&gt;&lt;span class="support support_function support_function_any-method support_function_any-method_objc"&gt;addObject&lt;span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc"&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class="meta meta_bracketed meta_bracketed_objc"&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;[&lt;/span&gt;outlineView &lt;span class="meta meta_function-call meta_function-call_objc"&gt;&lt;span class="support support_function support_function_any-method support_function_any-method_objc"&gt;itemAtRow&lt;span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc"&gt;:&lt;/span&gt;&lt;/span&gt;i&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;]&lt;/span&gt;&lt;/span&gt;;
    }&lt;/span&gt;
    &lt;span class="keyword keyword_control keyword_control_c"&gt;for&lt;/span&gt;&lt;span class="meta meta_initialization meta_initialization_c"&gt; &lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_c"&gt;(&lt;/span&gt;&lt;/span&gt;i = &lt;span class="constant constant_numeric constant_numeric_c"&gt;0&lt;/span&gt;; i &amp;lt; &lt;span class="meta meta_bracketed meta_bracketed_objc"&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;[&lt;/span&gt;items &lt;span class="meta meta_function-call meta_function-call_objc"&gt;&lt;span class="support support_function support_function_any-method support_function_any-method_objc"&gt;count&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;]&lt;/span&gt;&lt;/span&gt; &amp;amp;&amp;amp; !&lt;span class="meta meta_bracketed meta_bracketed_objc"&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;[&lt;/span&gt;object &lt;span class="meta meta_function-call meta_function-call_objc"&gt;&lt;span class="support support_function support_function_any-method support_function_any-method_objc"&gt;isEqualToString&lt;span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc"&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class="meta meta_bracketed meta_bracketed_objc"&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;[&lt;/span&gt;&lt;span class="meta meta_bracketed meta_bracketed_objc"&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;[&lt;/span&gt;&lt;span class="meta meta_bracketed meta_bracketed_objc"&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;[&lt;/span&gt;&lt;span class="meta meta_bracketed meta_bracketed_objc"&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;[&lt;/span&gt;&lt;span class="meta meta_bracketed meta_bracketed_objc"&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;[&lt;/span&gt;items &lt;span class="meta meta_function-call meta_function-call_objc"&gt;&lt;span class="support support_function support_function_any-method support_function_any-method_objc"&gt;objectAtIndex&lt;span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc"&gt;:&lt;/span&gt;&lt;/span&gt;i&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;]&lt;/span&gt;&lt;/span&gt; &lt;span class="meta meta_function-call meta_function-call_objc"&gt;&lt;span class="support support_function support_function_any-method support_function_any-method_objc"&gt;representedObject&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;]&lt;/span&gt;&lt;/span&gt; &lt;span class="meta meta_function-call meta_function-call_objc"&gt;&lt;span class="support support_function support_function_any-method support_function_any-method_objc"&gt;objectID&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;]&lt;/span&gt;&lt;/span&gt; &lt;span class="meta meta_function-call meta_function-call_objc"&gt;&lt;span class="support support_function support_function_any-method support_function_any-method_objc"&gt;URIRepresentation&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;]&lt;/span&gt;&lt;/span&gt; &lt;span class="meta meta_function-call meta_function-call_objc"&gt;&lt;span class="support support_function support_function_any-method support_function_any-method_objc"&gt;absoluteString&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;]&lt;/span&gt;&lt;/span&gt;; i++)
    &lt;span class="meta meta_block meta_block_c"&gt;{
        &lt;span class="meta meta_bracketed meta_bracketed_objc"&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;[&lt;/span&gt;items &lt;span class="meta meta_function-call meta_function-call_objc"&gt;&lt;span class="support support_function support_function_any-method support_function_any-method_objc"&gt;addObjectsFromArray&lt;span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc"&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class="meta meta_bracketed meta_bracketed_objc"&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;[&lt;/span&gt;&lt;span class="meta meta_bracketed meta_bracketed_objc"&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;[&lt;/span&gt;items &lt;span class="meta meta_function-call meta_function-call_objc"&gt;&lt;span class="support support_function support_function_any-method support_function_any-method_objc"&gt;objectAtIndex&lt;span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc"&gt;:&lt;/span&gt;&lt;/span&gt;i&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;]&lt;/span&gt;&lt;/span&gt; &lt;span class="meta meta_function-call meta_function-call_objc"&gt;&lt;span class="support support_function support_function_any-method support_function_any-method_objc"&gt;childNodes&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;]&lt;/span&gt;&lt;/span&gt;;
    }&lt;/span&gt;
    &lt;span class="keyword keyword_control keyword_control_c"&gt;return&lt;/span&gt; i &amp;lt; &lt;span class="meta meta_bracketed meta_bracketed_objc"&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;[&lt;/span&gt;items &lt;span class="meta meta_function-call meta_function-call_objc"&gt;&lt;span class="support support_function support_function_any-method support_function_any-method_objc"&gt;count&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;]&lt;/span&gt;&lt;/span&gt; ? &lt;span class="meta meta_bracketed meta_bracketed_objc"&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;[&lt;/span&gt;items &lt;span class="meta meta_function-call meta_function-call_objc"&gt;&lt;span class="support support_function support_function_any-method support_function_any-method_objc"&gt;objectAtIndex&lt;span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc"&gt;:&lt;/span&gt;&lt;/span&gt;i&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;]&lt;/span&gt;&lt;/span&gt; : &lt;span class="constant constant_language constant_language_objc"&gt;nil&lt;/span&gt;;
}&lt;/span&gt;&lt;/span&gt;

&lt;span class="meta meta_function-with-body meta_function-with-body_objc"&gt;&lt;span class="meta meta_function meta_function_objc"&gt;- &lt;span class="meta meta_return-type meta_return-type_objc"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;(&lt;/span&gt;&lt;span class="storage storage_type storage_type_id storage_type_id_objc"&gt;id&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;)&lt;/span&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_objc"&gt;outlineView&lt;/span&gt;&lt;/span&gt;&lt;span class="meta meta_argument-type meta_argument-type_objc"&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc"&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc"&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;(&lt;/span&gt;&lt;span class="support support_class support_class_cocoa"&gt;NSOutlineView&lt;/span&gt; *&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;)&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_objc"&gt;outlineView&lt;/span&gt;&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc"&gt;persistentObjectForItem&lt;/span&gt;&lt;span class="meta meta_argument-type meta_argument-type_objc"&gt;&lt;span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc"&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc"&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;(&lt;/span&gt;&lt;span class="storage storage_type storage_type_id storage_type_id_objc"&gt;id&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc"&gt;)&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_objc"&gt;item&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class="meta meta_block meta_block_c"&gt;{
    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; "Persistent object" means a unique representation of the item's object,
&lt;/span&gt;    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; representing the objects identity, not its state. Outline view writes
&lt;/span&gt;    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; this to user defaults as soon as the item expands. That's when it asks
&lt;/span&gt;    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; for the persistent object, sending -outlineView:persistentObjectForItem:
&lt;/span&gt;    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; and execution arrives here. A minor problem arises when adding new
&lt;/span&gt;    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; items. The new item represents a new unsaved managed object. The managed
&lt;/span&gt;    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; object only has a temporary object identifier. It will receive a
&lt;/span&gt;    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; permanent one when saved. So, if the objectID answers a temporary one,
&lt;/span&gt;    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; ask the context to save and re-request the objectID. The second request
&lt;/span&gt;    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; gives a permanent identifier, assuming saving succeeds. Don't worry about
&lt;/span&gt;    &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; committing unsaved edits at this point.
&lt;/span&gt;    NSManagedObject *object = &lt;span class="meta meta_bracketed meta_bracketed_objc"&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;[&lt;/span&gt;item &lt;span class="meta meta_function-call meta_function-call_objc"&gt;&lt;span class="support support_function support_function_any-method support_function_any-method_objc"&gt;representedObject&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;]&lt;/span&gt;&lt;/span&gt;;
    NSManagedObjectID *objectID = &lt;span class="meta meta_bracketed meta_bracketed_objc"&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;[&lt;/span&gt;object &lt;span class="meta meta_function-call meta_function-call_objc"&gt;&lt;span class="support support_function support_function_any-method support_function_any-method_objc"&gt;objectID&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;]&lt;/span&gt;&lt;/span&gt;;
    &lt;span class="keyword keyword_control keyword_control_c"&gt;if&lt;/span&gt;&lt;span class="meta meta_initialization meta_initialization_c"&gt; &lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_c"&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class="meta meta_bracketed meta_bracketed_objc"&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;[&lt;/span&gt;objectID &lt;span class="meta meta_function-call meta_function-call_objc"&gt;&lt;span class="support support_function support_function_any-method support_function_any-method_objc"&gt;isTemporaryID&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;]&lt;/span&gt;&lt;/span&gt;)
    &lt;span class="meta meta_block meta_block_c"&gt;{
        &lt;span class="keyword keyword_control keyword_control_c"&gt;if&lt;/span&gt;&lt;span class="meta meta_initialization meta_initialization_c"&gt; &lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_c"&gt;(&lt;/span&gt;&lt;/span&gt;!&lt;span class="meta meta_bracketed meta_bracketed_objc"&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;[&lt;/span&gt;&lt;span class="meta meta_bracketed meta_bracketed_objc"&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;[&lt;/span&gt;object &lt;span class="meta meta_function-call meta_function-call_objc"&gt;&lt;span class="support support_function support_function_any-method support_function_any-method_objc"&gt;managedObjectContext&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;]&lt;/span&gt;&lt;/span&gt; &lt;span class="meta meta_function-call meta_function-call_objc"&gt;&lt;span class="support support_function support_function_any-method support_function_any-method_objc"&gt;save&lt;span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc"&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class="constant constant_language constant_language_c"&gt;NULL&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;]&lt;/span&gt;&lt;/span&gt;)
        &lt;span class="meta meta_block meta_block_c"&gt;{
            &lt;span class="keyword keyword_control keyword_control_c"&gt;return&lt;/span&gt; &lt;span class="constant constant_language constant_language_objc"&gt;nil&lt;/span&gt;;
        }&lt;/span&gt;
        objectID = &lt;span class="meta meta_bracketed meta_bracketed_objc"&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;[&lt;/span&gt;object &lt;span class="meta meta_function-call meta_function-call_objc"&gt;&lt;span class="support support_function support_function_any-method support_function_any-method_objc"&gt;objectID&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;]&lt;/span&gt;&lt;/span&gt;;
    }&lt;/span&gt;
    &lt;span class="keyword keyword_control keyword_control_c"&gt;return&lt;/span&gt; &lt;span class="meta meta_bracketed meta_bracketed_objc"&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;[&lt;/span&gt;&lt;span class="meta meta_bracketed meta_bracketed_objc"&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc"&gt;[&lt;/span&gt;objectID &lt;span class="meta meta_function-call meta_function-call_objc"&gt;&lt;span class="support support_function support_function_any-method support_function_any-method_objc"&gt;URIRepresentation&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;]&lt;/span&gt;&lt;/span&gt; &lt;span class="meta meta_function-call meta_function-call_objc"&gt;&lt;span class="support support_function support_function_any-method support_function_any-method_objc"&gt;absoluteString&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc"&gt;]&lt;/span&gt;&lt;/span&gt;;
}&lt;/span&gt;&lt;/span&gt;

&lt;/span&gt;&lt;span class="storage storage_type storage_type_objc"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_storage punctuation_definition_storage_type punctuation_definition_storage_type_objc"&gt;@&lt;/span&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Add this header and module to the project, under the Classes group. An instance of this class will become the new data source for the outline view.&lt;/p&gt;

&lt;h2&gt;Add the new data-source&lt;/h2&gt;

&lt;p&gt;Add a new instance of &lt;code&gt;RROutlineViewExpandedItemsAutosaver&lt;/code&gt; to the nib and connect the new instance to the &lt;code&gt;NSOutlineView&lt;/code&gt; as the outline view&amp;#8217;s &lt;code&gt;dataSource&lt;/code&gt;. Use Interface Builder. Drag an Object from the palette to the nib. Change its class in the Identity Inspector panel (Command-6). Then connect the &lt;code&gt;NSOutlineView&lt;/code&gt; to it as the new data source. It replaces the pre-existing connection between outline view and app delegate.&lt;/p&gt;

&lt;p&gt;After this, the nib is ready.&lt;/p&gt;

&lt;h1&gt;App delegate&lt;/h1&gt;

&lt;p&gt;Apple have used the standard Core Data application delegate. It builds a managed object model, persistent store co-ordinator and managed object context. The classic Core Data stack! Our changes need to add a message-send for enabling auto-save expanded items to the outline view. But it&amp;#8217;s not the only change required.&lt;/p&gt;

&lt;p&gt;There is a subtle problem. It concerns bindings and Core Data. The important requirement is that when the outline view tries to restore auto-saved expanded items, the outline view has its items already loaded. Otherwise how can it translate &amp;#8220;persistent objects&amp;#8221; to items. Persistent objects, in this context, refers to keys used in the application defaults to identify the set of expanded items; everything not expanded defaults to collapsed.&lt;/p&gt;

&lt;h2&gt;Moving where the application adds its persistent store&lt;/h2&gt;

&lt;p&gt;When the application loads, it automatically loads &lt;code&gt;MainMenu&lt;/code&gt; nib. All the object instances therein become instantiated. Indirectly, the managed object context and its other Core Data stack components come to life at this time. Therein lies the subtle problem. The Cocoa framework will not immediately load the Core Data objects if the context already has its store when nib-loading establishes the bindings. That&amp;#8217;s a mouthful. Basically, the order must go:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Establish bindings. Do this wholesale.&lt;/li&gt;
&lt;li&gt;Add persistent store. At this point, the outline view grabs its contents through bindings to the tree controller.&lt;/li&gt;
&lt;li&gt;Enable auto-save expanded items.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Simplify the &lt;code&gt;-persistentStoreCoordinator&lt;/code&gt; implementation to:&lt;/p&gt;

&lt;div class="textmate-source twilight"&gt;
&lt;pre&gt;
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
    if (persistentStoreCoordinator == nil) {
        persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc]
            initWithManagedObjectModel:[self managedObjectModel]];
    }
    return persistentStoreCoordinator;
}
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;And second, add a new method:&lt;/p&gt;

&lt;div class="textmate-source twilight"&gt;
&lt;pre&gt;
- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
    NSFileManager *fileManager;
    NSString *applicationSupportFolder = nil;
    NSURL *url;
    NSError *error;

    fileManager = [NSFileManager defaultManager];
    applicationSupportFolder = [self applicationSupportFolder];
    if (![fileManager fileExistsAtPath:applicationSupportFolder isDirectory:NULL]) {
        [fileManager createDirectoryAtPath:applicationSupportFolder attributes:nil];
    }

    url = [NSURL fileURLWithPath:[applicationSupportFolder
        stringByAppendingPathComponent:@"AbstractTree.xml"]];
    if (![[self persistentStoreCoordinator]
        addPersistentStoreWithType:NSXMLStoreType
                     configuration:nil
                               URL:url
                           options:nil
                             error:&amp;error]) {
        [[NSApplication sharedApplication] presentError:error];
    }    
    // Watch out for this! Make sure that the data is available before enabling
    // auto-save for expanded items. You can switch it on within the nib. Problem
    // though is that the data store must be available at nib awaking time!
    // Otherwise, how can the auto-saving of expanded items compare against
    // existing items in order to determine whether or not they should be restored
    // as expanded or not.
    //
    // There's another caveat. You need to let the bindings make the necessary
    // connections first, before connecting the Core Data context and persistent
    // store coordinator to the store. In other words, the store must be added
    // last. Here is a good place. Application-did-finish-launching occurs after
    // all nib awaking methods, after bindings have been established. Hence, when
    // the store gets added, the outline view immediately sees the contents
    // through its bindings. Otherwise the outline-view expanded items auto-saver
    // cannot see the items at all.
    [outlineView setAutosaveExpandedItems:YES];
}
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;That&amp;#8217;s it!&lt;/p&gt;

&lt;h1&gt;Successful test&lt;/h1&gt;

&lt;p&gt;Build and go. This time, when you expand nodes, quit then re-run, the sample correctly expands the previously-expanded items. Great. You can view the sample application&amp;#8217;s user defaults by typing, in Terminal:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;defaults read com.apple.dts.AbstractTreeApp
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It will list the saved expanded items array, resembling something along these lines:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{
    "NSOutlineView Items Nodes" =     (
        "x-coredata://68A35129-6EA4-45E7-B3FB-F1C15FDC02D9/Node/p104",
        "x-coredata://68A35129-6EA4-45E7-B3FB-F1C15FDC02D9/Node/p102"
    );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Download the complete sample project &lt;a href="http://blog.pioneeringsoftware.co.uk/files/AbstractTree.zip"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/PioneeringSoftware/~4/388629524" height="1" width="1"/&gt;</description>
      <pubDate>Wed, 10 Sep 2008 07:42:56 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:09ce9a4f-24c3-4fa9-a2f5-743a709dfb25</guid>
      <comments>http://blog.pioneeringsoftware.co.uk/2008/09/10/outline-view-tree-controller-and-itemforpersistentobject#comments</comments>
      <category>AppKit</category>
      <category>core</category>
      <category>data</category>
      <category>outline</category>
      <category>view</category>
      <category>tree</category>
      <category>controller</category>
      <trackback:ping>http://blog.pioneeringsoftware.co.uk/trackbacks?article_id=outline-view-tree-controller-and-itemforpersistentobject&amp;day=10&amp;month=09&amp;year=2008</trackback:ping>
      <link>http://feeds.feedburner.com/~r/PioneeringSoftware/~3/388629524/outline-view-tree-controller-and-itemforpersistentobject</link>
    <feedburner:origLink>http://blog.pioneeringsoftware.co.uk/2008/09/10/outline-view-tree-controller-and-itemforpersistentobject</feedburner:origLink></item>
    <item>
      <title>Organising view controllers, continued</title>
      <description>&lt;p&gt;The last article on this subject (&lt;a href="http://blog.pioneeringsoftware.co.uk/2008/08/29/organising-view-controllers"&gt;Organising view controllers&lt;/a&gt;) started looking at an example. Apple&amp;#8217;s ViewController sample illustrates basic use of multiple view controllers. This article completes the work.&lt;/p&gt;

&lt;p&gt;Download the &lt;a href="http://blog.pioneeringsoftware.co.uk/files/View_Controller_Mark_II.zip"&gt;source&lt;/a&gt; if you would rather skip ahead. Sometimes it&amp;#8217;s easier that way. You can see the code in its full context. Snippets and extracts don&amp;#8217;t always tell you everything you need to know.&lt;/p&gt;

&lt;h1&gt;Missing pieces&lt;/h1&gt;

&lt;p&gt;Where did we leave off? Application delegate and window controller exist but don&amp;#8217;t do much. The window controller has a stub for the pop-up action. It does nothing. Of course, the application needs some custom view controllers for handling the view-specific behaviours. The image view has an Open push-button for example. Click this and the controller presents an Open Panel letting the user change the view&amp;#8217;s image. Lake Don Pedro looks nice, but perhaps you fancy a change! The table view initialises some names, etc. Custom view controllers will add the view-specific controller behaviour.&lt;/p&gt;

&lt;p&gt;However, the biggest missing piece: the view meta-controller itself. The project needs a class for meta-controlling view controllers.&lt;/p&gt;

&lt;h1&gt;Start simple&lt;/h1&gt;

&lt;p&gt;What is the most basic requirement? Our test application (View Controller Mark II) wants to say &amp;#8220;load a view&amp;#8221; given by &lt;code&gt;@"CustomImageView"&lt;/code&gt;, a string. The meta-control architecture then loads the corresponding nib, constructs an instance of &lt;code&gt;CustomImageViewController&lt;/code&gt; and then hangs the result in a &lt;em&gt;graph&lt;/em&gt; of meta-controllers. At any time thereafter, any object method with access to the meta-control graph can access the view controller (i.e. &lt;code&gt;[vmc viewControllerForViewPath:@"CustomImageView"]&lt;/code&gt; where &lt;code&gt;vmc&lt;/code&gt; means the view meta-controller) and, from there, its view, represented object and title. Note, if there were nested sub-controllers, the view-path would read &lt;code&gt;@"CustomImageView.SomeOtherName"&lt;/code&gt;. SomeOtherName stands for a nested tier of control. In this case though, requirements only need non-nested controllers. Pity.&lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s take the &lt;code&gt;NSViewController&lt;/code&gt; class and bolt on a meta-controller. The new class associates with one view controller (its subordinate controller) and multiple subordinate meta-controllers by name. See diagram below.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://blog.pioneeringsoftware.co.uk/files/2008-08-31_view_meta_controller_class_diagram.png" alt="View Meta Controller class diagram"/&gt;&lt;/p&gt;

&lt;p&gt;The diagram indicates unidirectional associations using directed arrows. The &lt;code&gt;viewController&lt;/code&gt; association from meta-controller to view controller is unidirectional, in that direction. Likewise from name to subordinate meta-controller. Names are unique strings identifying lower levels of meta-control. This model forms a graph or tree: a hierarchy of named meta-controllers.&lt;/p&gt;

&lt;p&gt;It&amp;#8217;s a simple model. Note, no reverse navigation from view controller to meta-controller. Hence no need for new &lt;code&gt;NSViewController&lt;/code&gt; subclasses. The hierarchy lives outside the pre-existing view controller framework. Of course, the model supports view controller subclasses, but not for maintaining the meta-control graph.&lt;/p&gt;

&lt;p&gt;You might have noticed the diamond shape at the name-end of the meta-controller association; it marks an aggregation. In fact, in this case, it marks a self-aggregation, also known as circular. Some sources say that self-aggregation does not make sense. I&amp;#8217;m not so sure. In this instance, meta-controllers are &amp;#8220;parts of&amp;#8221; other meta-controllers. Aggregation is not a precise concept. Suffices to say there is a strong association between meta-controller parts.&lt;/p&gt;

&lt;h1&gt;Overlaying some methods&lt;/h1&gt;

&lt;p&gt;Of course, the diagram above shows &lt;code&gt;RRViewMetaController&lt;/code&gt; without methods. So not a very useful class, at present.&lt;/p&gt;

&lt;p&gt;Adding methods can prove challenging. It takes judgement, experience, skill, even art. There is never one &amp;#8220;right&amp;#8221; way. It&amp;#8217;s almost like pouring custard over your pudding! You don&amp;#8217;t want a thick blob at one side; a smooth even coating works best. Yes, I know, that carries it too far. But I&amp;#8217;m sure you know what I mean: behaviour needs judicious placement within a class structure. Methods are little packets of behaviour. Placing them, naming them is as important as implementing them, because in the long run mistakes can have an important impact on maintenance. When the custard goes cold, it gets harder to spread. I&amp;#8217;ll shut up about custard now (I must be hungry though I just ate).&lt;/p&gt;

&lt;p&gt;Meta-control will need methods for the following function groups.&lt;/p&gt;

&lt;h2&gt;Initialising and de-allocating&lt;/h2&gt;

&lt;p&gt;The usual housekeeping work.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-init&lt;/code&gt; initialises an instance&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-dealloc&lt;/code&gt; de-allocates an instance&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Accessing properties&lt;/h2&gt;

&lt;p&gt;Every meta-controller controls a subordinate view controller. Other objects need access to this.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-viewController&lt;/code&gt; answers the associated view controller&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-view&lt;/code&gt; answers the view controller&amp;#8217;s view&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Adding and removing from super-views&lt;/h2&gt;

&lt;p&gt;Every meta-controller directly and indirectly manages a triplet: a view controller, a nib and a view hierarchy. The view hierarchy does not automatically enter any given window undirected. The design requires methods for adding and removing the view, along with any sub-views.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-addToSuperview:aSuperview&lt;/code&gt; adds the meta-controlled view to &lt;code&gt;aSuperview&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-removeFromSuperview&lt;/code&gt; removes the meta-controlled view from its super-view&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Naming and removing meta-controllers&lt;/h2&gt;

&lt;p&gt;Accessing meta-controllers&amp;#8217; names, removing them if necessary.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-namesOfMetaControllers&lt;/code&gt; gives all the names of the subordinate meta-controllers&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-nameOfMetaController:aMetaController&lt;/code&gt; answers the name of &lt;code&gt;aMetaController&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-removeMetaController:aMetaController&lt;/code&gt; removes &lt;code&gt;aMetaController&lt;/code&gt; by identity&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Resolving and removing view paths&lt;/h2&gt;

&lt;p&gt;Given view-path strings, meta-controller instances answer corresponding controllers, loading view controllers and views as necessary.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-resolveViewPath:aViewPath&lt;/code&gt; resolves a view path answering an array of meta-controllers&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-removeViewPath:aViewPath&lt;/code&gt; removes the meta-controller identified by &lt;code&gt;aViewPath&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-viewMetaControllerForViewPath:aViewPath&lt;/code&gt; answers the view meta-controller identified by &lt;code&gt;aViewPath&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-viewControllerForViewPath:aViewPath&lt;/code&gt; shortcuts the view controller accessor, answering a view controller for a given view path&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Adding these to the class diagram:&lt;/p&gt;

&lt;p&gt;&lt;img src="http://blog.pioneeringsoftware.co.uk/files/2008-09-02_view_meta_controller_class_diagram_ii.png" alt="View Meta Controller class diagram (ii)"/&gt;&lt;/p&gt;

&lt;p&gt;Notice the method signatures reflect C++ and Java language styling. Method name comes before arguments and arguments appear in parentheses. Of course, in real life, Objective-C intermingles signature and arguments. Hence you see &lt;code&gt;addToSuperview:&lt;/code&gt; as the name, with &lt;code&gt;aSuperView&lt;/code&gt; : &lt;code&gt;NSView&lt;/code&gt; as the argument-and-type pair. I&amp;#8217;ve also skipped the pointer asterisk as implied. I hope it makes sense. Note also, plus prefix means public in this context, not a class method.&lt;/p&gt;

&lt;h1&gt;Interface, RRViewMetaController.h&lt;/h1&gt;

&lt;p&gt;Turning the requirements into Objective-C:&lt;/p&gt;

&lt;div class="textmate-source dawn"&gt;

&lt;pre class="textmate-source"&gt;&lt;span class="source source_objc"&gt;&lt;span class="meta meta_preprocessor meta_preprocessor_c meta_preprocessor_c_include"&gt;#&lt;span class="keyword keyword_control keyword_control_import keyword_control_import_include keyword_control_import_include_c"&gt;import&lt;/span&gt; &lt;span class="string string_quoted string_quoted_other string_quoted_other_lt-gt string_quoted_other_lt-gt_include string_quoted_other_lt-gt_include_c"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_c"&gt;&amp;lt;&lt;/span&gt;AppKit/AppKit.h&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_c"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; Meta refers to a position beyond, of a higher or second-order kind. Thus, a
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; meta-controller controls controllers! Hence, the view meta-controller class
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; controls view controllers.
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; Notice, the class does not implement a back-reference to the parent
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; meta-controller. True, even though they form a tree. View paths specify leaf
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; nodes. The class resolves the path by walking from root to leaf, recording
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; the journey at each step. This approach obviates references to parent nodes.
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c"&gt;//&lt;/span&gt; Effectively, the graph of meta-controllers "hovers" above the view
&lt;/span&gt;&lt;span class="comment comment_line comment_line_double-slash comment_line_doubl