<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">
 
 <title>Richard Hult</title>
 
 <link href="http://rhult.github.com/" />
 <updated>2011-09-22T02:42:51-07:00</updated>
 <id>http://rhult.github.com/</id>
 <author>
   <name>Richard Hult</name>
   <!--email></email-->
 </author>

 
 <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/RichardHult" /><feedburner:info uri="richardhult" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:browserFriendly></feedburner:browserFriendly><entry>
   <title>Small gotcha when using NSView as delegate for CALayer</title>
   <link href="http://rhult.github.com/calayer-delegate-nsview-gotcha.html" />
   <updated>2010-01-07T00:00:00-08:00</updated>
   <id>http://rhult.github.com/calayer-delegate-nsview-gotcha</id>
   <content type="html">&lt;p&gt;I ran into another small unexpected behavior when working on some Core Animation code recently. I had a subclass of NSView that acted as a layer-hosting view. The layers that the view hosted had the view set as delegate to do some simple drawing.&lt;/p&gt;

&lt;p&gt;I didn&amp;#8217;t want the layers to animate their bounds so I did the usual:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='objc'&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;layer&lt;/span&gt; &lt;span class='nl'&gt;setActions:&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;NSDictionary&lt;/span&gt; &lt;span class='nl'&gt;dictionaryWithObjectsAndKeys:&lt;/span&gt;
                   &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;NSNull&lt;/span&gt; &lt;span class='n'&gt;null&lt;/span&gt;&lt;span class='p'&gt;],&lt;/span&gt; &lt;span class='s'&gt;@&amp;quot;bounds&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                   &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;NSNull&lt;/span&gt; &lt;span class='n'&gt;null&lt;/span&gt;&lt;span class='p'&gt;],&lt;/span&gt; &lt;span class='s'&gt;@&amp;quot;position&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                   &lt;span class='nb'&gt;nil&lt;/span&gt;&lt;span class='p'&gt;]];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&amp;#8230;which to my surprise didn&amp;#8217;t work. After digging around for a while I found out that NSView implements &lt;code&gt;actionForLayer:forKey&lt;/code&gt;, which has precedence over the actions dictionary. The quick and dirty solution was to override it and return a &lt;code&gt;null&lt;/code&gt; action for the relevant keys:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='objc'&gt;&lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kt'&gt;id&lt;/span&gt;&lt;span class='o'&gt;&amp;lt;&lt;/span&gt;&lt;span class='n'&gt;CAAction&lt;/span&gt;&lt;span class='o'&gt;&amp;gt;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='nl'&gt;actionForLayer:&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;CALayer&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;layer&lt;/span&gt; &lt;span class='nl'&gt;forKey:&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;NSString&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;event&lt;/span&gt;
&lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='p'&gt;([&lt;/span&gt;&lt;span class='n'&gt;event&lt;/span&gt; &lt;span class='nl'&gt;isEqualToString:&lt;/span&gt;&lt;span class='s'&gt;@&amp;quot;position&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='o'&gt;||&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;event&lt;/span&gt; &lt;span class='nl'&gt;isEqualToString:&lt;/span&gt;&lt;span class='s'&gt;@&amp;quot;bounds&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;])&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
        &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kt'&gt;id&lt;/span&gt;&lt;span class='o'&gt;&amp;lt;&lt;/span&gt;&lt;span class='n'&gt;CAAction&lt;/span&gt;&lt;span class='o'&gt;&amp;gt;&lt;/span&gt;&lt;span class='p'&gt;)[&lt;/span&gt;&lt;span class='n'&gt;NSNull&lt;/span&gt; &lt;span class='n'&gt;null&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
    &lt;span class='p'&gt;}&lt;/span&gt;

    &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='nb'&gt;nil&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;However, my guess is that NSView is not really meant to be used as delegate for its hosted layers like this. A better design in most cases would be to either subclass the layer to do its own drawing, or to use a separate controller for event handling or other book keeping for the layer(s).&lt;/p&gt;

&lt;p&gt;So in reality this is not really a problem but I thought I&amp;#8217;d post it here in case someone else runs into this problem.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>KVO problems with CALayer on 10.5?</title>
   <link href="http://rhult.github.com/kvo-problems-with-calayer.html" />
   <updated>2009-12-13T00:00:00-08:00</updated>
   <id>http://rhult.github.com/kvo-problems-with-calayer</id>
   <content type="html">&lt;p&gt;I was trying out some code on 10.5 that was originally developed on 10.6, and ran into a number of really strange issues. At first they seemed almost random and unrelated but after debugging for a while it turned out they were all related to key-value observation in some way or another.&lt;/p&gt;

&lt;p&gt;After more debugging, it became clear that for some reason I was not getting any observer callbacks and Cocoa bindings weren&amp;#8217;t working for certain objects.&lt;/p&gt;

&lt;p&gt;This looked suspicious, and sure enough, implementing:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='objc'&gt;&lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kt'&gt;BOOL&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='nl'&gt;automaticallyNotifiesObserversForKey:&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;NSString&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;key&lt;/span&gt;
&lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='kt'&gt;BOOL&lt;/span&gt; &lt;span class='n'&gt;notifies&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;super&lt;/span&gt; &lt;span class='nl'&gt;automaticallyNotifiesObserversForKey:&lt;/span&gt;&lt;span class='n'&gt;key&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
    &lt;span class='n'&gt;NSLog&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s'&gt;@&amp;quot;Notifies for key %@: %d&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;key&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;notifies&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
    &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='n'&gt;notifies&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;in one of the affected classes showed that the return value was always &lt;code&gt;NO&lt;/code&gt;, even for properties that I had declared myself!&lt;/p&gt;

&lt;p&gt;The reason is that CALayer&amp;#8217;s implementation of &lt;code&gt;automaticallyNotifiesObserversForKey:&lt;/code&gt; in 10.5 returns &lt;code&gt;NO&lt;/code&gt; for all keys. In 10.6 it works as expected, returning only &lt;code&gt;NO&lt;/code&gt; for its own properties.&lt;/p&gt;

&lt;p&gt;Fortunately the work-around is simple: just implement the class method yourself and return &lt;code&gt;YES&lt;/code&gt; for your own properties:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='objc'&gt;&lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kt'&gt;BOOL&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='nl'&gt;automaticallyNotifiesObserversForKey:&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;NSString&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;key&lt;/span&gt;
&lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='p'&gt;([&lt;/span&gt;&lt;span class='n'&gt;key&lt;/span&gt; &lt;span class='nl'&gt;isEqualToString:&lt;/span&gt;&lt;span class='s'&gt;@&amp;quot;myKey&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;])&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
        &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='n'&gt;YES&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
    &lt;span class='p'&gt;}&lt;/span&gt;

    &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;super&lt;/span&gt; &lt;span class='nl'&gt;automaticallyNotifiesObserversForKey:&lt;/span&gt;&lt;span class='n'&gt;key&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;</content>
 </entry>
 
 <entry>
   <title>Tinybird – here we go!</title>
   <link href="http://rhult.github.com/tinybird-here-we-go.html" />
   <updated>2009-11-30T00:00:00-08:00</updated>
   <id>http://rhult.github.com/tinybird-here-we-go</id>
   <content type="html">&lt;p&gt;If you follow me on twitter, you have already noticed that our new company &lt;a href='http://tinybird.com/'&gt;Tinybird&lt;/a&gt; was announced last week. Some of the code posts here in my blog were written as side-effect of working on Harmony, the break reminder tool that Tinybird is giving away as a small launching gift. I hope you&amp;#8217;ll enjoy it!&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Height-for-width layout with CATextLayer (part 2)</title>
   <link href="http://rhult.github.com/height-for-width-catextlayer-part2.html" />
   <updated>2009-10-27T00:00:00-07:00</updated>
   <id>http://rhult.github.com/height-for-width-catextlayer-part2</id>
   <content type="html">&lt;p&gt;Last week I wrote about my experiments to get a &lt;a href='/height-for-width-catextlayer.html'&gt;height-for-width layout&lt;/a&gt; working with a Core Animation text layer. In other words, a layer that resizes vertically to fit its text within a certain width. So far we have the text measuring right and now it&amp;#8217;s time to put it to use.&lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s start with an illustration to show what the end goal is:&lt;/p&gt;
&lt;div&gt;	
	&lt;center&gt;
		&lt;img src='/images/posts/height-for-width-widest.png' style='width:15.3em; display:inline; vertical-align:bottom' /&gt;
		&lt;img src='/images/posts/height-for-width-wider.png' style='width:12.0em; display:inline; vertical-align:bottom' /&gt;
		&lt;img src='/images/posts/height-for-width-narrow.png' style='width:10.2em; display:inline; vertical-align:bottom' /&gt;
	&lt;/center&gt;
&lt;/div&gt;
&lt;p&gt;The text is automatically wrapped to fit the available size, which by itself is nothing new, as long as you manually provide the width and height, but in this case the size needs to be calculated from the height.&lt;/p&gt;

&lt;h3 id='layout_managers'&gt;Layout managers&lt;/h3&gt;

&lt;p&gt;The way Core Animation handles laying out layers is through the layout manager protocol:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='objc'&gt;&lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;CGSize&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='nl'&gt;preferredSizeOfLayer:&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;CALayer&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;layer&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kt'&gt;void&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='nl'&gt;layoutSublayersOfLayer:&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;CALayer&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;layer&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kt'&gt;void&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='nl'&gt;invalidateLayoutOfLayer:&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;CALayer&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;layer&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this post I will focus on the two former, &lt;code&gt;preferredSizeOfLayer:&lt;/code&gt; and &lt;code&gt;layoutSublayersOfLayer:&lt;/code&gt;. The latter is useful when you are caching results and need to invalidate them, but we ignore that for now.&lt;/p&gt;

&lt;h3 id='constraints'&gt;Constraints&lt;/h3&gt;

&lt;p&gt;The layout manager implementation shipped with Core Animation is called CAConstraintLayoutManager, and works by letting you apply constraints on certain properties of the layer&amp;#8217;s geometry. Those are min/mid/max for x/y, width and height. You can also scale and offset the resulting values by constant values which gives a very high level of freedom to set up simple or complex relationships between different layers. In our example, as seen on the images above, we have three layers: the white background, the blue frame, and the text. As I want the blue frame to be resized to fit the text, the easiest setup was to add both the blue frame and the text layer as direct sublayers of the white background. The constraints then become:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Resize the text layer&amp;#8217;s width to follow the width of the background (with some margin)&lt;/li&gt;

&lt;li&gt;Center the text layer horizontally within the background&lt;/li&gt;

&lt;li&gt;Adjust the text layer&amp;#8217;s bottom at the bottom of the background (with some margin)&lt;/li&gt;

&lt;li&gt;Make the blue frame follow the size of the text layer (with padding)&lt;/li&gt;

&lt;li&gt;Center the blue frame relative to the text layer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With the standard layout manager, this results in the text layer being one line and the text to be truncated at the right edge. This is because the text layer reports its preferred frame size to have the height of one line of text.&lt;/p&gt;

&lt;h3 id='the_heightforwidth_layout_manager'&gt;The height-for-width layout manager&lt;/h3&gt;

&lt;p&gt;Enter our custom layout manager. Since we still want to be able to use constraints, we subclass the constraints layout manager. By overriding its method &lt;code&gt;preferredSizeOfLayer:&lt;/code&gt; and have it assign the size we calculated in the previous post to the text layer, we can get the behavior we want (but with one caveat, more about that soon):&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='objc'&gt;&lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;CGSize&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='nl'&gt;preferredSizeOfLayer:&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;CALayer&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;layer&lt;/span&gt;
&lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='p'&gt;([&lt;/span&gt;&lt;span class='n'&gt;layer&lt;/span&gt; &lt;span class='nl'&gt;isKindOfClass:&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;CATextLayer&lt;/span&gt; &lt;span class='n'&gt;class&lt;/span&gt;&lt;span class='p'&gt;]]&lt;/span&gt; &lt;span class='o'&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class='p'&gt;((&lt;/span&gt;&lt;span class='n'&gt;CATextLayer&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;layer&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='n'&gt;wrapped&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
        &lt;span class='n'&gt;CGRect&lt;/span&gt; &lt;span class='n'&gt;bounds&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;layer&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;bounds&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
        &lt;span class='n'&gt;bounds&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;size&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;self&lt;/span&gt; &lt;span class='nl'&gt;frameSizeForTextLayer:&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;CATextLayer&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;layer&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
        &lt;span class='n'&gt;layer&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;bounds&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;bounds&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
    &lt;span class='p'&gt;}&lt;/span&gt;

    &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;super&lt;/span&gt; &lt;span class='nl'&gt;preferredSizeOfLayer:&lt;/span&gt;&lt;span class='n'&gt;layer&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The caveat is that the layout manager goes through the layers to resize and place them, including setting the width of the text. Setting the width of the text could change its height, and some constraints might need to be redone after doing that!&lt;/p&gt;

&lt;p&gt;This means we also have to override &lt;code&gt;layoutSublayersOfLayer:&lt;/code&gt; to add a small hack. We first invoke super&amp;#8217;s implementation to handle the normal constraints based layout. Then we setup the new heights for any text layers as a result of the first pass. Finally we invoke super&amp;#8217;s implementation again. As long as you don&amp;#8217;t have constraints that would change the text width in the second pass, this works nicely.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='objc'&gt;&lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kt'&gt;void&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='nl'&gt;layoutSublayersOfLayer:&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;CALayer&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;layer&lt;/span&gt;
&lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='c1'&gt;// First let the regular constraints kick in to set the width of text layers.&lt;/span&gt;
    &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;super&lt;/span&gt; &lt;span class='nl'&gt;layoutSublayersOfLayer:&lt;/span&gt;&lt;span class='n'&gt;layer&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;

    &lt;span class='c1'&gt;// Now adjust the height of any wrapped text layers, as their widths are known.&lt;/span&gt;
    &lt;span class='k'&gt;for&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;CALayer&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;child&lt;/span&gt; &lt;span class='k'&gt;in&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;layer&lt;/span&gt; &lt;span class='n'&gt;sublayers&lt;/span&gt;&lt;span class='p'&gt;])&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
        &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='p'&gt;([&lt;/span&gt;&lt;span class='n'&gt;child&lt;/span&gt; &lt;span class='nl'&gt;isKindOfClass:&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;CATextLayer&lt;/span&gt; &lt;span class='n'&gt;class&lt;/span&gt;&lt;span class='p'&gt;]])&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
            &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;self&lt;/span&gt; &lt;span class='nl'&gt;preferredSizeOfLayer:&lt;/span&gt;&lt;span class='n'&gt;child&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
        &lt;span class='p'&gt;}&lt;/span&gt;
    &lt;span class='p'&gt;}&lt;/span&gt;

    &lt;span class='c1'&gt;// Then let the regular constraints adjust any values that depend on heights.&lt;/span&gt;
    &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;super&lt;/span&gt; &lt;span class='nl'&gt;layoutSublayersOfLayer:&lt;/span&gt;&lt;span class='n'&gt;layer&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This is obviously stretching the intention of the constraint layout manager, but it works for simple and common layer trees like the one described here. There are also some easy opportunities for optimizing the code, as I tried to keep it as simple as possible (such as caching the text measuring, and not laying out unless necessary).&lt;/p&gt;

&lt;p&gt;I hope the posts and code will prove useful for someone else besides me. And as usual, if anyone knows a better way to do this, please let me know.&lt;/p&gt;

&lt;p&gt;The code is available as an Xcode project in a &lt;a href='http://github.com/rhult/height-for-width'&gt;git repo&lt;/a&gt; or &lt;a href='http://github.com/rhult/height-for-width/zipball/master'&gt;source package&lt;/a&gt;.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Height-for-width layout with CATextLayer</title>
   <link href="http://rhult.github.com/height-for-width-catextlayer.html" />
   <updated>2009-10-20T00:00:00-07:00</updated>
   <id>http://rhult.github.com/height-for-width-catextlayer</id>
   <content type="html">&lt;p&gt;For a project I&amp;#8217;m working on, I needed a Core Animation text layer that could adapt its height depending on the available width. This is commonly called a &amp;#8220;height-for-width&amp;#8221; layout. The stock CATextLayer and CAConstraintLayoutManager can&amp;#8217;t really do this, but since you can implement custom layout managers, that&amp;#8217;s where I started.&lt;/p&gt;

&lt;p&gt;The two most important methods of the layout manager protocol are:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='objc'&gt;&lt;span class='c1'&gt;// To implement in the CALayoutManager implementation:&lt;/span&gt;
&lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;CGSize&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='nl'&gt;preferredSizeOfLayer:&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;CALayer&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;layer&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kt'&gt;void&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='nl'&gt;layoutSublayersOfLayer:&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;CALayer&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;layer&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The idea was to override &lt;code&gt;preferredSizeOfLayer:&lt;/code&gt; to special-case any CATextLayers that had wrapping enabled and calculate the needed height for any given width. My first somewhat naive attempt was to use the AppKit&amp;#8217;s NSString additions like &lt;code&gt;sizeWithAttributes:&lt;/code&gt; or &lt;code&gt;boundingRectWithSize:options:attributes:&lt;/code&gt;. The results were almost right but not quite the same as what Core Animation itself would get. Using this approach, the layer would adjust its height according to the available width, but not exactly right. For some widths, the height would be one line to tall or short.&lt;/p&gt;

&lt;p&gt;The second option was to use the Cocoa text system and put together the various pieces in order to measure the height with some more control over the process. This consisted of creating an NSTextStorage instance (and setting up the text and its attributes), an NSTextContainer instance (with the right width, and &amp;#8220;infinite&amp;#8221; height), and an NSLayoutManager instance. After putting those together and forcing a layout which is otherwise done lazily, I got the bounds from the layout manager. The code looked something like this:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='objc'&gt;&lt;span class='c1'&gt;// Measures the height needed for a given width using the Cocoa text system:&lt;/span&gt;
&lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;CGSize&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='nl'&gt;frameSizeForTextLayer:&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;CATextLayer&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;layer&lt;/span&gt;
&lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='n'&gt;NSTextStorage&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;storage&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
    &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='p'&gt;([&lt;/span&gt;&lt;span class='n'&gt;layer&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;string&lt;/span&gt; &lt;span class='nl'&gt;isKindOfClass:&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;NSAttributedString&lt;/span&gt; &lt;span class='n'&gt;class&lt;/span&gt;&lt;span class='p'&gt;]])&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
        &lt;span class='n'&gt;storage&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[[&lt;/span&gt;&lt;span class='n'&gt;NSTextStorage&lt;/span&gt; &lt;span class='n'&gt;alloc&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='nl'&gt;initWithAttributedString:&lt;/span&gt;&lt;span class='n'&gt;layer&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;string&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
    &lt;span class='p'&gt;}&lt;/span&gt; &lt;span class='k'&gt;else&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
        &lt;span class='n'&gt;storage&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[[&lt;/span&gt;&lt;span class='n'&gt;NSTextStorage&lt;/span&gt; &lt;span class='n'&gt;alloc&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='nl'&gt;initWithString:&lt;/span&gt;&lt;span class='n'&gt;layer&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;string&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;

        &lt;span class='cm'&gt;/* ... set up the attributes for the storage, like the font ... */&lt;/span&gt;
    &lt;span class='p'&gt;}&lt;/span&gt;

    &lt;span class='n'&gt;NSTextContainer&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;container&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[[&lt;/span&gt;&lt;span class='n'&gt;NSTextContainer&lt;/span&gt; &lt;span class='n'&gt;alloc&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='nl'&gt;initWithContainerSize:&lt;/span&gt;&lt;span class='n'&gt;NSMakeSize&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;layer&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;bounds&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;size&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;width&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;FLT_MAX&lt;/span&gt;&lt;span class='p'&gt;)];&lt;/span&gt;
    &lt;span class='n'&gt;NSLayoutManager&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;manager&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[[&lt;/span&gt;&lt;span class='n'&gt;NSLayoutManager&lt;/span&gt; &lt;span class='n'&gt;alloc&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='n'&gt;init&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;

    &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;manager&lt;/span&gt; &lt;span class='nl'&gt;addTextContainer:&lt;/span&gt;&lt;span class='n'&gt;container&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
    &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;storage&lt;/span&gt; &lt;span class='nl'&gt;addLayoutManager:&lt;/span&gt;&lt;span class='n'&gt;manager&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;

    &lt;span class='c1'&gt;// The text layer doesn&amp;#39;t use fragment line padding.&lt;/span&gt;
    &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;container&lt;/span&gt; &lt;span class='nl'&gt;setLineFragmentPadding:&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;

    &lt;span class='c1'&gt;// Force layout, since it&amp;#39;s done lazily.&lt;/span&gt;
    &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;manager&lt;/span&gt; &lt;span class='nl'&gt;glyphRangeForTextContainer:&lt;/span&gt;&lt;span class='n'&gt;container&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;

    &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;manager&lt;/span&gt; &lt;span class='nl'&gt;usedRectForTextContainer:&lt;/span&gt;&lt;span class='n'&gt;container&lt;/span&gt;&lt;span class='p'&gt;].&lt;/span&gt;&lt;span class='n'&gt;size&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The result was much better this time, as a matter of fact so good that I thought it was finally right. But then I discovered that for some fonts there was still a small difference between CATextLayer&amp;#8217;s measurements and mine. I could not find any parameters that would remove the differences, but some investigations seemed to indicate that the difference was in how the line heights, line spacing or maximum line height was set up. Or perhaps there is some rounding going on in the Cocoa text system to get text lines to end up on evenly aligned pixel boundaries? Either way, I didn&amp;#8217;t really feel like going the trial-and-error way to get the (hopefully) right results&amp;#8230;&lt;/p&gt;

&lt;p&gt;After doing some more debugging in Xcode, it looked like CATextLayer actually uses Core Text directly, so I decided to try that next. This was quite similar to using the Cocoa text system, not surprising as the latter is built as a quite thin layer on top of the former.&lt;/p&gt;

&lt;p&gt;Finally, it looked like the results were matching CATextLayer! :) The code that does the text measuring now looked like the following:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='objc'&gt;&lt;span class='c1'&gt;// Measures the height needed for a given width using Core Text:&lt;/span&gt;
&lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;CGSize&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='nl'&gt;frameSizeForTextLayer:&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;CATextLayer&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;layer&lt;/span&gt;
&lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='n'&gt;NSAttributedString&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;string&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;self&lt;/span&gt; &lt;span class='nl'&gt;attributedStringForTextLayer:&lt;/span&gt;&lt;span class='n'&gt;layer&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
    &lt;span class='n'&gt;CTTypesetterRef&lt;/span&gt; &lt;span class='n'&gt;typesetter&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;CTTypesetterCreateWithAttributedString&lt;/span&gt;&lt;span class='p'&gt;((&lt;/span&gt;&lt;span class='n'&gt;CFAttributedStringRef&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;string&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
    &lt;span class='n'&gt;CGFloat&lt;/span&gt; &lt;span class='n'&gt;width&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;layer&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;bounds&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;size&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;width&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
    
    &lt;span class='n'&gt;CFIndex&lt;/span&gt; &lt;span class='n'&gt;offset&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;length&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
    &lt;span class='n'&gt;CGFloat&lt;/span&gt; &lt;span class='n'&gt;y&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
    &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
        &lt;span class='n'&gt;length&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;CTTypesetterSuggestLineBreak&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;typesetter&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;offset&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;width&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
        &lt;span class='n'&gt;CTLineRef&lt;/span&gt; &lt;span class='n'&gt;line&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;CTTypesetterCreateLine&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;typesetter&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;CFRangeMake&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;offset&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;length&lt;/span&gt;&lt;span class='p'&gt;));&lt;/span&gt;
        
        &lt;span class='n'&gt;CGFloat&lt;/span&gt; &lt;span class='n'&gt;ascent&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;descent&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;leading&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
        &lt;span class='n'&gt;CTLineGetTypographicBounds&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;line&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='n'&gt;ascent&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='n'&gt;descent&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='n'&gt;leading&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
        
        &lt;span class='n'&gt;CFRelease&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;line&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
        
        &lt;span class='n'&gt;offset&lt;/span&gt; &lt;span class='o'&gt;+=&lt;/span&gt; &lt;span class='n'&gt;length&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
        &lt;span class='n'&gt;y&lt;/span&gt; &lt;span class='o'&gt;+=&lt;/span&gt; &lt;span class='n'&gt;ascent&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='n'&gt;descent&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='n'&gt;leading&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
    &lt;span class='p'&gt;}&lt;/span&gt; &lt;span class='k'&gt;while&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;offset&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;string&lt;/span&gt; &lt;span class='n'&gt;length&lt;/span&gt;&lt;span class='p'&gt;]);&lt;/span&gt;
    
    &lt;span class='n'&gt;CFRelease&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;typesetter&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
    
    &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='n'&gt;CGSizeMake&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;width&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;ceil&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;y&lt;/span&gt;&lt;span class='p'&gt;));&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The method &lt;code&gt;attributedStringForTextLayer:&lt;/code&gt; sets up an NSAttributedString correctly from the string, font and fontSize properties of a text layer.&lt;/p&gt;

&lt;p&gt;At this point, the height-for-width layout was halfway through complete. The remaining issue left to solve was how make the layout manager do something useful with the calculated height and use it as an input for the regular constraints, for example to place something above the text layer, or to make a nice looking box sized to fit the wrapped text. But that, and the full source code will be available in an upcoming post soon. Stay tuned!&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Blog moved to github</title>
   <link href="http://rhult.github.com/blog-moved-to-github.html" />
   <updated>2009-10-12T00:00:00-07:00</updated>
   <id>http://rhult.github.com/blog-moved-to-github</id>
   <content type="html">&lt;p&gt;After trying out blogspot/blogger for a while, I realized it is not ideal for a code blog. &lt;a href='http://simply-hacking.org'&gt;Micke&lt;/a&gt; pointed me to the solution created by &lt;a href='http://tom.preston-werner.com/'&gt;Tom Preston-Werner&lt;/a&gt;, which looked perfect for my needs. Simple and very flexible, and since I sit all day in front of my terminal typing git commands, the work flow is also very familiar ;)&lt;/p&gt;

&lt;p&gt;Tom&amp;#8217;s post about the motivation for creating Jekyll (the site generator used to power the blog) is spot on: &lt;a href='http://tom.preston-werner.com/2008/11/17/blogging-like-a-hacker.html'&gt;Blogging Like a Hacker&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So, I have moved over the posts from my &lt;a href='http://rhult.blogspot.com/'&gt;old blog&lt;/a&gt;, and new posts will only appear here. See you!&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Getting HideOnLaunch right</title>
   <link href="http://rhult.github.com/getting-hideonlaunch-right.html" />
   <updated>2009-06-30T00:00:00-07:00</updated>
   <id>http://rhult.github.com/getting-hideonlaunch-right</id>
   <content type="html">&lt;p&gt;In a previous post I described how to set up an app to &lt;a href='preference-to-launch-on-login.html'&gt;launch automatically on login&lt;/a&gt;. After posting that, I&amp;#8217;ve noticed that it seems like the &amp;#8220;Hide on launch&amp;#8221; property often seems to be mixed up with the &lt;code&gt;kLSSharedFileListItemHidden&lt;/code&gt; key (for an example, see &lt;a href='http://lists.apple.com/archives/Cocoa-dev/2008/Feb/msg00632.html'&gt;this thread&lt;/a&gt; from 2008 on Cocoa-dev).&lt;/p&gt;

&lt;p&gt;So in an attempt to improve chances of people finding the right answer when searching for the incorrect key &lt;code&gt;kLSSharedFileListItemHidden&lt;/code&gt;, here is the relevant snippet again:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='objc'&gt;&lt;span class='n'&gt;NSDictionary&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;properties&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt;
    &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;NSDictionary&lt;/span&gt; &lt;span class='nl'&gt;dictionaryWithObject:&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;NSNumber&lt;/span&gt; &lt;span class='nl'&gt;numberWithBool:&lt;/span&gt;&lt;span class='n'&gt;YES&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
                                &lt;span class='c1'&gt;// NOTE, notice the key name:&lt;/span&gt;
                                &lt;span class='nl'&gt;forKey:&lt;/span&gt;&lt;span class='s'&gt;@&amp;quot;com.apple.loginitem.HideOnLaunch&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;

&lt;span class='n'&gt;LSSharedFileListItemRef&lt;/span&gt; &lt;span class='n'&gt;itemRef&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; 
    &lt;span class='n'&gt;LSSharedFileListInsertItemURL&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;loginItemsListRef&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                                  &lt;span class='n'&gt;kLSSharedFileListItemLast&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                                  &lt;span class='nb'&gt;NULL&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                                  &lt;span class='nb'&gt;NULL&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                                  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;CFURLRef&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;bundleURL&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                                  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;CFDictionaryRef&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;properties&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                                  &lt;span class='nb'&gt;NULL&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
&lt;span class='p'&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;</content>
 </entry>
 
 <entry>
   <title>Making an NSSlider snap to tick marks</title>
   <link href="http://rhult.github.com/making-nsslider-snap.html" />
   <updated>2009-05-26T00:00:00-07:00</updated>
   <id>http://rhult.github.com/making-nsslider-snap</id>
   <content type="html">&lt;p&gt;Have you noticed how the sliders in the energy savings panel in the system preferences behave a little differently from your average slider? They have this nice touch of resistance when you drag the slider across a tick mark, making it easy to end up exactly on a mark. I wanted the same behavior for a slider in my app, and found out that it isn&amp;#8217;t builtin in the slider. The way I solved it was by connecting an action to the slider, where I manually restrict the value a bit:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='objc'&gt;&lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kt'&gt;IBAction&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='nl'&gt;sliderValueChanged:&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kt'&gt;id&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;sender&lt;/span&gt;
&lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='kt'&gt;double&lt;/span&gt; &lt;span class='n'&gt;range&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;sender&lt;/span&gt; &lt;span class='n'&gt;maxValue&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;sender&lt;/span&gt; &lt;span class='n'&gt;minValue&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
    &lt;span class='kt'&gt;double&lt;/span&gt; &lt;span class='n'&gt;tickInterval&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;range&lt;/span&gt; &lt;span class='o'&gt;/&lt;/span&gt; &lt;span class='p'&gt;([&lt;/span&gt;&lt;span class='n'&gt;sender&lt;/span&gt; &lt;span class='n'&gt;numberOfTickMarks&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;    

    &lt;span class='kt'&gt;double&lt;/span&gt; &lt;span class='n'&gt;relativeValue&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;sender&lt;/span&gt; &lt;span class='n'&gt;doubleValue&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;sender&lt;/span&gt; &lt;span class='n'&gt;minValue&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;

    &lt;span class='c1'&gt;// Get the distance to the nearest tick.&lt;/span&gt;
    &lt;span class='kt'&gt;int&lt;/span&gt; &lt;span class='n'&gt;nearestTick&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;round&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;relativeValue&lt;/span&gt; &lt;span class='o'&gt;/&lt;/span&gt; &lt;span class='n'&gt;tickInterval&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
    &lt;span class='kt'&gt;double&lt;/span&gt; &lt;span class='n'&gt;distance&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;relativeValue&lt;/span&gt; &lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='n'&gt;nearestTick&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt; &lt;span class='n'&gt;tickInterval&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;

    &lt;span class='c1'&gt;// Change the check here depending on how much resistance you&lt;/span&gt;
    &lt;span class='c1'&gt;// want, or if you don&amp;#39;t want it to depend on the tick interval.&lt;/span&gt;
    &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;fabs&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;distance&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='n'&gt;tickInterval&lt;/span&gt; &lt;span class='o'&gt;/&lt;/span&gt; &lt;span class='mi'&gt;8&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
        &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;sender&lt;/span&gt; &lt;span class='nl'&gt;setDoubleValue:&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;sender&lt;/span&gt; &lt;span class='n'&gt;doubleValue&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='n'&gt;distance&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
    &lt;span class='p'&gt;}&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Then you have to make the slider continously perform the action when moved, instead of just when releasing it. This can be done either by checking the &amp;#8220;Continous&amp;#8221; check button in Interface Builder or programmatically using:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='objc'&gt;&lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kt'&gt;void&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='nl'&gt;setContinuous:&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kt'&gt;BOOL&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;flag&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;on the slider instance.&lt;/p&gt;

&lt;p&gt;There might be a better way to do this, if anyone knows about it I&amp;#8217;m all ears :)&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Keyboard shortcuts in Xcode and Interface Builder</title>
   <link href="http://rhult.github.com/keyboard-shortcuts-in-xcode-and-ib.html" />
   <updated>2009-04-29T00:00:00-07:00</updated>
   <id>http://rhult.github.com/keyboard-shortcuts-in-xcode-and-ib</id>
   <content type="html">&lt;p&gt;If you, like me, are trying to avoid using the mouse (for ergonomic reasons) as much as possible, you probably already have noticed that the Mac is quite alright when it comes to keyboard accessibility. This includes Xcode and Interface Builder, even though the latter by nature requires quite a bit of mouse wrestling. There are also some nice features that can help you having to type less.&lt;/p&gt;

&lt;p&gt;Recently, I have been trying to collect the most useful keyboard shortcuts and really learn them so I&amp;#8217;m not tempted to use the mouse more than necessary. Here&amp;#8217;s the list so far:&lt;/p&gt;

&lt;h3 id='xcode'&gt;Xcode&lt;/h3&gt;

&lt;p&gt;Besides all the normal text editing shortcuts, I often use those: (⇧ = shift, ⌘ = cmd, ^ = ctrl, ⌥ = option)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⇧⌘E = Zoom the editor by hiding the list above it&lt;/li&gt;

&lt;li&gt;⇧⌘D = Open quickly, useful for quickly open any file in the project our elsewhere&lt;/li&gt;

&lt;li&gt;^⌘T = Edit all in scope, this saves a lot of tedious editing&lt;/li&gt;

&lt;li&gt;^/ = Next placeholder in completions&lt;/li&gt;

&lt;li&gt;^. = Toggle between completions&lt;/li&gt;

&lt;li&gt;⌥⌘-Up = Toggle between the header/source&lt;/li&gt;

&lt;li&gt;^1 = Pop up the file history menu, useful when navigating the project files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The shortcut ^. deserves some extra attention, as it also completes text macros which can save a lot of typing. There is a whole slew of macros that you can use, just a few examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pim = expands to #import &amp;#8220;file&amp;#8221;, highlighting file so you can change it easily&lt;/li&gt;

&lt;li&gt;a = expands to the standard alloc/init combination&lt;/li&gt;

&lt;li&gt;init = expands to a standard init skeleton&lt;/li&gt;

&lt;li&gt;dealloc = expands to the standard dealloc skeleton&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id='interface_builder'&gt;Interface Builder&lt;/h3&gt;

&lt;p&gt;Interface builder also has a few ones I often use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⌘ while resizing a window = live autoresizing&lt;/li&gt;

&lt;li&gt;^⌘ + up/down = select parent/child of selected view&lt;/li&gt;

&lt;li&gt;^⌘ + left/right = select previous/next sibling&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id='global_shortcuts'&gt;Global shortcuts&lt;/h3&gt;

&lt;p&gt;Finally I have a tiny list of desktop wide shortcuts (obviously in addition to the well known ones like ⌘-Tab etc) I sometimes find useful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;^F2 = Focus the application menu&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&amp;#8230; I said it&amp;#8217;s tiny!&lt;/p&gt;

&lt;p&gt;I hope this can be useful for others as well.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Adding a preference to launch app on login using the Shared File List API</title>
   <link href="http://rhult.github.com/preference-to-launch-on-login.html" />
   <updated>2009-04-16T00:00:00-07:00</updated>
   <id>http://rhult.github.com/preference-to-launch-on-login</id>
   <content type="html">&lt;p&gt;I wanted to make my app launch automatically on login, and looked around for ways to do that programmatically. I first found two ways to do it, both a little bit less than ideal: either using Apple Events to talk to System Events, or by using NSUserDefaults (or CFPreferences) to tweak values in the persistent domain &lt;code&gt;loginwindow&lt;/code&gt; (as opposed to the app&amp;#8217;s own persistent domain where the preferences for the app are stored). The Apple Events way seemed a bit hackish to me, as did the NSUserDefaults way, considering it meant poking at preferences you don&amp;#8217;t own.&lt;/p&gt;

&lt;p&gt;Nonetheless, I decided to try the latter, so I went ahead and implemented a simple controller that exposed only one property that I could then bind to a check box button in Interface Builder using Cocoa bindings:&lt;/p&gt;

&lt;p&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='objc'&gt;&lt;span class='k'&gt;@property&lt;/span&gt; &lt;span class='kt'&gt;BOOL&lt;/span&gt; &lt;span class='n'&gt;launchOnLogin&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;To show more clearly what I did, here&amp;#8217;s the part that reads the current login items:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='objc'&gt;&lt;span class='n'&gt;NSUserDefaults&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;defaults&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='n'&gt;NSDictionary&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;domain&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;

&lt;span class='n'&gt;defaults&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;NSUserDefaults&lt;/span&gt; &lt;span class='n'&gt;standardUserDefaults&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
  
&lt;span class='n'&gt;domain&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;defaults&lt;/span&gt; &lt;span class='nl'&gt;persistentDomainForName:&lt;/span&gt;&lt;span class='s'&gt;@&amp;quot;loginwindow&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
&lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;domain&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='n'&gt;NSArray&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;items&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;domain&lt;/span&gt; &lt;span class='nl'&gt;objectForKey:&lt;/span&gt;&lt;span class='s'&gt;@&amp;quot;AutoLaunchedApplicationDictionary&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;

    &lt;span class='k'&gt;for&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;NSDictionary&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;entry&lt;/span&gt; &lt;span class='k'&gt;in&lt;/span&gt; &lt;span class='n'&gt;items&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
        &lt;span class='n'&gt;NSString&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;entryPath&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;entry&lt;/span&gt; &lt;span class='nl'&gt;objectForKey:&lt;/span&gt;&lt;span class='s'&gt;@&amp;quot;Path&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
        &lt;span class='c1'&gt;// ... do something with the entry ...&lt;/span&gt;
    &lt;span class='p'&gt;}&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The writing part is similar, getting a copy of the existing array, then adding or removing our own item and then setting as the new array:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='objc'&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;domain&lt;/span&gt; &lt;span class='nl'&gt;setObject:&lt;/span&gt;&lt;span class='n'&gt;updatedItems&lt;/span&gt; &lt;span class='nl'&gt;forKey:&lt;/span&gt;&lt;span class='s'&gt;@&amp;quot;AutoLaunchedApplicationDictionary&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;

&lt;span class='c1'&gt;// Update the setting.&lt;/span&gt;
&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;defaults&lt;/span&gt; &lt;span class='nl'&gt;setPersistentDomain:&lt;/span&gt;&lt;span class='n'&gt;domain&lt;/span&gt; &lt;span class='nl'&gt;forName:&lt;/span&gt;&lt;span class='s'&gt;@&amp;quot;loginwindow&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;

&lt;span class='c1'&gt;// Write changes to disk so the system settings will be up to date&lt;/span&gt;
&lt;span class='c1'&gt;// if opened before the changes are automatically synced.&lt;/span&gt;
&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;defaults&lt;/span&gt; &lt;span class='n'&gt;synchronize&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;While this approach works in general, there&amp;#8217;s a problem: the login items list in the system settings panel is not updated when changing the setting in my app&amp;#8217;s preferences, and the other way around. Maybe not a big problem, but it made me search some more, which led me to the following note in the release notes for the Launch Services framework in Leopard:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Shared File List API is new to Launch Services in Mac OS X Leopard. This API provides access to several kinds of system-global and per-user persistent lists of file system objects, such as recent documents and applications, favorites, and login items. For details, see the new interface file LSSharedFileList.h.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The mentioned header file is the only documentation for this new API, but it is quite straightforward: you create an instance of the right list, in this case the login items list for the session. Then there is a bunch of things you can do with the list, what&amp;#8217;s interesting for our use case is to get a copy of the array in the list, to add or remove an item, or to register an observer callback for a list. The callback is called when the list changes, which makes it possible for the UI to update accordingly.&lt;/p&gt;

&lt;p&gt;Since my app already depends on Leopard, I decided to rewrite my code using LSSharedFileList. The UI was already set up to use the simple controller described above, so a quick rewrite of the controller was enough. I create the list in my init method and add an observer:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='objc'&gt;&lt;span class='n'&gt;loginItemsListRef&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;LSSharedFileListCreate&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;NULL&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                                           &lt;span class='n'&gt;kLSSharedFileListSessionLoginItems&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                                           &lt;span class='nb'&gt;NULL&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
&lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;loginItemsListRef&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='c1'&gt;// Add an observer so we can update the UI if changed externally.&lt;/span&gt;
    &lt;span class='n'&gt;LSSharedFileListAddObserver&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;loginItemsListRef&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                                &lt;span class='n'&gt;CFRunLoopGetMain&lt;/span&gt;&lt;span class='p'&gt;(),&lt;/span&gt;
                                &lt;span class='n'&gt;kCFRunLoopCommonModes&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                                &lt;span class='n'&gt;loginItemsChanged&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                                &lt;span class='n'&gt;self&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The matching tearing down is done in dealloc:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='objc'&gt;&lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;loginItemsListRef&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='n'&gt;LSSharedFileListRemoveObserver&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;loginItemsListRef&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                                   &lt;span class='n'&gt;CFRunLoopGetMain&lt;/span&gt;&lt;span class='p'&gt;(),&lt;/span&gt;
                                   &lt;span class='n'&gt;kCFRunLoopCommonModes&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                                   &lt;span class='n'&gt;loginItemsChanged&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                                   &lt;span class='n'&gt;self&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
    &lt;span class='n'&gt;CFRelease&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;loginItemsListRef&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The callback for the observer is a plain old C funtion:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='objc'&gt;&lt;span class='k'&gt;static&lt;/span&gt; &lt;span class='kt'&gt;void&lt;/span&gt;
&lt;span class='nf'&gt;loginItemsChanged&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;LSSharedFileListRef&lt;/span&gt; &lt;span class='n'&gt;listRef&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kt'&gt;void&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;context&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='n'&gt;LoginItemsController&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;controller&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;context&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;

    &lt;span class='c1'&gt;// Emit change notification for the bindnings. We can&amp;#39;t do will/did&lt;/span&gt;
    &lt;span class='c1'&gt;// around the change but this will have to do.&lt;/span&gt;
    &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;controller&lt;/span&gt; &lt;span class='nl'&gt;willChangeValueForKey:&lt;/span&gt;&lt;span class='s'&gt;@&amp;quot;launchOnLogin&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
    &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;controller&lt;/span&gt; &lt;span class='nl'&gt;didChangeValueForKey:&lt;/span&gt;&lt;span class='s'&gt;@&amp;quot;launchOnLogin&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The rest of the code just needs to check if our app bundle is listed in the login items list in the getter for the property, and add it or remove it in the setter:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='objc'&gt;&lt;span class='c1'&gt;// Get an NSArray with the items.&lt;/span&gt;
&lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;NSArray&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;loginItems&lt;/span&gt;
&lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='n'&gt;CFArrayRef&lt;/span&gt; &lt;span class='n'&gt;snapshotRef&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;LSSharedFileListCopySnapshot&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;loginItemsListRef&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nb'&gt;NULL&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
    
    &lt;span class='c1'&gt;// Use toll-free bridging to get an NSArray with nicer API&lt;/span&gt;
    &lt;span class='c1'&gt;// and memory management.&lt;/span&gt;
    &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;NSMakeCollectable&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;snapshotRef&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='n'&gt;autorelease&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;

&lt;span class='c1'&gt;// Return a CFRetained item for the app&amp;#39;s bundle, if there is one.&lt;/span&gt;
&lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;LSSharedFileListItemRef&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;mainBundleLoginItemCopy&lt;/span&gt;
&lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='n'&gt;NSArray&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;loginItems&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;self&lt;/span&gt; &lt;span class='n'&gt;loginItems&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
    &lt;span class='n'&gt;NSURL&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;bundleURL&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;NSURL&lt;/span&gt; &lt;span class='nl'&gt;fileURLWithPath:&lt;/span&gt;&lt;span class='p'&gt;[[&lt;/span&gt;&lt;span class='n'&gt;NSBundle&lt;/span&gt; &lt;span class='n'&gt;mainBundle&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='n'&gt;bundlePath&lt;/span&gt;&lt;span class='p'&gt;]];&lt;/span&gt;
    
    &lt;span class='k'&gt;for&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kt'&gt;id&lt;/span&gt; &lt;span class='n'&gt;item&lt;/span&gt; &lt;span class='k'&gt;in&lt;/span&gt; &lt;span class='n'&gt;loginItems&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
        &lt;span class='n'&gt;LSSharedFileListItemRef&lt;/span&gt; &lt;span class='n'&gt;itemRef&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;LSSharedFileListItemRef&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;item&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
        &lt;span class='n'&gt;CFURLRef&lt;/span&gt; &lt;span class='n'&gt;itemURLRef&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
        
        &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;LSSharedFileListItemResolve&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;itemRef&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='n'&gt;itemURLRef&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nb'&gt;NULL&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='n'&gt;noErr&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
            &lt;span class='c1'&gt;// Again, use toll-free bridging.&lt;/span&gt;
            &lt;span class='n'&gt;NSURL&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;itemURL&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;NSURL&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='p'&gt;)[&lt;/span&gt;&lt;span class='n'&gt;NSMakeCollectable&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;itemURLRef&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='n'&gt;autorelease&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
            &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='p'&gt;([&lt;/span&gt;&lt;span class='n'&gt;itemURL&lt;/span&gt; &lt;span class='nl'&gt;isEqual:&lt;/span&gt;&lt;span class='n'&gt;bundleURL&lt;/span&gt;&lt;span class='p'&gt;])&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
                &lt;span class='n'&gt;CFRetain&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;item&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
                &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;LSSharedFileListItemRef&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;item&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
            &lt;span class='p'&gt;}&lt;/span&gt;
        &lt;span class='p'&gt;}&lt;/span&gt;
    &lt;span class='p'&gt;}&lt;/span&gt;
    
    &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='nb'&gt;NULL&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;

&lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kt'&gt;void&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;addMainBundleToLoginItems&lt;/span&gt;
&lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='c1'&gt;// We use the URL to the app itself (i.e. the main bundle).&lt;/span&gt;
    &lt;span class='n'&gt;NSURL&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;bundleURL&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;NSURL&lt;/span&gt; &lt;span class='nl'&gt;fileURLWithPath:&lt;/span&gt;&lt;span class='p'&gt;[[&lt;/span&gt;&lt;span class='n'&gt;NSBundle&lt;/span&gt; &lt;span class='n'&gt;mainBundle&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='n'&gt;bundlePath&lt;/span&gt;&lt;span class='p'&gt;]];&lt;/span&gt;
    
    &lt;span class='c1'&gt;// Ask to be hidden on launch. The key name to use was a bit hard to find, but can&lt;/span&gt;
    &lt;span class='c1'&gt;// be found by inspecting the plist ~/Library/Preferences/com.apple.loginwindow.plist&lt;/span&gt;
    &lt;span class='c1'&gt;// and looking at some existing entries. Thanks to Anders for the hint!&lt;/span&gt;
    &lt;span class='n'&gt;NSDictionary&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;properties&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
    &lt;span class='n'&gt;properties&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;NSDictionary&lt;/span&gt; &lt;span class='nl'&gt;dictionaryWithObject:&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;NSNumber&lt;/span&gt; &lt;span class='nl'&gt;numberWithBool:&lt;/span&gt;&lt;span class='n'&gt;YES&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
                                             &lt;span class='nl'&gt;forKey:&lt;/span&gt;&lt;span class='s'&gt;@&amp;quot;com.apple.loginitem.HideOnLaunch&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
    
    &lt;span class='n'&gt;LSSharedFileListItemRef&lt;/span&gt; &lt;span class='n'&gt;itemRef&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
    &lt;span class='n'&gt;itemRef&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;LSSharedFileListInsertItemURL&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;loginItemsListRef&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                                            &lt;span class='n'&gt;kLSSharedFileListItemLast&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                                            &lt;span class='nb'&gt;NULL&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                                            &lt;span class='nb'&gt;NULL&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                                            &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;CFURLRef&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;bundleURL&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                                            &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;CFDictionaryRef&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;properties&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                                            &lt;span class='nb'&gt;NULL&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
    &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;itemRef&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
        &lt;span class='n'&gt;CFRelease&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;itemRef&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
    &lt;span class='p'&gt;}&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;

&lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kt'&gt;void&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;removeMainBundleFromLoginItems&lt;/span&gt;
&lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='c1'&gt;// Try to get the item corresponding to the main bundle URL.&lt;/span&gt;
    &lt;span class='n'&gt;LSSharedFileListItemRef&lt;/span&gt; &lt;span class='n'&gt;itemRef&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;self&lt;/span&gt; &lt;span class='n'&gt;mainBundleLoginItemCopy&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
    &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;!&lt;/span&gt;&lt;span class='n'&gt;itemRef&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
        &lt;span class='k'&gt;return&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
    
    &lt;span class='n'&gt;LSSharedFileListItemRemove&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;loginItemsListRef&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;itemRef&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
    
    &lt;span class='n'&gt;CFRelease&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;itemRef&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;

&lt;span class='cp'&gt;#pragma mark Property accessor methods&lt;/span&gt;
&lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kt'&gt;BOOL&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;launchOnLogin&lt;/span&gt;
&lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;!&lt;/span&gt;&lt;span class='n'&gt;loginItemsListRef&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
        &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='n'&gt;NO&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;

    &lt;span class='n'&gt;LSSharedFileListItemRef&lt;/span&gt; &lt;span class='n'&gt;itemRef&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;self&lt;/span&gt; &lt;span class='n'&gt;mainBundleLoginItemCopy&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;    
    &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;!&lt;/span&gt;&lt;span class='n'&gt;itemRef&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
        &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='n'&gt;NO&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
    
    &lt;span class='n'&gt;CFRelease&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;itemRef&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
    &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='n'&gt;YES&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;

&lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kt'&gt;void&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='nl'&gt;setLaunchOnLogin:&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kt'&gt;BOOL&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;value&lt;/span&gt;
&lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;!&lt;/span&gt;&lt;span class='n'&gt;loginItemsListRef&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
        &lt;span class='k'&gt;return&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
    
    &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;!&lt;/span&gt;&lt;span class='n'&gt;value&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
        &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;self&lt;/span&gt; &lt;span class='n'&gt;removeMainBundleFromLoginItems&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
    &lt;span class='p'&gt;}&lt;/span&gt; &lt;span class='k'&gt;else&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
        &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;self&lt;/span&gt; &lt;span class='n'&gt;addMainBundleToLoginItems&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
    &lt;span class='p'&gt;}&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;That&amp;#8217;s all! Not only does this give you synchronization between your preferences panel and the system settings, but also feels more correct than poking at a different application&amp;#8217;s or subsystem&amp;#8217;s persistent domain.&lt;/p&gt;

&lt;p&gt;Note: There is also a &amp;#8220;seed&amp;#8221; for each list that can be used to check if the list has changed. Using that you can make sure that any change notification is not emitted unless necessary (see &lt;code&gt;LSSharedFileListGetSeedValue()&lt;/code&gt;). The example above doesn&amp;#8217;t do that, so there will be notification sent out when toggling the setting from the app&amp;#8217;s preferences.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Registering defaults for NSUserDefaults using a property list</title>
   <link href="http://rhult.github.com/registering-defaults.html" />
   <updated>2009-04-09T00:00:00-07:00</updated>
   <id>http://rhult.github.com/registering-defaults</id>
   <content type="html">&lt;p&gt;In a previous post about &lt;a href='using-property-lists.html'&gt;using property lists&lt;/a&gt;, I wrote a little about property lists and a use case I had for them. Another one I ran in to recently is related to user defaults:&lt;/p&gt;

&lt;p&gt;NSUserDefaults is the system in Mac OS X that handles user preferences. Applications usually register default values at launch time, so that all preferences have a sane default value in case the user hasn&amp;#8217;t set one for a particular preference. Most examples I&amp;#8217;ve found on the subject do something along the lines of:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='objc'&gt;&lt;span class='c1'&gt;// Register user defaults in the class initializer.&lt;/span&gt;
&lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kt'&gt;void&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;initialize&lt;/span&gt;
&lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='n'&gt;NSDictionary&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;appDefaults&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;NSDictionary&lt;/span&gt; &lt;span class='nl'&gt;dictionaryWithObjectsAndKeys:&lt;/span&gt;
                                 &lt;span class='s'&gt;@&amp;quot;YES&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s'&gt;@&amp;quot;ShowToolBar&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                                 &lt;span class='s'&gt;@&amp;quot;NO&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s'&gt;@&amp;quot;AutoSaveEnabled&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                                 &lt;span class='c1'&gt;// ... lots of objects and keys here,&lt;/span&gt;
                                 &lt;span class='nb'&gt;nil&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;

    &lt;span class='p'&gt;[[&lt;/span&gt;&lt;span class='n'&gt;NSUserDefaults&lt;/span&gt; &lt;span class='n'&gt;standardUserDefaults&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='nl'&gt;registerDefaults:&lt;/span&gt;&lt;span class='n'&gt;appDefaults&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;However, if you have many keys, or just want to make it possible to change them without recompiling, they can be managed through a property list file instead of doing it programmatically. To do that, first create a property list by selecting File → New File&amp;#8230; or pressing ⌘-N in Xcode and selecting Property List in the Other category. Then add the default values using the property list editor.&lt;/p&gt;

&lt;p&gt;Assuming the plist file is named UserDefaults.plist, the code can then be changed to:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='objc'&gt;&lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kt'&gt;void&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;initialize&lt;/span&gt;
&lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='n'&gt;NSString&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;defaultsPath&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[[&lt;/span&gt;&lt;span class='n'&gt;NSBundle&lt;/span&gt; &lt;span class='n'&gt;mainBundle&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='nl'&gt;pathForResource:&lt;/span&gt;&lt;span class='s'&gt;@&amp;quot;UserDefaults&amp;quot;&lt;/span&gt;
                                                             &lt;span class='nl'&gt;ofType:&lt;/span&gt;&lt;span class='s'&gt;@&amp;quot;plist&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
    &lt;span class='n'&gt;NSDictionary&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;appDefaults&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;NSDictionary&lt;/span&gt; &lt;span class='nl'&gt;dictionaryWithContentsOfFile:&lt;/span&gt;&lt;span class='n'&gt;defaultsPath&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;

    &lt;span class='p'&gt;[[&lt;/span&gt;&lt;span class='n'&gt;NSUserDefaults&lt;/span&gt; &lt;span class='n'&gt;standardUserDefaults&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='nl'&gt;registerDefaults:&lt;/span&gt;&lt;span class='n'&gt;appDefaults&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;</content>
 </entry>
 
 <entry>
   <title>Using property lists</title>
   <link href="http://rhult.github.com/using-property-lists.html" />
   <updated>2009-04-04T00:00:00-07:00</updated>
   <id>http://rhult.github.com/using-property-lists</id>
   <content type="html">&lt;p&gt;Before entering the Cocoa world, whenever I needed to store and retrieve some small amount of data in an application, I usually handcrafted an XML format and wrote a matching XML parser using either DOM or SAX. This often meant a lot of uninspiring code duplication. On Mac OS X, there is a standardized format that is used throughout the system called property list or &amp;#8220;plist&amp;#8221;. Not only are there APIs to parse plists into the familiar data structures in Cocoa, but there is also a builtin editor for plists in Xcode.&lt;/p&gt;

&lt;p&gt;This came in handy recently in some code I was working on. I needed to store 20 or 30 pairs of strings and select one pair randomly from time to time. Instead of entering the strings in the code, they were put into a plist file.&lt;/p&gt;

&lt;h2 id='create_and_edit_a_plist'&gt;Create and edit a plist&lt;/h2&gt;

&lt;p&gt;It&amp;#8217;s easy to create a property list. In Xcode, just select File → New File&amp;#8230; or press cmd-N, and select the template Property List in the Other category. Then add the data you wish to, using the property list editor in Xcode. You can of course also handwrite the XML using any text editor.&lt;/p&gt;

&lt;h2 id='use_the_data'&gt;Use the data&lt;/h2&gt;

&lt;p&gt;Assume that the property list is structured with a top-level key called Pairs, whose value is an array of our string pairs. Each pair in turn is also an array, with two strings in each. The code to read the list could then look as follows:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='objc'&gt;&lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kt'&gt;void&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;readStringPairs&lt;/span&gt;
&lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='n'&gt;NSString&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;path&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[[&lt;/span&gt;&lt;span class='n'&gt;NSBundle&lt;/span&gt; &lt;span class='n'&gt;mainBundle&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='nl'&gt;pathForResource:&lt;/span&gt;&lt;span class='s'&gt;@&amp;quot;MyFileName&amp;quot;&lt;/span&gt;
                                                     &lt;span class='nl'&gt;ofType:&lt;/span&gt;&lt;span class='s'&gt;@&amp;quot;plist&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
    &lt;span class='n'&gt;NSDictionary&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;toplevelDict&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;NSDictionary&lt;/span&gt; &lt;span class='nl'&gt;dictionaryWithContentsOfFile:&lt;/span&gt;&lt;span class='n'&gt;path&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;

    &lt;span class='c1'&gt;// Get the array of pairs, retain it as we need it later.&lt;/span&gt;
    &lt;span class='n'&gt;pairs&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[[&lt;/span&gt;&lt;span class='n'&gt;toplevelDict&lt;/span&gt; &lt;span class='nl'&gt;valueForKey:&lt;/span&gt;&lt;span class='s'&gt;@&amp;quot;Pairs&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='n'&gt;retain&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;

&lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kt'&gt;void&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='n'&gt;randomizeStrings&lt;/span&gt;
&lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='c1'&gt;// Get a random pair, represented by an array.&lt;/span&gt;
    &lt;span class='n'&gt;NSArray&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;pair&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;pairs&lt;/span&gt; &lt;span class='nl'&gt;objectAtIndex:&lt;/span&gt;&lt;span class='n'&gt;arc4random&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='o'&gt;%&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;pairs&lt;/span&gt; &lt;span class='n'&gt;count&lt;/span&gt;&lt;span class='p'&gt;]];&lt;/span&gt;

    &lt;span class='c1'&gt;// Get the two strings.&lt;/span&gt;
    &lt;span class='n'&gt;NSString&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;name&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;pair&lt;/span&gt; &lt;span class='nl'&gt;objectAtIndex:&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
    &lt;span class='n'&gt;NSString&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;description&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;pair&lt;/span&gt; &lt;span class='nl'&gt;objectAtIndex:&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;

    &lt;span class='c1'&gt;// Do something with the strings here.&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you need something a little bit more flexible or complex, you can nest dictionaries and arrays in the plist as well. As a matter of fact, my original code doesn&amp;#8217;t only have two strings per pair, but one string and an array of strings.&lt;/p&gt;</content>
 </entry>
 
 
</feed>

