I decided today that I would learn a bit of Quartz Composer. I had never touched it before, beyond reading a couple articles and watching a conference talk. The most useful introduction for me was "UI Prototyping with Quartz Composer and Origami" by Pasan Premaratne. It takes you from absolute zero to having built a simpler version to Path's attractive spinout menu with Facebook's Origami.
I recommend you not only read Pasan's post, but also actually follow along! At worst you'll have spent twenty minutes exploring a novel way of doing things. At best you'll have acquired a new tool for your kit and become even more handsome.
Near the end of his post, Pasan laments:
The only downside that I see right now to using Quartz Composer is that if you're prototyping something complex, your composition can get unwieldy and convoluted fairly quickly. In just creating a radial menu with three buttons we have over 20 patches in our composition.
I agree that it can become unwieldy. Here's a snapshot of my small composition:
Those three yellow blocks contain essentially the same code. Each block does not explicitly group its contained patches; they are spacially arranged in a particular region on the canvas. Nothing more. So a yellow block is about as constructive as a source code comment.
These blocks contain the same patches duplicated with slightly different parameters. That is of course a bit offensive to me as a programmer. If possible I would like to clean up that repetition. But I'm not even sure that I can.
Here's the rub. If Quartz Composer lacks tools to abstract away chunks of your composition, then it is little more than a shiny toy. It would be like a programming language that does not support creating functions. But if QC does enable building bigger, reusable units of design, then it is worthy of my attention.
So! Let's learn how to build abstractions in Quartz Composer, together! This is my very first day with QC, so there are going to be some false starts. Bear with me. :)
Step one is to read and follow along with everything in Pasan's post.
Go do it. I'll be here.
As I went through Pasan's tutorial, I kept seeing mention of a "Macro" feature all over Quartz Composer. If it resembles any other system's macro functionality, that would be one way to reduce complexity in your project.
The next step, then, must be to select the group of patches responsible for one of the buttons and click
Create Macro in the toolbar. This replaces all the patches with a single
Macro Patch. The noodle from
Drag outlet is connected to this macro patch, which is a good sign. In fact if you go to the Viewer you should see that nothing has changed.
Double click the macro patch to jump into its definition. Take care to double click in its body, not its titlebar (which renames the patch). We can see that it is almost identical to what we had before. However there is a new patch called
Number Splitter near the top. This must be how Quartz Composer connects the
Drag interaction from outside the macro to the inputs of the animations inside the macro. Observe that
Number Splitter's inlet is green, presumably to indicate that it is special in this way.
If we want to make this macro reusable, we would need to create our own parameters. The button image must be one such parameter otherwise every button will be labeled "A". The best place to start is to delete the image patch which is hardcoded to be
To parameterize the image, right click the
Layer patch. Under
Publish Inputs select the
Image property. QC offers to let you name the parameter differently from the name that the
Layer patch expects, but in this case we can stick with
Image. This turns the inlet green, which matches the
Number Splitter parameter. Here's to hoping we're right!
Edit Parent in the QC toolbar leaves the macro and returns to our overall composition. If you look at the macro patch you can see that it now has an
Image inlet. Drag that
ButtonA.png file back into the composition, delete its
Layer, and hook up its outlet to the macro's
If all went according to plan, there should be no change in the Viewer. Indeed there are still three spinout buttons labeled A, B, and C.
Here begins one of my false starts. Read on before making changes to your composition.
Let's get rid of the other two buttons and replace them with macros. Copy and paste appears to work just fine on macro patches. Ensure that each of the three macros has its
Image inlets populated.
If you flip to the Viewer you'll see that there's only one button. The problem is that all three buttons are animating between the same positions at the same times. The other two buttons are hiding below the visible one.
To fix this we need more macro parameters. But to add them, we would need to edit the macro three times, once for each button. This is because we used copy and paste to duplicate the macros. Just like in programming, copy and paste is a worst practice.
Lesson learned. False start over.
Copying and pasting macros didn't pan out. How else can we achieve the abstraction we want?
Another button in the QC toolbar is
Add to Library. I presume that is for reusing the components you have created across different projects. Let's add the macro to our library as a new patch type. After playing around a little I've discovered that rather than adding the macro to your library directly, it's better to explode the macro first (available under its right-click menu). Call the new patch type
Then from the Patch Library drag in two more
Radial Buttons and wire them up. You'll notice that the inlets are now called
Input. That's downright ridiculous, so let's fix that problem first.
In the Patch Library, right click the
Radial Button object and select
Edit. Then without selecting a patch, open up the Patch Inspector. This inspects
Radial Button itself. In the dropdown at the top select
Published Inputs & Outputs. You'll see a table of
Input mapped to
input_proxy_1 and another
Input mapped to
input_proxy_2. Change one of the
Input labels to
Progress and the other to
Image. I don't know if there's a way to immediately see which is which. However if you save and reopen
Radial Button, the label on the image layer should say
Progress. If you guessed wrong be sure to flip them the other way.
We've renamed the properties, so let's go back to our composition to see our change.
Ah crud. Still two uselessly-named
Input inlets. I bet that every time
Radial Button we must remove it from our composition and add
the new version back in. What a pain! If you know a better solution,
please get in touch. Otherwise, if you really must remove and re-add your custom patches after each change, it's probably best to finish the patch in isolation before adding it to your project.
Beware! Don't forget to adjust the layer ordering any time you add new layers.
Hit Area should be the layer with the highest number, then the
Add Button layer should be the next layer below that. If you miss this step, you will see rendering bugs. Or worse, the touch handler mysteriously won't fire, because it is obscured by other layers.
Next let's make more properties into parameters.
y-coordinate are as good a place to start as any. Recall that in Pasan's post we assigned different
End Values to each button's
Transition patches. However, notice that the
Start Value and
End Value of the
Transition patch have ordinary inlet ports. That means we could publish those inputs from
Radial Button itself. Start by right clicking
Transition X, selecting
Publish Inputs, then
End Value. Call it
End X. Similarly, publish
End Value as
Go back to your composition, remove the
Radial Button patches, then
re-add them. You'll see that you have the new
End X and
End Y inputs. Hook up the
Image inlets as before. Then, using the same method as in Pasan's post, assign constant values using the Patch Inspector for each of the
End X and
End Y inputs on each of the three buttons. For convenience they are:
- A button:
- B button:
- C button:
Notice as you're editing how the default values for
End X and
End Y are the values that had been assigned to each
End Value of
Button A's two
Transition patches. You should change them to better defaults (like
0) by editing the
Radial Button patch. Use the Patch Inspector with no patch selected, then change the values under
In the Viewer, confirm that the animation is working again. The only thing left to fix is the friction and tension of each of the buttons coming out. I'm sure you can handle that.
How Bout Dem Abstractions
Thanks to our custom
Radial Button patch, there is less duplication in our composition. The number of patches has decreased by half, which makes the design more comprehensible.
That is not good enough.
If we want to add or remove a button in this menu, it'd be a surprising amount of work. We would need to do some trigonometry for each button to produce a new set of magic numbers. No way! It needs to be as easy to add a button as it would be in, say, Interface Builder.
Before we attempt that, let's simplify the problem first. Let's move everything to the origin. Change the
Start Value for both
Transition X and
Transition Y to
0. And then in your composition, change the
y-coordinate of the
Add Button and the
Hit Test from
Using the origin, instead of the magic
-512, reduces the fiddliness of the task. Also, we'll lay the buttons across the complete circle around the add button, rather than just half or a quarter of it. Once we have that working, coming back and restoring these constraints would be straightforward.
To begin, we'll remove the
End X and
End Y published inputs from
Radial Button. You remove them the same way you added them: just right click the patch and select the property under
The new inputs we'll want are
Radius input will tell us how far from the origin to move that button.
Index will be used to decide where on the circle that button will go.
To calculate the destination of each button, we'll need to use
cos. Quartz Composer provides a
Mathematical Expression patch that evaluates expressions of arbitrarily many variables. We'll need one patch for the
x-coordinate and another for the
y-coordinate, so drag out two.
If you inspect a
Mathematical Patch, under the
Settings pane there is a text field for formula. Any free variables in this formula will end up as inputs to the patch (which is a wonderful bit of design).
x-coordinate patch, we'll want to use the formula
sin(360 * index/count) * radius
sin uses degrees not radians. Knowing that will save you the
twenty minutes of head-scratching and intense self-doubt that I suffered. :)
y-coordinate patch, we'll use the same formula but with
cos instead, producing
cos(360 * index/count) * radius
Result outlets of these two patches up to the
End Values of the corresponding
We'll need to publish the
Radius properties. If we do that on one of the two
Mathematical Expression patches, the other one wouldn't receive those inputs. If we do it on both patches, we might expect that Quartz Composer would send the same value to both patches, but that's not the case. The patch just publishes two sets of inputs with the same names. Useless!
What we want here is an
Input Splitter. Right click one of the
Mathematical Expression patches and select
Insert Input Splitter for
Radius. This gives you a tiny patch with an unnamed input and output. The key to using this is that you can send output from one patch as input to multiple other patches. So drag the noodle from the
Input Splitter to both
Mathematical Expression patches'
Radius input. Repeat for
Then finally we can publish the
Index properties in the usual way from the
Input Splitter patches.
With all those changes made, our
Radial Button patch looks like this:
And then our project can use that new and improved version like this:
Radius set to
Count set to
2, we get the following result:
Great! We can factor out the
Friction parameter in the same way. (This is your cue!)
Put Your Abstraction to Work
That was certainly a lot of work to build out that
Radial Button patch. Let's see how well it serves us by adding a fourth and fifth button to the menu.
First, add your new images to the composition. Delete their
Layer patches. Drag in two more
Radial Buttons. Hook up their
Then set the
Radius of the two new patches to
200. Set the
Count of all the
Radial Button patches to
5. Then finally set the
Index values of the new patches to
If you wanted to, you could use an
Input Splitter to avoid duplicating the
Count properties and make it even easier to add buttons.
I wonder if there's an automatic way to specify the
Index properties. Is there a way to count or enumerate the number of connections from an outlet? Quartz Composer provides a lot of patches so I would not be surprised if it did.
To ace this project, you could move the menu to the bottom left of the screen. Make it cover only a quarter of the circle like Path does. I imagine you could implement the latter merely by choosing interesting values for
Count. Or, better yet, expose the
360 factor from the
cos formulae as a published input.
Quartz Composer is certainly an interesting tool to have in your repertoire. Origami does a lot to make it more usable and more flexible. For future projects I no longer have a reason to prototype animations in code. Origami helps you design your interactions faster and gets you closer to that sweet, sweet instantaneous feedback.
Yes, it is too easy to create a mess of your composition with too many patches. Just like not using functions would make a mess of your code. Quartz Composer thankfully does provide designers robust tools for creating and reusing abstractions. You just have to think like a programmer. ;)
Update: Jeremy Bishop has taken this a couple steps further. He added more parameters and took a stab at the iterator idea.