<?xml version="1.0" encoding="UTF-8"?>
<!-- generator="wordpress/2.3.3" -->
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	>

<channel>
	<title>Vacuous Virtuoso &#187; Xcode</title>
	<link>http://lipidity.com</link>
	<description>Despotic Development</description>
	<pubDate>Tue, 23 Dec 2008 06:24:04 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.3.3</generator>
	<language>en</language>
			<item>
		<title>Cleaning up Quicksilver</title>
		<link>http://lipidity.com/apple/cleaning-up-quicksilver/</link>
		<comments>http://lipidity.com/apple/cleaning-up-quicksilver/#comments</comments>
		<pubDate>Fri, 07 Dec 2007 08:38:31 +0000</pubDate>
		<dc:creator>Ankur</dc:creator>
		
		<category><![CDATA[Apple]]></category>

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

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

		<category><![CDATA[Source Code]]></category>

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

		<guid isPermaLink="false">http://lipidity.com/apple/cleaning-up-quicksilver/</guid>
		<description><![CDATA[Given the Quicksilver source code is now out in the open, I&#8217;m trying to learn from it while at the same time cleaning it up. Here&#8217;s what I&#8217;ve done so far.



Bug fixes

A lot of features were buried in the source, available only by setting some preferences to allow them. Mostly it was due to buggy [...]]]></description>
			<content:encoded><![CDATA[<p>Given the <a href="http://code.google.com/p/blacktree-alchemy/source">Quicksilver source code</a> is now out in the open, I&#8217;m trying to learn from it while at the same time cleaning it up. Here&#8217;s what I&#8217;ve done so far.</p>

<!--more-->

<h3>Bug fixes</h3>

<p>A lot of features were buried in the source, available only by setting some preferences to allow them. Mostly it was due to buggy implementation or pending improvements. I managed to resurrect a few of these nifty ones and fix a few old bugs that have annoyed me for ages.</p>

<h4>Color handling</h4>

<p>Color handling was commented out in the Quicksilver source; probably because it resulted in errors. A simple change in the source allowed correct handling of color values, and the code for drawing the icon was already there. I added functionality to allow dropping colors from the Quicksilver object pane into color wells and the like - which is what makes this useful.</p>

<p class="centre"><img src="http://images.lipidity.com/QS-Color-handling-20071207-185619.jpg" alt="Handling color in the object pane is now possible" title="A color stored in the object pane" /></p>

<h4>Uninstalled plugins</h4>

<p>Added a plugin set to the plugins preference pane to list plugins that you haven&#8217;t yet installed or downloaded. Not sure why this was commented out; I didn&#8217;t have to add any new code here.</p>

<p class="centre"><img src="http://images.lipidity.com/QS-Plug-ins-20071207-185845.jpg" alt="Uninstalled Plugins can now be listed" title="Listing uninstalled plugins in the preference pane" /></p>

<h4>Triggers</h4>

<p>There was a very annoying bug in the triggers preference pane that haunted me from the day I started using Quicksilver. Switching to the iTunes trigger set resulted in errors, and often froze or messed up Quicksilver. Finally, it&#8217;s fixed, and we can see and use these triggers.</p>

<p class="centre"><img src="http://images.lipidity.com/QS-Triggers-20071207-190435.jpg" alt="Quicksilver triggers preference pane has been fixed" /></p>

<p>Speaking of plugins; I think I heard mention of a &#8220;plugin check bug&#8221; fixed somewhere, but it wasn&#8217;t working for me so I&#8217;ve also fixed the issue where the plugin list would be blank until you hit refresh.</p>

<h4>Miscellaneous bug fixes</h4>

<p>Other things like crashing when you try to relaunch with the &#8220;Customize&#8221; preference pane open, and with the source, hundreds of warnings when trying to compile, errors in the shell scripts when working in a path with non-alphanumeric characters, etc. have also been fixed.</p>

<p class='centre'><img src="http://images.lipidity.com/QS-Customize-20071207-190804.jpg" alt="Fixed a crash with the Quicksilver customize pane" /></p>

<h3>Tidy up</h3>

<blockquote>
  <p>[I wonder] how long it&#8217;ll take people to notice the Frankensteinian nature of the source and respond  - appropriately - with pitchforks. <cite><a href="http://twitter.com/alcor/statuses/388774142" rel="nofollow">Alcor</a></cite></p>
</blockquote>

<p>I&#8217;ll have to agree with Alcor - to say the source is a mess would be an understatement. There are hundreds of compiler warnings, unused or unimplemented features, classes, objects, code, structures, nibs, etc. Hundreds. I&#8217;ve tried to remove as much of the redundant or test code as possible and I&#8217;ve yet to reach the end. Mind you, I&#8217;ve been doing this for hours a day for the past week. The formatting and indentation is almost as bad. I&#8217;m cleaning that up too, with some good old regular expressions and elbow grease. As I go, I&#8217;m also trying to improve the logic where I see potential for improvement. Already the application is feeling more responsive but I want to also reduce the memory footprint.</p>

<p>Why go to all this trouble? Mainly, to</p>

<ul>
<li>Make the source more readable; make it easier for people to understand what&#8217;s going on.</li>
<li>Improve the performance of the application in both speed and memory usage.</li>
<li>Fix existing bugs and add new features.</li>
</ul>

<hr />

<p>In the meantime, I&#8217;ll continue my clean up of the Quicksilver source and when it&#8217;s lookin&#8217; fine perhaps post it up (the QS license doesn&#8217;t really say much about the source, so I might have to ask Alcor first. Perhaps he&#8217;d like to add it to the subversion repository after reviewing?).</p>

<p>Lastly, let me just say, I&#8217;ve learnt a lot from looking at the source code to Quicksilver. There are some very neat tricks in there, and great little snippets that could be very useful elsewhere. So thank you Mr. Jitkoff; thank you for writing the application, and thank you for the releasing the source code. It is highly appreciated.</p>
]]></content:encoded>
			<wfw:commentRss>http://lipidity.com/apple/cleaning-up-quicksilver/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Modifier flags during app launch</title>
		<link>http://lipidity.com/apple/modifier-flags-launch/</link>
		<comments>http://lipidity.com/apple/modifier-flags-launch/#comments</comments>
		<pubDate>Tue, 27 Nov 2007 10:45:57 +0000</pubDate>
		<dc:creator>Ankur</dc:creator>
		
		<category><![CDATA[Apple]]></category>

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

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

		<category><![CDATA[Source Code]]></category>

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

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

		<guid isPermaLink="false">http://lipidity.com/apple/modifier-flags-launch/</guid>
		<description><![CDATA[Unfortunately, [[NSApp currentEvent] modiferFlags] can&#8217;t be used to determine if any modifier keys were held down during launch. To accomplish this, it seems one must delve into the world of Carbon.



The GetCurrentEventKeyModifiers() call works on Mac OS X v10.2 and later through which one can check for shiftKey, cmdKey, optionKey and controlKey.

I use the something [...]]]></description>
			<content:encoded><![CDATA[<p>Unfortunately, <code>[[NSApp currentEvent] modiferFlags]</code> can&#8217;t be used to determine if any modifier keys were held down during launch. To accomplish this, it seems one must delve into the world of Carbon.</p>

<!--more-->

<p>The <code>GetCurrentEventKeyModifiers()</code> call works on Mac OS X v10.2 and later through which one can check for <code>shiftKey</code>, <code>cmdKey</code>, <code>optionKey</code> and <code>controlKey</code>.</p>

<p>I use the something like the following to check if the shift key is pressed during launch for <a href="http://lipidity.com/software/zippit/">my compression app</a>.</p>

<pre><code>#import &lt;Cocoa/Cocoa.h&gt;
#import &lt;Carbon/Carbon.h&gt;

int main(int argc, char *argv[]) {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    if((GetCurrentEventKeyModifiers() &amp; shiftKey)!=0) {
        // Change something useful
        NSLog(@"Shift key pressed during launch");
    }
    [[NSApplication sharedApplication] run];
    [pool release];
    return 0;
}
</code></pre>

<p>In the highly unlikely event that you&#8217;ve just got a call to <code>NSApplicationMain</code> in your <code>main</code> function (joking), you can just test <code>(GetCurrentEventKeyModifiers() &amp; shiftKey)!=0</code> in the <code>-applicationWillFinishLaunching:</code> method in your NSApplication delegate.</p>

<p>Any new APIs in Leopard to achieve this or any easier way?</p>
]]></content:encoded>
			<wfw:commentRss>http://lipidity.com/apple/modifier-flags-launch/feed/</wfw:commentRss>
		</item>
		<item>
		<title>CTGradient code bloat</title>
		<link>http://lipidity.com/apple/ctgradient-code-bloat/</link>
		<comments>http://lipidity.com/apple/ctgradient-code-bloat/#comments</comments>
		<pubDate>Tue, 20 Nov 2007 21:05:12 +0000</pubDate>
		<dc:creator>Ankur</dc:creator>
		
		<category><![CDATA[Apple]]></category>

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

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

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

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

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

		<category><![CDATA[Source Code]]></category>

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

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

		<guid isPermaLink="false">http://lipidity.com/apple/cgradient-code-bloat/</guid>
		<description><![CDATA[While getting rid of extraneous junk in an application package is easy using Trimmit, the only way to prevent &#34;code bloat&#34; (and accompanying excess RAM and CPU usage) is through good programming practices. Where most developers fall short is in poor optimization of borrowed code. Let&#8217;s take CTGradient as an example as it&#8217;s well known [...]]]></description>
			<content:encoded><![CDATA[<p>While getting rid of extraneous junk in an application package is easy using <a href="http://lipidity.com/software/trimmit/">Trimmit</a>, the only way to prevent &quot;code bloat&quot; (and accompanying excess RAM and CPU usage) is through good programming practices. Where most developers fall short is in poor optimization of borrowed code. Let&#8217;s take CTGradient as an example as it&#8217;s well known and used (or more accurately, abused) in dozens of applications.</p>

<!--more-->

<p>CTGradient contains an incredible diversity of built-in gradients, gradient styles, and methods for creating fancy rainbows, radial gradients, linear gradients, aqua gradients, and a number of other interesting class and instance methods. It allows you to dynamically alter gradients by adding or removing colors, changing the level of transparency, filling NSRects or NSBezierPaths, rotating the gradient, etc. For demonstration purposes, all these features are excellent. For production, this is a nightmare.</p>

<p><strong><a href="http://pub.lipidity.com/CTGradient/CTGradient.m">CTGradient.m</a> weighs in at over 1300 lines of code.</strong></p>

<p>Ignoring Mr. Weider&#8217;s unique style of formatting, take a look through the code. If you&#8217;re putting this class in an application, you can immediately remove over a thousand lines. Just like that. But if you take some time to understand what&#8217;s going on, you can optimize this thing till it runs like a Ferrari.</p>

<p>You&#8217;ll notice <a href="http://lipidity.com/software/trimmit/">Trimmit</a> uses a gradient background for it&#8217;s window. Let&#8217;s cut down CTGradient until it matches the level of optimization of Trimmit&#8217;s gradient code.</p>

<h2>Clear the junk</h2>

<p>Firstly, let&#8217;s remove the methods we know for sure we won&#8217;t need. Remove the following methods completely from both the interface and the implementation (scroll down for more):</p>

<pre style='height:340px;overflow:scroll'><code>// We don't need the preset gradients

+ (id)dividerGradient;
+ (id)statusBarGradient;
+ (id)aquaSelectedGradient;
+ (id)aquaNormalGradient;
+ (id)aquaPressedGradient;
+ (id)unifiedSelectedGradient;
+ (id)unifiedNormalGradient;
+ (id)unifiedPressedGradient;
+ (id)unifiedDarkGradient;
+ (id)sourceListSelectedGradient;
+ (id)sourceListUnselectedGradient;
+ (id)rainbowGradient;
+ (id)hydrogenSpectrumGradient;

// We won't make any drastic modifications to the gradient once created

- (CTGradient *)gradientWithAlphaComponent:(float)alpha;
- (CTGradient *)addColorStop:(NSColor *)color atPosition:(float)position;
- (CTGradient *)removeColorStopAtIndex:(unsigned)index;
- (CTGradient *)removeColorStopAtPosition:(float)position;
- (CTGradientBlendingMode)blendingMode;
- (NSColor *)colorStopAtIndex:(unsigned)index;
- (NSColor *)colorAtPosition:(float)position;

// Now we don't need to conform to the NSCopying and NSCoding protocols

- (id)copyWithZone:(NSZone *)zone;
- (void)encodeWithCoder:(NSCoder *)coder;
- (id)initWithCoder:(NSCoder *)coder;

// We only need to fill a simple NSRect

- (void)drawSwatchInRect:(NSRect)rect;
- (void)radialFillRect:(NSRect)rect;
- (void)fillBezierPath:(NSBezierPath *)path angle:(float)angle;
- (void)radialFillBezierPath:(NSBezierPath *)path;

// Remove the entire (Private) category;
// but leave -addElement:

- (void)_commonInit;
// move setBlendingMode code into init
- (void)setBlendingMode:(CTGradientBlendingMode)mode;
- (CTGradientElement *)elementAtIndex:(unsigned)index;
- (CTGradientElement)removeElementAtIndex:(unsigned)index;
- (CTGradientElement)removeElementAtPosition:(float)position;
</code></pre>

<p>The following C functions are now unused:</p>

<pre><code>static void chromaticEvaluation(void *info, const float *in, float *out);
static void inverseChromaticEvaluation(void *info, const float *in, float *out);
static void transformRGB_HSV(float *components);
static void transformHSV_RGB(float *components);
static void resolveHSV(float *color1, float *color2);
</code></pre>

<p>And also remove the following from the header:</p>

<pre><code>typedef enum  _CTBlendingMode
    {
    CTLinearBlendingMode,
    CTChromaticBlendingMode,
    CTInverseChromaticBlendingMode
    } CTGradientBlendingMode;
</code></pre>

<p>Remove the <code>&lt;protocols&gt;</code> and unnecessary instance variables so the interface looks like this:</p>

<pre><code>@interface CTGradient : NSObject {
    CTGradientElement* elementList;
    CGFunctionRef gradientFunction;
}
+ (id)gradientWithBeginningColor:(NSColor *)begin endingColor:(NSColor *)end;
- (void)fillRect:(NSRect)rect angle:(float)angle;
- (void)addElement:(CTGradientElement *)newElement;
@end
</code></pre>

<p>Before we go further, we&#8217;ll need to take a detour and fix the remaining code so that it compiles. That&#8217;s easy enough - just remove any references to code we&#8217;ve removed.</p>

<p>If you&#8217;ve been following along, your CTGradient should now look like <a href="http://pub.lipidity.com/CTGradient/CTGradient2.m">this</a> (also made it readable!).</p>

<p>In a few short minutes, we&#8217;re down from more than <strong>1300</strong> to a little over <strong>200 lines</strong>.</p>

<p><big>It gets better.</big></p>

<h2>Optimize it</h2>

<p>First stop, <code>fillRect:angle:</code>. Since the gradient for Trimmit&#8217;s window only runs vertically (angle 90), we can straight away take out the <var>angle</var> argument, and also cut the entire if / else structure with the angle down to the two lines that are under the <code>if(angle == 90)</code>. Ah, much better.</p>

<p>Now things start getting a teeny bit more complex - and fun.</p>

<hr />

<p>The CTGradient code is written so that one can have many different color stops. The CTGradientElement struct has a nextElement which points to the next CTGradientElement and the position of the current element is stored under the float <var>position</var>. However, we need just two - the starting and ending shades.</p>

<p>Let&#8217;s take a look at cutting the multiple elements down to just two.</p>

<p>The <code>addElement:</code> method has a lot of looping to insert the element at the correct place. However, since we know that we only ever need two, we can cut it down to this:</p>

<pre><code>- (void)addElement:(CTGradientElement *)newElement {
    if(elementList) {
        // elementList exists, add second element
        elementList-&gt;nextElement = malloc(sizeof(CTGradientElement));
        *(elementList-&gt;nextElement) = *newElement;
        elementList-&gt;nextElement-&gt;nextElement = 0;
    } else {
        // no elements - add first element
        elementList = malloc(sizeof(CTGradientElement));
        *elementList = *newElement;
        elementList-&gt;nextElement = 0;
    }
}
</code></pre>

<p>Similarly with <code>dealloc</code>,</p>

<pre><code>- (void)dealloc {
    CGFunctionRelease(gradientFunction);
    free(elementList-&gt;nextElement);
    free(elementList);
    [super dealloc];
}
</code></pre>

<p>While we&#8217;re at it, let&#8217;s clean up <code>init</code> as well:</p>

<pre><code>- (id)init {
    if(self = [super init]) {
        CGFunctionCallbacks evaluationCallbackInfo = {0 , &amp;linearEvaluation, 0};
        static const float input_value_range[2] = { 0, 1 };
        static const float output_value_ranges[8] = { 0, 1, 0, 1, 0, 1, 0, 1 };
        gradientFunction = CGFunctionCreate(&amp;elementList, 1, input_value_range, 4, output_value_ranges, &amp;evaluationCallbackInfo);
    }
    return self;
}
</code></pre>

<p>Now we head to the <code>linearEvaluation</code> function. <var>info</var> passed in refers to <var>elementList</var> (see the CGFunctionCreate call). Again, since we know that we&#8217;ll only ever have two elements we can reduce it to:</p>

<pre><code>void linearEvaluation (void *info, const float *in, float *out) {
    float position = *in;

    CTGradientElement *color1 = *(CTGradientElement **)info;
    CTGradientElement *color2 = color1-&gt;nextElement;

    out[0] = (color2-&gt;red - color1-&gt;red)*position + color1-&gt;red; 
    out[1] = (color2-&gt;green - color1-&gt;green)*position + color1-&gt;green;
    out[2] = (color2-&gt;blue - color1-&gt;blue)*position + color1-&gt;blue;
    out[3] = (color2-&gt;alpha - color1-&gt;alpha)*position + color1-&gt;alpha;
}
</code></pre>

<p>And now we can finally remove the lines from <code>+gradientWithBeginningColor:endingColor:</code> where the positions are set, and also remove the <var>position</var> float from the CTGradientElement struct.</p>

<hr />

<p>We&#8217;re now down to a little over <strong>100 lines</strong>. Your CTGradientElement.m should now be looking something like <a href="http://pub.lipidity.com/CTGradient/CTGradient3.m">this</a>. Going well, but let&#8217;s take things up a notch.</p>

<hr />

<p>For Trimmit&#8217;s background, we&#8217;re not interested in the red, green, blue and alpha components - we just need a grayscale shading. This is the most fun part.</p>

<p>Instead of <code>float red, green, blue, alpha;</code> in the CTGradientElement struct, we can have just <code>float shade;</code>.</p>

<p>Now, during <code>init</code> we have:</p>

<pre><code>CGFunctionCallbacks evaluationCallbackInfo = {0 , &amp;linearEvaluation, 0};
static const float input_value_range[2] = { 0, 1 };
static const float output_value_ranges[8] = { 0, 1, 0, 1, 0, 1, 0, 1 };
gradientFunction = CGFunctionCreate(&amp;elementList, 1, input_value_range, 4, output_value_ranges, &amp;evaluationCallbackInfo);
</code></pre>

<p>The third argument, <var>input_value_range</var> is the domain. This gets passed into linearEvalution through <var>*in</var>.This is the independent variable. When we&#8217;re drawing the gradient, linearEvaluation is called with <var>*in</var> (the domain) starting at the first value of <var>input_value_range</var> and ending up at the second. So, something like 0.000, 0.001, 0.002 &#8230; 0.998, 0.999, 1.000. The function&#8217;s job is to set the the color components for each given value of the domain.</p>

<p>The first optimization we can make here is to return only one channel instead of four. This changes it to:</p>

<pre><code>- (id)init {
    if (self = [super init]) {
        CGFunctionCallbacks evaluationCallbackInfo = {0, &amp;linearEvaluation, 0};
        static const float range[2] = {0, 1};
        static const float domain[2] = {0, 1};
        gradientFunction = CGFunctionCreate(&amp;elementList, 1, domain, 1, range, &amp;evaluationCallbackInfo);
    }
    return self;
}
</code></pre>

<p>This change impacts back on the linearEvaluation function - now it only needs to return one channel:</p>

<pre><code>void linearEvaluation (void *info, const float *in, float *out) {
    CTGradientElement *color1 = *(CTGradientElement **)info;
    out[0] = (color1-&gt;nextElement-&gt;shade - color1-&gt;shade)*(*in) + color1-&gt;shade;
}
</code></pre>

<p>And don&#8217;t forget <code>+gradientWithBeginningColor:endingColor:</code>. We improve performance here, as we only need the shade - not a color. We can rename it to something appropriate.</p>

<pre><code>+ (id)gradientWithBeginningShade:(float)begin endingShade:(float)end {
    id newInstance = [[[self class] alloc] init];
    CTGradientElement color1, color2;

    color1.shade = begin; color2.shade = end;

    [newInstance addElement:&amp;color1];
    [newInstance addElement:&amp;color2];
    return [newInstance autorelease];
}
</code></pre>

<p>Now we&#8217;re only returning one grayscale channel. But hold on, we&#8217;re still in the RGB colorspace! That&#8217;s easily fixed. In <code>fillRect:</code>, replace:</p>

<pre><code>#if MAC_OS_X_VERSION_MAX_ALLOWED &gt;= MAC_OS_X_VERSION_10_4
    CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
#else
    CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
#endif
</code></pre>

<p>with:</p>

<pre><code>CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceGray();
</code></pre>

<p class="note">CGColorSpaceCreateDeviceGray is device-dependent on Mac OS X 10.3 and below, but starting from Tiger, it&#8217;s now device-independent which is good in terms of appearance.</p>

<p>While we&#8217;re in <code>fillRect:</code>, we can also make a few more improvements:</p>

<pre><code>- (void)fillRect:(NSRect)rect {
    CGContextRef currentContext = [[NSGraphicsContext currentContext] graphicsPort];
    CGContextSaveGState(currentContext);
    CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceGray();
    CGContextClipToRect(currentContext, *(CGRect *)&amp;rect);
    CGShadingRef myCGShading = CGShadingCreateAxial(colorspace, CGPointMake(0, 0), CGPointMake(0, NSMaxY(rect)), gradientFunction, 0, 0);
    CGContextDrawShading(currentContext, myCGShading);

    CGShadingRelease(myCGShading);
    CGColorSpaceRelease(colorspace);
    CGContextRestoreGState(currentContext);
}
</code></pre>

<p>I moved the startPoint and endPoint CGPoints straight into the CGShadingCreateAxial call. Also, we only ever fill the whole view with the gradient, so just put in <code>0</code> instead of using NSMinX and NSMinY. We could replace the <code>CGPointMake(0, 0)</code> with <code>(CGPoint){0,0}</code>, but I&#8217;m not sure if that&#8217;d be noticeably faster.</p>

<hr />

<p>Our code is at a lean <strong>80 lines</strong>. Now for the biggest change yet.</p>

<hr />

<p>Heading back to dear old linearEvaluation. Look at this line:</p>

<pre><code>out[0] = (color1-&gt;nextElement-&gt;shade - color1-&gt;shade)*(*in) + color1-&gt;shade;
</code></pre>

<p>What&#8217;s happening here? Think about it like this:</p>

<p>Essentially, we&#8217;re just &quot;graphing&quot; a straight line:</p>

<pre><code>y = mx + c
</code></pre>

<p>where <var>x</var> is the position, <var>*in</var>, and <var>y</var> is the shade we return (<var>out[0]</var>), and <var>c</var> is the initial shade.</p>

<p>Since our <var>x</var> value only goes from 0 to 1 (see domain in CGFunctionCreate), at <code>x = 0</code>, we will have <code>y = c</code>. At <code>x = 1</code>, we&#8217;ll have <code>y = m + c</code>.</p>

<p><img src='http://lipidity.com/wordpress/wp-content/uploads/2007/11/gradient-graph.png' alt='Graph of Gradient Function' class='feature' /></p>

<p><strong>So</strong> we only need the start shade as our <var>*info</var> value, since we can have <var>m</var> as the difference between the final and initial shades. Let&#8217;s effect this in our code.</p>

<p>We can get rid of the CTGradientElement struct. Remove from the header:</p>

<pre><code>typedef struct _CTGradientElement {
    float shade;
    struct _CTGradientElement *nextElement;
} CTGradientElement;
</code></pre>

<p>Instead of the struct, we&#8217;ll use a single <var>shade</var> instance variable. We don&#8217;t need the <code>+gradientWithBeginningShade:endingShade:</code> anymore, nor do we need <code>-addElement:</code>. So our interface now looks like this:</p>

<pre><code>@interface CTGradient : NSObject {
    float shade;
    CGFunctionRef gradientFunction;
}
- (void)fillRect:(NSRect)rect;
@end
</code></pre>

<p>From the implementation, we can remove the <code>free</code> calls in <code>dealloc</code> as well as the whole of <code>+gradientWithBeginningShade:endingShade:</code> and <code>addElement:</code>. We can set the <code>shade</code> in the init method (and replace <code>&amp;elementList</code> with <code>&amp;shade</code>), and change the linearEvaluation function to:</p>

<pre><code>void linearEvaluation (void *info, const float *in, float *out) {
    out[0] = *(float*)info + (*in)*0.1;
}
</code></pre>

<p>The <code>0.1</code> is what determines how much brighter the top of the gradient is (it&#8217;s the <var>m</var> value of our straight line) compared to the bottom.</p>

<p><strong>But</strong> since the <code>(*in)*0.1</code> will go from <code>0</code> to <code>0.1</code> (as <var>*in</var> goes from 0 to 1), we can actually change the <code>domain</code> we declare for CGFunctionCreate to {0, 0.1} and have our function as:</p>

<pre><code>void linearEvaluation (void *info, const float *in, float *out) {
    out[0] = *(float*)info + *in;
}
</code></pre>

<p>You can now add accessors for <code>shade</code> if you need to change the shade of the gradient or you could even have an <code>initWithShade:</code> method. In fact, here&#8217;s an Xcode project with the gradient.</p>

<p class="download"><a href="http://pub.lipidity.com/CTGradient/GradientSample.tar.bz2">Lean Gradient Xcode Project</a></p>

<p>We now have our gradient code down <big>from <strong>1300 lines</strong> to just over <strong>30 lines</strong></big>.</p>

<hr />

<p>We&#8217;ve almost reached a similar level of optimization to Trimmit&#8217;s gradient. There will be further optimizations you can make, but it depends on what you&#8217;re using the gradient for. For example, if the gradient you&#8217;re drawing is always the same, you can optimize further by removing the <code>shade</code> instance variable and all references to it, and hardcode the value into the linearEvaluation function.</p>

<p>Getting to the end of this article, you&#8217;re probably thinking that it would be a bit of a waste to use CTGradient if by the time you optimize it for your application, it doesn&#8217;t resemble the original at all. And you&#8217;re quite right. The documentation already shows you <a href="http://developer.apple.com/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_shadings/chapter_9_section_6.html">how to draw gradients</a>, yet the number of applications using CTGradient - the whole 1300 lines of it - is astonishing.</p>

<p>Please: When you use other people&#8217;s code, don&#8217;t put it in without a thought. Go through it, understand it, and optimize it for your specific need. For the better performance and reduced RAM usage, computers will thank you.</p>
]]></content:encoded>
			<wfw:commentRss>http://lipidity.com/apple/ctgradient-code-bloat/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Beyond Repair</title>
		<link>http://lipidity.com/apple/beyond-repair/</link>
		<comments>http://lipidity.com/apple/beyond-repair/#comments</comments>
		<pubDate>Thu, 15 Nov 2007 09:16:33 +0000</pubDate>
		<dc:creator>Ankur</dc:creator>
		
		<category><![CDATA[Apple]]></category>

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

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

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

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

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

		<guid isPermaLink="false">http://lipidity.com/apple/beyond-repair/</guid>
		<description><![CDATA[Where can you find an application that comprises a single shell command wrapped in so many layers that it ends up 10,000 times bigger, slower, more obtrusive, less intuitive and full of junk, but still performs the same function as the original command?

Here&#8217;s your answer.

AppleScript can be handy sometimes, but when people release applications like [...]]]></description>
			<content:encoded><![CDATA[<p>Where can you find an application that comprises a single shell command wrapped in so many layers that it ends up 10,000 times bigger, slower, more obtrusive, less intuitive and full of junk, but still performs the same function as the original command?</p>

<p><a href="http://www.nwwnetwork.net/software.php?app=wallsaver">Here&#8217;s your answer</a>.</p>

<p>AppleScript can be handy sometimes, but when people release applications like this, and get awards for it, alarm bells should be going off. WallSaver is almost two megabytes in size. For a single command-line.</p>

<p>Just reinforce the point, I wrote another wrapper for the same command in Objective-C. It&#8217;s 100 kb, or 60 kb without the icon. It can pause the screensaver (which brings it down to 0 CPU usage), resume it or restart it. And not once will it throw an &#8220;AppleScript error&#8221; <img src='http://lipidity.com/wordpress/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> . <a href="http://lipidity.com/software/desksaver/">Download it</a> and see for yourself.</p>

<p>As for the source code, all you need is:</p>

<pre><code>/System/Library/Frameworks/ScreenSaver.framework/Resources/ScreenSaverEngine.app/Contents/MacOS/ScreenSaverEngine -background
</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://lipidity.com/apple/beyond-repair/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Eliminate Bloatware</title>
		<link>http://lipidity.com/apple/eliminate-bloatware/</link>
		<comments>http://lipidity.com/apple/eliminate-bloatware/#comments</comments>
		<pubDate>Fri, 02 Nov 2007 02:00:45 +0000</pubDate>
		<dc:creator>Ankur</dc:creator>
		
		<category><![CDATA[Apple]]></category>

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

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

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

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

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

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

		<guid isPermaLink="false">http://lipidity.com/apple/eliminate-bloatware/</guid>
		<description><![CDATA[The Vikings left their trash on the ground - Mac developers are stuffing theirs into their apps.]]></description>
			<content:encoded><![CDATA[<p>We&#8217;ve heard the excuses and listened to the complaints; the amount of junk shipped in third-party applications just doesn&#8217;t seem to change. If anything, it&#8217;s getting worse. Just because users may not realise how much bloat they&#8217;re getting along with the app, doesn&#8217;t change the fact that it&#8217;s bloat. Unnecessary junk.</p>

<p>Finder info, .DS_Store files, resource forks, debug symbols&#8230; They do nothing for the user short of wasting disk space. For the developer, extra bandwidth costs. Do people not realise this is a problem, or are they just too lazy to act? Either way, the solution is simple.</p>

<p>You can clean apps (sometimes to less than half their original size) with <a href="http://lipidity.com/apple/cleaning-apps-mac-os-x/">some basic commands</a> in the Terminal. But this takes time, and what&#8217;s more, Mac users in general have an aversion to the command line. Enter <a href="http://lipidity.com/trimmit/">Trimmit</a>.</p>

<p class='centre'><a href='http://lipidity.com/trimmit/' title='Trimmit'><img src='http://lipidity.com/wordpress/wp-content/uploads/2007/11/flow2.jpg' alt='[Trimmit]' title="Trimming Flow.app" /></a></p>

<blockquote><p>Trimmit is a svelte utility which takes the pain out of keeping your applications as tight as possible. I use it on Flow each time I seed a build, and I seriously doubt the process could any easier.</p> <cite><a href="http://extendmac.com/" title="ExtendMac Flow">Brian Amerige</a></cite></blockquote>

<p>Trimmit uses superfast UNIX APIs, Apple&#8217;s own developer tools, and the rock-solid Cocoa framework to automate your cleaning process. Just drop your built app onto it, maybe configure a few settings, and you&#8217;re away. <big>It doesn&#8217;t get any easier - or faster.</big></p>

<p>Other applications claim to save disk space by stripping universal binaries, or removing languages. <big>Trimmit does all of that - and more.</big> But it works. Look at this screenshot from Xslimmer regarding Xcode:</p>

<p class='centre'><img src='http://lipidity.com/wordpress/wp-content/uploads/2007/11/xcode-xslim.jpg' alt='Xslimmer on Xcode' title='Shows only 2 architectures' /></p>

<p>Xslimmer naively believes that only two architectures can exist in any application, but the truth is revealed by Trimmit:</p>

<p class='centre'><img src='http://lipidity.com/wordpress/wp-content/uploads/2007/11/xcodetrim-2.png' alt='i386, ppc, ppc64, x86_64' title='Architectures in Xcode' /></p>

<p><br />Because Trimmit interacts with the shell (<a href="http://zsh.sourceforge.net/FAQ/zshfaq01.html#l3">zsh</a>, in fact), no similar application can even come close to it&#8217;s level of power, accuracy and performance.</p>

<p><big><a href="http://rixstep.com/1/20071101,01.shtml">The results</a> speak for themselves.</big></p>

<p>Use Trimmit to recover disk space that third-party applications so rudely waste.</p>

<p class='centre' style='margin:2em 0'><big><a href="http://lipidity.com/trimmit/">Go get Trimmit.</a></big></p>
]]></content:encoded>
			<wfw:commentRss>http://lipidity.com/apple/eliminate-bloatware/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Mac developer? Clean up your app</title>
		<link>http://lipidity.com/apple/cleaning-apps-mac-os-x/</link>
		<comments>http://lipidity.com/apple/cleaning-apps-mac-os-x/#comments</comments>
		<pubDate>Sat, 15 Sep 2007 14:00:24 +0000</pubDate>
		<dc:creator>Ankur</dc:creator>
		
		<category><![CDATA[Apple]]></category>

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

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

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

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

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

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

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

		<guid isPermaLink="false">http://dev.lipidity.com/tutorial/cleaning-apps-mac-os-x</guid>
		<description><![CDATA[
  The reason for this article should be obvious: too many OS X third party developers do an absolutely terrible job of building and packaging their applications. rixstep


Too many apps are shipped with debug symbols, uncompressed images, redundant files or generally useless rubbish that not only wastes users&#8217; disk space, it ultimately ends up [...]]]></description>
			<content:encoded><![CDATA[<blockquote>
  <p>The reason for this article should be obvious: too many OS X third party developers do an absolutely terrible job of building and packaging their applications. <cite><a href="http://rixstep.com/2/2/20070526,00.shtml">rixstep</a></cite></p>
</blockquote>

<p>Too many apps are shipped with debug symbols, uncompressed images, redundant files or generally useless rubbish that not only wastes users&#8217; disk space, it ultimately ends up increasing the developer&#8217;s own bandwidth costs.</p>

<!--more-->

<p>It&#8217;s not just the script kiddies at fault. <a href="http://www.cocoatech.com">Path Finder</a> weighs in at over 60 MB on disk, but after a few simple operations, is halved in size to just over 30 MB. <a href="http://appzapper.com">AppZapper</a> can be trimmed from three megabytes to just one. <a href="http://www.xslimmer.com">Xslimmer</a> (ironically) is over 80% junk, going from 5 MB on disk to a meagre 1 MB after being cleaned. In this case, the <em>disk image</em> is <em>three times as big</em> as the cleaned, <em>uncompressed</em> app. Starting to see the trend? Almost every app can be cleaned up to save a good deal of space.</p>

<h2>Deploying your app</h2>

<p>Rixstep provide instructions on <a href="http://rixstep.com/2/20070306,00.shtml">deploying OS X apps</a> covering most of the major areas.</p>

<p>Essentially, you need to:</p>

<dl>
<dt>Remove Junk</dt>
<dd>Get rid of all .DS_Store files. In Cocoa apps, remove PkgInfo.
You should only have objects.nib or keyedobjects.nib in your nibs: remove classes.nib, info.nib, and data.dependency; these are not relevant for the user. Also remove all header files from your app and from embedded frameworks.</dd>

<dt>Clean up localization</dt>
<dd>Keep all non-language-specific resources in the Resources folder rather than duplicating them in each .lproj folder.
Look through the framework you&#8217;re embedding, and remove any unnecessary files files. That includes language projects which your main app doesn&#8217;t support.</dd>

<dt>Compress images</dt>
<dd>Make sure all your images are compressed, especially if they&#8217;re in TIFF format. (But you shouldn&#8217;t use images for simple graphics like curves or gradients anyway; there are Cocoa drawing routines for that and NSBezierPath is your friend)</dd>

<dt>Trim binaries</dt>
<dd>Do not release an app that has been built using the Xcode &#8220;Debug&#8221; configuration. Remove the debug symbols from your build product, and if possible, release architecture-specific versions of your app, as well as a universal binary.</dd>
</dl>

<p>Get all the details in <a href="http://rixstep.com/2/2/20070526,00.shtml">Building and Packaging Native OS X Applications</a>.</p>

<h2>Automation</h2>

<blockquote>
  <p>A relevant question is: are there any tools that automate this process? And the answer has to be &#8216;not up to now there aren&#8217;t&#8217;. Until something comes along to save users from these issues it&#8217;s still all up to that developer you trust. <cite><a href="http://rixstep.com/2/20070306,00.shtml">Rixstep</a></cite></p>
</blockquote>

<p>There is finally a tool that automates at least some of the cleaning process. Say hello:</p>

<p class="centre"><big><a href='http://lipidity.com/downloads/trim-app/' title='download trim-app shell script'>Go get trim-app</a></big></p>

<p>Trim-app is a shell script. It:</p>

<ul>
<li>Cleans out all the horrible .DS_Store files</li>
<li>Removes unnecessary info.nib, classes.nib, and data.dependency files from inside nibs</li>
<li>Strips &#8220;fat&#8221; universal binaries to contain code for a chosen architecture only</li>
<li>Eliminates debug symbols that may be left in your application</li>
</ul>

<p>You can perform all procedures at once, one at a time, multiple at a time, etc. using different parameters or flags (run <code>trim-app -h</code> for help).</p>

<p>The script searches the current working directory including every subdirectory so no files are missed. Even embedded frameworks are scanned through and cleaned out. So make sure you&#8217;re in the right directory before running (use the <code>pwd</code> command if unsure)</p>

<p>All changes performed are <strong>permanent</strong> and non-recoverable. (If you&#8217;re writing the app, that shouldn&#8217;t be a problem. Just build it again if something goes wrong)</p>

<p class="note">While it&#8217;s written for cleaning out applications, this script is also great for general tasks - cleaning out .DS_Store files in a certain folder, for example.</p>

<h3>Installation</h3>

<ul>
<li>Unzip the <a href="http://lipidity.com/downloads/trim-app/">download</a>.</li>
<li>Open Terminal and navigate to the folder where the unzipped trim-app is.</li>
<li>Run <code>chmod 0700 trim-app</code>. This makes sure only you can execute it.</li>
</ul>

<p>If you want easy access to the command, move the file to /usr/bin/ by running <code>sudo mv trim-app /usr/bin/</code>. You&#8217;ll be asked for the admin password, then file will be moved.</p>

<p>You can now test if it&#8217;s working by running <code>trim-app -h</code>. Instructions for trim-app will appear. Once you&#8217;re done reading them, press <code>q</code> to exit, and start using it!</p>

<h3>Usage</h3>

<p>Running the shell script without any parameters will attempt to run all four procedures. You&#8217;ll be prompted for an architecture to keep in universal binaries. Leave this blank (just hit return) to leave universal binaries universal, or type in an architecture to keep (usually <code>ppc</code> or <code>i386</code>) if you want them stripped down.</p>

<p>Here&#8217;s an example of how you would use it in the Terminal:</p>

<pre><code>% <kbd>cd myApp.app/</kbd>
% <kbd>trim-app</kbd>
<samp>Architecture to keep? (ppc/i386)</samp> <kbd>ppc</kbd>
<samp>   Removing .DS_Store files&#8230;</samp>
<samp>   Trimming nibs&#8230;</samp>
<samp>   Trimming universal binaries&#8230;</samp>
<samp>   Stripping debug symbols&#8230;</samp>
<samp>   Cleaning up&#8230;</samp>
<samp>Done.</samp>
%</code></pre>

<p>If you don&#8217;t want to be prompted each time for the architecture, use either <code>--all</code> (as in, <code>trim-app --all</code>) (which will run everything <em>except</em> the liposuction of universal binaries), or add the architecture to keep immediately after, like <code>trim-app --all ppc</code>.</p>

<p>Add a <code>-p</code> flag to the end if you want to see a list of affected files. For example,</p>

<pre><code>% trim-app --all i386 -p
</code></pre>

<p>You can also run individual procedures. The following cleans out .DS_Stores from the entire Macintosh HD:</p>

<pre><code>% cd /
% sudo trim-app -d
</code></pre>

<p class="note">Run <code>trim-app -h</code> to find out more about the available parameters.</p>

<hr />

<p>Don&#8217;t forget the standard shell practices still apply. Ctrl-C any time during operation to immediately halt execution, save output to a file with <code>trim-app -p &gt; trim-log.txt</code>, and so on.</p>

<p>Lastly, remember that while this shell script does a lot of the work (and there is an application called <a href="http://www.synium.de/cleanapp/index.html">CleanApp</a> which can help remove unnecessary lproj files), it&#8217;s still up to you to take care of removing headers, compressing images, etc.</p>
]]></content:encoded>
			<wfw:commentRss>http://lipidity.com/apple/cleaning-apps-mac-os-x/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Cocoa Animation Effects</title>
		<link>http://lipidity.com/apple/cocoa-transition-animation-effects-video/</link>
		<comments>http://lipidity.com/apple/cocoa-transition-animation-effects-video/#comments</comments>
		<pubDate>Mon, 16 Jul 2007 06:15:19 +0000</pubDate>
		<dc:creator>Ankur</dc:creator>
		
		<category><![CDATA[Apple]]></category>

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

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

		<category><![CDATA[core-image]]></category>

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

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

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

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

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

		<guid isPermaLink="false">http://dev.lipidity.com/apple/cocoa-transition-animation-effects-video</guid>
		<description><![CDATA[I wrote this app about 8 months ago, and it&#8217;s managed to find itself on Youtube.







It uses Core Graphics and Core Image APIs for the transition effects to animate the window / view, which probably needs a minimum of Mac OS X Tiger. The full source code is available.
]]></description>
			<content:encoded><![CDATA[<p>I wrote this app about 8 months ago, and it&#8217;s managed to find itself on Youtube.</p>

<!--more-->

<div class='centre'>
<object width="425" height="350"><param name="movie" value="http://www.youtube.com/v/FSUu_a_f88c&#038;rel=0"></param><param name="wmode" value="transparent"></param><embed src="http://www.youtube.com/v/FSUu_a_f88c&#038;rel=0" type="application/x-shockwave-flash" wmode="transparent" width="425" height="350"></embed></object>
</div>

<p>It uses Core Graphics and Core Image APIs for the transition effects to animate the window / view, which probably needs a minimum of Mac OS X Tiger. The <a href='http://dev.lipidity.com/apple/core-graphics-meet-core-image-demo-app' title='Core Graphics Image Transition Animation Effects'>full source code</a> is available.</p>
]]></content:encoded>
			<wfw:commentRss>http://lipidity.com/apple/cocoa-transition-animation-effects-video/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Writing internal commands for Quicksilver</title>
		<link>http://lipidity.com/apple/quicksilver-internal-commands/</link>
		<comments>http://lipidity.com/apple/quicksilver-internal-commands/#comments</comments>
		<pubDate>Mon, 25 Jun 2007 13:41:56 +0000</pubDate>
		<dc:creator>Ankur</dc:creator>
		
		<category><![CDATA[Apple]]></category>

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

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

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

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

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

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

		<guid isPermaLink="false">http://dev.lipidity.com/feature/tutorial/quicksilver-internal-commands</guid>
		<description><![CDATA[Internal commands in Quicksilver are essentially actions that don't require a subject (direct object) defined by the user. Most tasks, such as "Force Catalog Rescan", and some commands like "QS Preferences" are internal commands. The Clipboard plugin also adds a large number of internal commands so as you can see, they're quite useful in some circumstances.]]></description>
			<content:encoded><![CDATA[<p class='centre'><img src="http://dev.lipidity.com/wp-content/uploads/2007/06/quicksilver-internal-commands1.jpg" alt="[Quicksilver internal commands screenshot]" title="Internal commands in the Quicksilver catalog" class="feature" width="542" height="137" /><br /><small>Internal commands are found in the Quicksilver section of the Catalog.</small></p>

<p>There&#8217;s absolutely no documentation on how to write an internal command for Quicksilver <em>anywhere</em> and figuring out the internal workings of the application can be frustrating at best. This tutorial aims to fill that void and, like some of my other Quicksilver-related articles, provide you with the knowledge and resources to make a working internal command.</p>

<h2>Prerequisites</h2>

<p>An internal command is written much like a standard Quicksilver action, so the only thing required is the <a href="todo:lnk">Xcode project template</a> for creating Quicksilver plugins. (However, if you really need <em>another</em> project template for writing internal commands, just ask)</p>

<p>It would also be a good idea to familiarize yourself with <a href="http://dev.lipidity.com/feature/tutorial/anatomy-of-a-plugin-part-2" title="Quicksilver plugins in Xcode and Cocoa">writing a Quicksilver action</a> before going on.</p>

<h2>Info.plist keys</h2>

<p>Somewhere in the plist, you need to set QSLoadImmediately to true for Quicksilver to load your internal command properly. That&#8217;s simply:</p>

<pre><code>&lt;key&gt;QSLoadImmediately&lt;/key&gt;
&lt;true/&gt;
</code></pre>

<p>To register our internal command with Quicksilver, we need to add <code>QSInternalMessages</code> to the <code>QSRegistration</code> key. So, find the <code>QSRegistrationTemplate</code> key, rename it to <code>QSRegistration</code>, and configure the command in the following manner:</p>

<pre><code>&lt;key&gt;QSRegistration&lt;/key&gt;
&lt;dict&gt;
    &lt;key&gt;QSInternalMessages&lt;/key&gt;
    &lt;dict&gt;
        &lt;key&gt;testInternalCommand&lt;/key&gt;
        &lt;dict&gt;
            &lt;key&gt;target&lt;/key&gt;
            &lt;string&gt;testCommand&lt;/string&gt;
            &lt;key&gt;action&lt;/key&gt;
            &lt;string&gt;doTestCommand:&lt;/string&gt;
            &lt;key&gt;name&lt;/key&gt;
            &lt;string&gt;Just a Test&lt;/string&gt;
            &lt;key&gt;icon&lt;/key&gt;
            &lt;string&gt;&lt;/string&gt;
        &lt;/dict&gt;
        ...
</code></pre>

<p>What do the keys mean?</p>

<dl>
<dt>target</dt>
<dd>
<p>The class that will be running the internal command. This needs to be a singleton. More on that later.</p>
</dd>

<dt>action</dt>
<dd>
<p>An instance method that the target class responds to; this is the command.</p>
</dd>

<dt>name</dt>
<dd>
<p>A brief and meaningful name for your internal command.</p>
</dd>

<dt>icon</dt>
<dd>
<p>An icon for your command. You can use an identifier like <code>com.blacktree.Quicksilver</code> or <code>com.apple.AddressBook</code> and Quicksilver will load up the appropriate icon for you.</p>
</dd>
</dl>

<h2>The class</h2>

<p>As I mentioned earlier, the class that runs the command should be a singleton - that means only one copy of the class is instantiated and used (<a href="http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/chapter_3_section_10.html">Apple&#8217;s documentation on singletons</a>). The internal command will be called like: <code>[[targetClass sharedInstance] doActionMethod:nil]</code> where <code>targetClass</code> is the class you specified under <code>target</code> and <code>doActionMethod:</code> is the method for the key <code>action</code> in the Info.plist.</p>

<p>From the example Info.plist excerpt given above, the class that would be written would look something like this:</p>

<pre><code>// testCommand.h

#import &lt;QSCore/QSActionProvider.h&gt;

@interface testCommand : QSActionProvider
{
}
+ (id)sharedInstance;
-(IBAction)doTestCommand:(id)sender;
@end
</code></pre>

<hr />

<pre><code>// testCommand.m

#import "testCommand.h"

@implementation testCommand

// Following few methods to make this class a singleton

static testCommand *sharedTestCommandManager = nil;

+ (id)sharedInstance {
    if (sharedTestCommandManager == nil) {
        [[self alloc] init]; // assignment not done here
    }
    return sharedTestCommandManager;
}
+ (id)allocWithZone:(NSZone *)zone{
    //    @synchronized(self) {
    if (sharedTestCommandManager == nil) {
        sharedTestCommandManager = [super allocWithZone:zone];
        return sharedTestCommandManager;  // assignment and return on first allocation
    }
    //      }
    return nil; //on subsequent allocation attempts return nil
}
- (id)copyWithZone:(NSZone *)zone{
    return self;
}
- (id)retain{
    return self;
}
- (unsigned)retainCount{
    return UINT_MAX;  //denotes an object that cannot be released
}
- (void)release{
    //do nothing
}
- (id)autorelease{
    return self;
}

// Method to perform the internal command

-(IBAction)doTestCommand:(id)sender
{
    NSLog(@"Running test internal command...");
    // do something useful
}

@end
</code></pre>

<p>And that&#8217;s it. Possibly the shortest ever Quicksilver development tutorial you&#8217;re going to see in a while.</p>

<p><a name="cube-smoking" id="cube-smoking"></a></p>

<p>I thought I&#8217;d drive home the usefulness of internal commands by showing off a little something I&#8217;ve been playing with for a while.</p>

<p class='centre'><img class='feature' src="http://dev.lipidity.com/wp-content/uploads/2007/06/quicksilver-cube-disco-smoking.png" alt="[Quicksilver Cube Smoking]" title="Quicksilver cube interface smoking" longdesc="http://dev.lipidity.com/feature/tutorial/now-this-is-hot-cocoa" width="290" height="375" /><br /><small>A simple internal command allows any interface to smoke</small></p>

<p>Yeah, it&#8217;s the cube interface. Smoking.</p>

<p>In fact, with this internal command, I can make <em>anything</em> smoke.</p>

<p class='centre'><a rel="lightbox" href="http://dev.lipidity.com/wp-content/uploads/2007/06/primer-smoking.jpg" title="Quicksilver Primer interface smoking"><img src="http://dev.lipidity.com/wp-content/uploads/2007/06/primer-smoking-thumb.jpg" alt="[Primer interface smoking]" title="Quicksilver Primer interface smoking" width="426" height="343" class='feature' /></a></p>

<h2>Summary</h2>

<ul>
<li>Add QSLoadImmediately key, set to true</li>
<li>Add QSInternalMessages to the QSRegistration key</li>
<li>Write singleton class that runs the command</li>
</ul>

<p>As always, I&#8217;ll be thrilled to see what you come up with.</p>
]]></content:encoded>
			<wfw:commentRss>http://lipidity.com/apple/quicksilver-internal-commands/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Secret Quicksilver animation effects</title>
		<link>http://lipidity.com/apple/quicksilver-animation-effects/</link>
		<comments>http://lipidity.com/apple/quicksilver-animation-effects/#comments</comments>
		<pubDate>Wed, 20 Jun 2007 02:00:14 +0000</pubDate>
		<dc:creator>Ankur</dc:creator>
		
		<category><![CDATA[Apple]]></category>

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

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

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

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

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

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

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

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

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

		<guid isPermaLink="false">http://dev.lipidity.com/feature/tutorial/quicksilver-animation-effects</guid>
		<description><![CDATA[You may have had a shot at creating a Quicksilver interface. (If you haven't, see <a href="http://dev.lipidity.com/feature/tutorial/quicksilver-interface-tutorial">the tutorial</a>). While it's good fun to play with changing the colors and positions of the controls, there is a whole lot of really clever code in Quicksilver that lets you go beyond that and play with some weird and wacky effects on <em>any</em> window.]]></description>
			<content:encoded><![CDATA[<p class="centre"><a rel="lightbox" href="http://dev.lipidity.com/wp-content/uploads/2007/06/quicksilver-cube-animation.png" title="Mac Cube Transition Effect"><img alt="[Quicksilver Cube Effect]" title="Image of Core Graphics cube transition effect" src="http://dev.lipidity.com/wp-content/uploads/2007/06/quicksilver-cube-effect.png" class="feature" width="292" height="233" /></a><br /><small>Quicksilver is capable of some very futuristic transition effects</small></p>

<h2>Standard Animation Effects</h2>

<p>Quicksilver adds a category to the NSWindow class to allow for some pretty cool effects.
The following should be pretty self-explanatory:</p>

<pre><code>-(void)pulse:(id)sender;
-(void)flare:(id)sender;
-(void)shrink:(id)sender;
-(void)fold:(id)sender;
</code></pre>

<p>What these methods essentially mean, is that you can pulse, flare, shrink or fold <em>any window</em> you have a reference to in a plugin - whether it&#8217;s an interface or dialog or something completely different. Eg, just call <code>[window shrink:nil]</code> and watch that NSWindow shrink down to nothing, or <code>[window flare:nil]</code> to make it burst away!</p>

<h2>Private Core Graphics Animations</h2>

<p>Quicksilver also adds methods to use private <a href="http://dev.lipidity.com/feature/tutorial/xcode-transitions-core-graphics-image-2" title="Core Graphics Transitions Tutorial">Core Graphics transitions</a> for instances of NSWindow or its subclasses. The main method is defined something like:</p>

<pre><code>- (void) displayWithTransition:(CGSTransitionType)type option:(CGSTransitionOption)option duration:(float)duration;
</code></pre>

<p>The transitions you have access to are fade, zoom, reveal, slide, warp-fade, swap, warp-switch and the famous cube animation. You can use them on any window, for example:</p>

<pre><code>[window displayWithTransition:CGSCube option:CGSLeft duration:1.3];
</code></pre>

<p>To learn more about Core Graphics transitions, see the <a href="http://dev.lipidity.com/feature/tutorial/ebook-xcode-animations-core-graphics" title="Core Graphics Animations and Transitions Tutorial">Core Graphics ebook</a>, or play around with the <a href="http://dev.lipidity.com/apple/core-transitions-framework" title="Core Graphics and Core Image transition animations">Core Transitions Framework</a>.</p>

<p class="yellow_note">Quicksilver&#8217;s CGSPrivate.h file is missing CGSFlip, but you can run the Dashboard-style flip effect by using the integer 9.</p>

<h2>Quicksilver&#8217;s own animations</h2>

<p>Alcor has used various techniques to write several very interesting animation effects into Quicksilver. You&#8217;ll be familiar with some of these (just watch the Primer interface activate and deactivate) but for whatever reason, many of these bizarre animations never made into an interface. They&#8217;re really very clever. I&#8217;ve played around with some of these:</p>

<ul>
<li>QSGrowEffect</li>
<li>QSShrinkEffect</li>
<li>QSSlightGrowEffect</li>
<li>QSSlightShrinkEffect</li>
<li>QSBingeEffect</li>
<li>QSPurgeEffect</li>
<li>QSExplodeEffect</li>
<li>QSExtraExtraEffect</li>
<li>QSShakeItLikeAPolaroidPictureEffect</li>
<li>QSBoobTubeEffect</li>
<li>QSVExpandEffect</li>
<li>QSVillainousKryptonianEffect</li>
<li>QSMMBlowEffect</li>
<li>QSLudicrousSpeedEffect</li>
</ul>

<p>Very interesting to look at, but how does one use these effects?</p>

<p>As we are dealing with Quicksilver, the solution is (as expected) very elegant. There are very simple methods to set a show, hide or execute effect for a window. These methods don&#8217;t even require an explanation; they&#8217;re so simple:</p>

<pre><code>// Effect for showing the QSWindow
[window setShowEffect:[NSDictionary dictionaryWithObjectsAndKeys:@&quot;QSSlightGrowEffect&quot;,@&quot;transformFn&quot;,@&quot;show&quot;,@&quot;type&quot;,[NSNumber numberWithFloat:2.0],@&quot;duration&quot;,nil]];

// Effect for hiding the QSWindow
[window setHideEffect:[NSDictionary dictionaryWithObjectsAndKeys:@&quot;QSSlightShrinkEffect&quot;,@&quot;transformFn&quot;,@&quot;hide&quot;,@&quot;type&quot;,[NSNumber numberWithFloat:2.0],@&quot;duration&quot;,nil]];

// The "execute" effect is slightly different:
[window setWindowProperty:[NSDictionary dictionaryWithObjectsAndKeys:@&quot;QSExplodeEffect&quot;,@&quot;transformFn&quot;,@&quot;hide&quot;,@&quot;type&quot;,[NSNumber numberWithFloat:1.5],@&quot;duration&quot;,nil] forKey:kQSWindowExecEffect];</code></pre>

<p>Easy!</p>

<h2>Sample plugin</h2>

<p><a href="http://dev.lipidity.com/wp-content/uploads/2007/06/effects.zip" title="Download Quicksilver Effects Plugin"><img class="fright" src="http://qs.lipidity.com/fumo/QSPlugin.png" title="Quicksilver Plugin Icon" alt="[Plugin Icon]" height="128" width="128" /></a> 
I don&#8217;t dare mention all this private goodness and not even provide a demonstration. I&#8217;ve created an &#8220;Effects&#8221; plugin that will allow you to change the activate, deactivate and execute effects for <em>any</em> Quicksilver interface temporarily to play around with the effects. Just for good measure, I&#8217;ve also thrown in an action that lets you explore Core Graphics transitions as well.</p>

<p>This is not really a bona fide Quicksilver plugin; while it does let you have some fun with the effects, it&#8217;s not very user-friendly and doesn&#8217;t save your configuration. 
If these animations prove to be popular, then I&#8217;ll write a proper plugin to let you configure the effects more easily (probably through a preference pane) and remembers your settings.</p>

<p class="download"><a href="http://dev.lipidity.com/wp-content/uploads/2007/06/effects.zip" title="Download Quicksilver Effects Plugin">Download Quicksilver Effects Plugin</a></p>

<h4>Usage</h4>

<p>Type in the name of the effect you want, then use &#8220;Set Hide Effect&#8221;, &#8220;Set Show Effect&#8221; or the &#8220;Set Exec Effect&#8221; action. The list of effects is given <a href="#toc-quicksilvers-own-animations">above</a>; just type one in and run the relevant action.</p>

<p>To run a Core Graphics transition, type in the name of the transition, and use the &#8220;Run CoreGraphics Transition&#8221; action. You can optionally specify a direction in the indirect selector. (You can enter text in the direct and indirect selectors by pressing the period &#8220;.&#8221; key)</p>

<h4>For example</h4>

<ul>
<li><big>&#8220;QSBingeEffect&#8221; -&gt; Set Show Effect</big></li>
<li><big>&#8220;QSPurgeEffect&#8221; -&gt; Set Hide Effect</big></li>
<li><big>&#8220;QSExplodeEffect&#8221; -&gt; Set Exec Effect</big></li>
<li><big>&#8220;flip&#8221; -&gt; Run CoreGraphics Transition</big></li>
<li><big>&#8220;slide&#8221; -&gt; Run CoreGraphics Transition -&gt; &#8220;up&#8221;</big></li>
<li><big>&#8220;swap&#8221; -&gt; Run CoreGraphics Transition -&gt; &#8220;inout&#8221;</big></li>
</ul>

<p class="centre">
<a rel="lightbox[demo]" href='http://dev.lipidity.com/wp-content/uploads/2007/06/set-show-effect.png' title='Set Show Effect'><img src='http://dev.lipidity.com/wp-content/uploads/2007/06/set-show-effect.thumbnail.png' alt='Set Show Effect' width='108' height='128' /></a>
<a rel="lightbox[demo]" href='http://dev.lipidity.com/wp-content/uploads/2007/06/set-hide-effect.png' title='Set Hide Effect'><img src='http://dev.lipidity.com/wp-content/uploads/2007/06/set-hide-effect.thumbnail.png' alt='Set Hide Effect' width='108' height='128' /></a>
<a rel="lightbox[demo]" href='http://dev.lipidity.com/wp-content/uploads/2007/06/set-exec-effect.png' title='Set Exec Effect'><img src='http://dev.lipidity.com/wp-content/uploads/2007/06/set-exec-effect.thumbnail.png' alt='Set Exec Effect' width='106' height='128' /></a>
<a rel="lightbox[demo]" href='http://dev.lipidity.com/wp-content/uploads/2007/06/core-graphics-cube.png' title='Core Graphics Cube Effect'><img src='http://dev.lipidity.com/wp-content/uploads/2007/06/core-graphics-cube.thumbnail.png' alt='Core Graphics Cube Effect' width='105' height='128' /></a>
<a rel="lightbox[demo]" href='http://dev.lipidity.com/wp-content/uploads/2007/06/core-graphics-swap-effect.png' title='Core Graphics Swap Effect'><img src='http://dev.lipidity.com/wp-content/uploads/2007/06/core-graphics-swap-effect.thumbnail.png' alt='Core Graphics Swap Effect' width='92' height='128' /></a><br />
<small>Pictures show how to use the effects plugin. Actual animations are not shown.</small>
</p>

<hr />

<p>Quicksilver has many such hidden surprises and nifty time-saving methods; it&#8217;s so well thought out and full of these amazing gems!</p>

<p>What do you think about these animations? Be sure to let me know if you&#8217;d like a proper, more user-friendly plugin to use these effects.</p>
]]></content:encoded>
			<wfw:commentRss>http://lipidity.com/apple/quicksilver-animation-effects/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Write your own Quicksilver interface</title>
		<link>http://lipidity.com/apple/quicksilver-interface-tutorial/</link>
		<comments>http://lipidity.com/apple/quicksilver-interface-tutorial/#comments</comments>
		<pubDate>Tue, 05 Jun 2007 02:00:17 +0000</pubDate>
		<dc:creator>Ankur</dc:creator>
		
		<category><![CDATA[Apple]]></category>

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

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

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

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

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

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

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

		<guid isPermaLink="false">http://dev.lipidity.com/feature/tutorial/quicksilver-interface-tutorial</guid>
		<description><![CDATA[Writing your own interface for Quicksilver is surprisingly easy, especially if you have experience in Cocoa and Xcode. Having covered the basics about the <a href="http://dev.lipidity.com/feature/tutorial/anatomy-of-a-plugin-infoplist-part-1" title="Basics of writing a Quicksilver plugin">QSPlugin key</a> in the Info.plist, as well the as the fundamentals of writing <a href="http://dev.lipidity.com/feature/tutorial/anatomy-of-a-plugin-part-2" title="Quicksilver plugin development">Quicksilver actions using Xcode</a>, we are ready to start in a completely new direction. The Quicksilver interface.]]></description>
			<content:encoded><![CDATA[<h2>Setup Xcode</h2>

<p><a name="download" id="download"></a>
<a href='http://dev.lipidity.com/wp-content/uploads/2007/06/quicksilver-interface-plug-in.zip' title='Xcode Project Template (Quicksilver interface)'><img src='http://dev.lipidity.com/wp-content/uploads/2007/04/xcode-project.png' alt='[Download Quicksilver Interface Project Template]' title='Xcode Project Template' longdesc='Xcode Project Template for creating a Quicksilver interface.' height='128' width='128' class='fright' /></a>
Writing an interface is very similar to writing an action, but the classes and Info.plist keys are somewhat different. <big>Download the <a href="http://dev.lipidity.com/wp-content/uploads/2007/06/quicksilver-interface-plug-in.zip" title="Quicksilver Interface Project Template for Xcode">QS Interface Project Template</a></big>. If you haven&apos;t previously downloaded a Quicksilver plugin template for Xcode, you&apos;ll have to do some <a href="http://lipidity.com/apple/quicksilver-plugins-in-objective-c">basic setup</a>.</p>

<p>Once you&apos;ve installed the project template, you can build a new interface project &quot;out-of-the-box&quot; to make sure it works.</p>

<p class='yellow_note'>Unfortunately, development isn&#8217;t a linear process, so you&#8217;ll probably have to keep referring back to different sections of this article as you develop your interface.</p>

<!--

### Got Class? ###

The class that is subclassed in the interface plugin is itself a subclass of `NSWindowController` called `QSInterfaceController`. By default, the project uses `QSResizingInterfaceController`, which is able to handle expanding and collapsing of the interface as well. Most of the work that needs to be done with regard to notifications, updating actions, executing or canceling commands, etc. is taken care of by the superclass. However you need to write any animations, effects, bindings, custom controls or extra features.

-->

<h2>Info.plist keys specifications</h2>

<p><img src="http://dev.lipidity.com/wp-content/uploads/2007/02/5_info_plist.png" alt="Info.plist icon" title="Info.plist icon" class='fright no-space' />We tell Quicksilver that we&apos;re providing it an interface in the <em>QSRegistration</em> section of the Info.plist file, under <em>QSCommandInterfaceControllers</em>. By default, this should have been already added in when you created the project.</p>

<pre><code>&lt;key&gt;QSRegistration&lt;/key&gt;
&lt;dict&gt;
    &lt;key&gt;QSCommandInterfaceControllers&lt;/key&gt;
    &lt;dict&gt;
        &lt;key&gt;testInterface&lt;/key&gt;
        &lt;string&gt;testInterface&lt;/string&gt;
    &lt;/dict&gt;
    . . .
    . . .
&lt;/dict&gt;
</code></pre>

<p>There is also a key called <em>QSPreferencePanesTemplate</em> inside <em>QSRegistration</em>. If you wish to have a preference pane for your plugin, rename the key to <em>QSPreferencePanes</em> and <a href="#toc-preference-panes">create the pref pane</a>; otherwise you can leave it or delete it. As per the previous setting, these values should be filled in already by the project.</p>

<pre><code>&lt;key&gt;QSRegistration&lt;/key&gt;
&lt;dict&gt;
    . . .
    . . .
    &lt;key&gt;QSPreferencePanesTemplate&lt;/key&gt;
    &lt;dict&gt;
        &lt;key&gt;testInterfacePrefPane&lt;/key&gt;
        &lt;dict&gt;
            &lt;key&gt;class&lt;/key&gt;
            &lt;string&gt;QSPreferencePane&lt;/string&gt;
            &lt;key&gt;description&lt;/key&gt;
            &lt;string&gt;testInterface interface preferences&lt;/string&gt;
            &lt;key&gt;icon&lt;/key&gt;
            &lt;string&gt;&lt;/string&gt;
            &lt;key&gt;name&lt;/key&gt;
            &lt;string&gt;testInterface&lt;/string&gt;
            &lt;key&gt;nibBundle&lt;/key&gt;
            &lt;string&gt;com.blacktree.Quicksilver.testInterface&lt;/string&gt;
            &lt;key&gt;nibName&lt;/key&gt;
            &lt;string&gt;testInterfacePrefPane&lt;/string&gt;
            &lt;key&gt;type&lt;/key&gt;
            &lt;string&gt;hidden&lt;/string&gt;
        &lt;/dict&gt;
    &lt;/dict&gt;
&lt;/dict&gt;
</code></pre>

<p class='yellow_note'>If you&apos;re not having a preference pane, you should remove the PrefPane.nib file from your project when you&apos;re ready to release your plugin; it&apos;ll save bandwidth as well as reduce clutter.</p>

<p>You may not have an idea of the bindings you&#8217;ll be using yet (or maybe you do), but when you do use them, it&apos;s good practice to specify some default values for Quicksilver to use when the value hasn&apos;t been set by the user. These values go in the <em>QSDefaults</em> key.</p>

<pre><code>&lt;key&gt;QSDefaultsTemplate&lt;/key&gt;
&lt;dict&gt;
    &lt;key&gt;interface.bindingname&lt;/key&gt;
    &lt;string&gt;value&lt;/string&gt;
    &lt;key&gt;interface.bindingname2&lt;/key&gt;
    &lt;real&gt;2.54328&lt;/real&gt;
    &lt;key&gt;interface.bindingname2&lt;/key&gt;
    &lt;integer&gt;4&lt;/integer&gt;
&lt;/dict&gt;
</code></pre>

<p class='yellow_note'>If you&apos;re using the &quot;QSDefaults&quot; key, make sure you&#8217;re renamed it from &quot;QSDefaultsTemplate&quot; to &quot;QSDefaults&quot;.</p>

<p>Don&#8217;t forget there&#8217;s also a <a href="http://lipidity.com/apple/anatomy-of-a-plugin-infoplist-part-1"><em>QSPlugin</em> section</a> in the Info.plist file that you should fill in.</p>

<h2>Coding</h2>

<p>That&apos;s the Info.plist file out of the way for now. It&apos;s time to start coding!</p>

<p>There are a few basic Quicksilver terms I hope you&apos;re familiar with:</p>

<p class='centre'>
<img class='feature' src="http://dev.lipidity.com/wp-content/uploads/2007/06/qs-selectors1.png" alt="[Quicksilver Interface Selectors]" title="Quicksilver interface selectors" /><br />
<small>Fig 1. The selectors are the used to pick the direct object, action, and indirect object respectively. Indirect object is often optional or not required.</small>
</p>

<p>Arrangement and positioning of the views using Interface Builder is left as an exercise for the reader. You don&apos;t want a lesson on how to drag and drop, do you?</p>

<h3>Controlling the Controller</h3>

<p>As mentioned before, the superclass (<code>QSResizingInterfaceController</code>) handles most of the work for you, therefore the majority of code you add will not be in new methods. Most of the code you write is (probably) going to be in existing methods that you will subclass.</p>

<h4>Major methods</h4>

<p>Here are the main methods you may decide to add extend:</p>

<dl>
<dt>- windowDidLoad</dt>
<dd>
<p>Anything that requires interaction with the interface window when it is first loaded. As the name implies, this method is called when the interface window is first loaded. All your setup <em>with the window</em> goes in this method. For example, setting the colors, adding bindings, and choosing animation effects for hiding / showing the interface etc. Please make sure you call <code>[super windowDidLoad];</code> somewhere to let Quicksilver to do its setup as well.</p>

<p>This is not the place to add initialization code for the class itself. There&apos;s an <code>-init</code> method for that.</p>
</dd>

<dt>- maxIconSize</dt>
<dd>
<p>The maximum size allowed for icons in the interface selectors. Adjust this depending on how big or small your interface is. It&apos;s set at 128 x 128 by default, which should be fine in most circumstances.</p>
</dd>

<dt>- showMainWindow:</dt>
<dd>
<p>Any fancy animations or transitions you want for when the interface is being shown can go in here. You could also do something like: <code>if ( [[self window]isVisible] ) [[self window] pulse:self];</code>. (The pulse method is explained later). If you&apos;re having your own transition effect and don&apos;t want Quicksilver&apos;s default fade-in animation, run <code>[window setFastShow:YES]</code>.</p>

<p>If you can, call the super implementation of the method where you would call <code>-makeKeyAndOrderFront:</code>, otherwise the option to suppress hotkeys while the command window is shown wont be honored.</p>
</dd>

<dt>- hideMainWindow:</dt>
<dd>
<p>Calling the super&apos;s implementation will hide the window with Quicksilver&apos;s effect. If you&apos;re using your own animation, you can run <code>[window setHideEffect:nil]</code> so Quicksilver doesn&apos;t run its own effect as well.</p>
</dd>

<dt>- activateInTextMode:</dt>
<dd>
<p>The use of the &quot;Activate in Text Mode&quot; trigger may be a rare occasion, but it&apos;s a cool place to throw in a surprising animation. <a href="http://dev.lipidity.com/downloads/fumo" title="Fumo Quicksilver Interface">Fumo</a> has used <a href="http://dev.lipidity.com/index.php?tag=core-graphics" title="Core Graphics Animation and Transition Effects">Core Graphics</a> warp and cube effects for this in the past.</p>
</dd>
</dl>

<h4>Expansion</h4>

<p>If you&apos;re having your interface expand and collapse, you&apos;ll need to subclass the following methods:</p>

<dl>
<dt>- showIndirectSelector:</dt>
<dd>
<p>Position the indirect selector in an appropriate location on the window. It may be useful to position it relative to another object, for example: <code>[iSelector setFrame:NSOffsetRect([aSelector frame],0,-250)];</code>
Don&apos;t forget to call super!</p>
</dd>

<dt>- expandWindow:</dt>
<dd>
<p>Expand your interface. You&apos;ll probably be using <code>[[self window] setFrame: display: animate:]</code> to resize the window smoothly.</p>
</dd>

<dt>- contractWindow:</dt>
<dd>
<p>As per <code>-expandWindow:</code>, but make the window smaller.</p>
</dd>

<dt>-hideIndirectSelector:</dt>
<dd>
<p>In most cases you wont need to subclass this. It just hides the indirect selector.</p>
</dd>
</dl>

<h2>Preference Panes</h2>

<h3>The easy stuff</h3>

<p>Creating your preference pane is easy since a lot of the work is done for you by the project template. There&apos;s a .nib file included in the interface project called &quot;testInterfacePrefPane&quot; (where &quot;testInterface&quot; is the name of your Xcode project). With this .nib file:</p>

<ol>
<li>Open it.</li>
<li>Add controls and bindings.</li>
<li>Save and quit.</li>
</ol>

<p>That&#8217;s <em>creating</em> it, though. There&apos;s still a little more work to be done.</p>

<h3>Info.plist keys</h3>

<p>Remember the <em>QSPreferencePanes</em> key I mentioned earlier? That needs to be present under the QSRegistration key in the Info.plist file. You don&#8217;t need to configure it, but you can change some keys like <em>description</em> with a short summary of the preference pane, or set an <em>icon</em>.</p>

<p>(Reminder: Make sure to drop the &quot;Template&quot; from &quot;QSPreferencePanesTemplate&quot;)</p>

<h3>The &quot;Customize&quot; button</h3>

<p>To set a customize button to show the preference pane for your interface, simply uncomment the <code>-customize:</code> method that&apos;s added in by default. It&#8217;s that easy.</p>

<pre><code>- (IBAction)customize:(id)sender{
    [[NSClassFromString(@"QSPreferencesController") sharedInstance] showPaneWithIdentifier:@"testInterfacePrefPane"];
}</code></pre>

<h3>A note on bindings</h3>

<p>When the user changes something in the preference pane, the binding will be updated. And that&#8217;s it. You&#8217;ll need to bind your classes / controls not only in the preference pane, but also in your code so that any changes to bindings reflect in your interface. For example, if you add a binding for &quot;testInterface.fadeStyle&quot;, you may want to do something like:</p>

<pre><code>[self bind:@"fadeStyle"
     toObject:[NSUserDefaultsController sharedUserDefaultsController]
  withKeyPath:@"values.testInterface.fadeStyle"
      options:nil];</code></pre>

<p class='note'>If you&#8217;re binding an NSColor, make sure to use NSUnarchiveFromDataTransformerName</p>

<h2>Building</h2>

<p>Once you&#8217;ve got your code ready, build the project.</p>

<p>(Don&#8217;t puke if you get warnings that you can&#8217;t get rid of; there will be a few warnings regarding some methods like <code>-setWindowProperty:</code>, and another great error if you add a preference pane and the customize button. We&#8217;ll have to live with them for now.)</p>

<p>Double-click on the built product to have Quicksilver install it. If there are no serious faults with the code, you&#8217;ll see your Incredible Interface&trade; and be able to enjoy, use, and distribute it!</p>

<h2>Summary</h2>

<ul>
<li>Download the <a href="#download" title="Quicksilver Interface Project Template for Xcode">Xcode Project Template</a>, making sure everything else is <a href="http://lipidity.com/apple/quicksilver-plugins-in-objective-c" title="Setup Xcode for Quicksilver plugin development">setup correctly</a></li>
<li>Add code to the subclass of <code>QSResizingInterfaceController</code></li>
<li>Arrange the views in Interface Builder</li>
<li>Add preference pane and bindings</li>
<li>Build project</li>
<li>Test and distribute</li>
</ul>

<p>If you need any additional information or resources, help is always available at the <a href="http://blacktree.cocoaforge.net/forums/">Quicksilver forums</a> or you can contact me.</p>

<p>Be sure to show me how you go with your interface!</p>

<h4>Also in this series</h4>

<h6><a href="http://lipidity.com/apple/quicksilver-plugins-in-objective-c" title="Step by step guide to writing plugins for Quicksilver in Cocoa using Xcode.">Quicksilver plugins in Objective-C</a></h6>

<blockquote><p>A guide to obtaining the required resources for Quicksilver plugin development in Xcode. Also runs through setting up Xcode to make creating plugins easier.</p></blockquote>

<h6><a href="http://lipidity.com/apple/anatomy-of-a-plugin-infoplist-part-1">Info.plist - QSPlugIn</a></h6>

<blockquote><p>The various sections of Quicksilver plugins, including the basics on how to properly configure your plugin, setup the correct options and settings, and how and where to write your plugin.</p></blockquote>

<h6><a href="http://lipidity.com/apple/anatomy-of-a-plugin-part-2" title="How to write a Quicksilver action using Xcode and Cocoa.">Quicksilver Actions</a></h6>

<blockquote><p>A quick look at QSActions in the Info.plist, followed by our first look at the code needed to get a plugin up and running.</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://lipidity.com/apple/quicksilver-interface-tutorial/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>
