Archive for March, 2008

Sparkle And Spin

Tuesday, March 25th, 2008
paul_rand.jpg

I found this 1993 video of Steve Jobs being interviewed about Paul Rand over on Paul Robinson’s site. It means so much to me when I see/read/hear someone that I admire admiring someone else that I admire.

Towards the end of the interview, Jobs is asked what his favorite Paul Rand work is. His answer is also my favorite – an offshoot of his famous IBM logo. It combines the wit and playfulness of his children’s illustrations with the discipline and craftsmanship of his corporate logos. Rand’s personality shines in this image:

eye-bee-m.jpg

Here are some of the children’s illustrations that I lifted from this post. “Sparkle and Spin” is the title of one of the children’s books he created with his wife, Ann.

paul-rand-design-work-2.jpg
paul-rand-design-work-1.jpg

And some of the corporate logos we all know too well…

PaulRandLogos.jpg

There’s an article on Design Observer with a funny story about how the Enron logo got its green after Paul Rand’s death in 1997. The middle prong of the E was originally yellow. The article describes all the fanfare and celebration that surrounded the unveiling of Enron’s new corporate identity but…

Within hours, the world would laugh it off the stage. Houston faxed the logo to Enron’s offices in Europe. But in transmission the middle, yellow prong disappeared, leaving the new design meant to celebrate Enron’s triumphant ascension looking more like an electric plug. Worse, to the Italians it resembled an obscene hand gesture, one that meant about the same thing as shooting a middle finger at an American. The European executives roared with laughter: now they had a new way to win Italian customers.

Back in Houston, dismay grew: the yellow prong also vanished when run through the copying machine. Somehow, Enron had spent millions of dollars on a new business logo without bothering to check if it worked in business. Soon the hallway signs went down, the new cards and letterheads were shredded. With no fanfare, another logo was introduced, replacing the yellow prong with a green one.

The symbol meant to carry Enron into the next millennium hadn’t lasted a week.

I would love to hear Rand’s comments on this teeny graphic design mishap.

One last treat. The design company, Imaginary Forces, did a great job animating Paul Rand’s work. The animations are inter-cut with clips of Paul Rand speaking about design. They’ve really captured his spirit with these animations.

Hi, I’m Cathy

Sunday, March 23rd, 2008

I’m feeling absolutely terrible because I snapped at someone on the Cocoa-dev list today. I was super bitchy and unhelpful to someone who asked a simple question. It was completely inappropriate and uncharacteristic of me. I hate it when I see people getting impatient with others who are simply reaching out to the community for guidance so I’m kind of mad at myself now.

The reason that this is bothering me so much is that I love to help people learn, especially beginners. I’ve always been this way. When I was in school, I always got jobs in the computer labs because I enjoyed helping people figure out how to use the software (getting keys to the equipment room was nice, too). When I moved to New York City years ago, with no real clue about what I was doing with my life, I got a job at the Soho Apple Store giving lessons in the theater. At the time, we gave free lessons in Final Cut Pro, After Effects, Photoshop, Shake, etc. It was great because the store let us design our own lessons for the Pro Apps. We didn’t have to give the standard Keynote presentation you see in Apple Store theaters today. We really taught people there and we had an audience that wanted to learn and engage with us. I admired the service the store was providing the community and I had a blast.

The guilt I’m feeling right now is also magnified by the growing visibility of this blog. I really had no idea that anyone, other than the few people I know personally who develop Mac software, would be interested in reading any of this. I started it so that I would be forced to articulate my thoughts and ideas in writing. It was like a school assignment for myself - an exercise to stretch my brain. But the internet is fast, especially with services like Twitter, and the numbers have been rising every day. This is good and bad. Good because I find myself in a position to help people again, just like the good old days at the Apple Store, bad because I have to take some measure of responsibility for what I say here and places like the Cocoa-dev list, and this isn’t what I was after when I started out a month ago.

But, that’s what I have so I’d like to introduce myself for real and explain my intentions a little bit. I know that the couple of sentences on my ‘About’ page aren’t very forthcoming.

My name is Cathy Shive and I’m a Cocoa developer. I work for a New York City company called Box Services, LLC. Their business is digital photography with a focus on the fashion industry. I’m part of a small software team that develops their in-house tools. I love the projects that I work on at Box and I admire my teammates. They’re incredibly talented and I learn from them every day, more than I ever let on ; )

My background is in arts and design. I’m a self-taught programmer. Basically, I was a power user until I got soooo into software that I had to learn how to make it myself. My interests have always been in interface design. My first apps were video apps centered around some kind of novel interface idea. I learned how to program on a Mac using the QuickTime API, OpenGL and C++. Once I started using Cocoa and Objective-C on a regular basis, I quickly learned to appreciate the importance of a good API and my “interface” interests grew into developing frameworks and, what I hope will be, good APIs. I’m also completely obsessed with software architecture and ‘design patterns’. I can’t quite put in words why, but it fascinates me to no end.

As I mentioned earlier, I started this blog as a way to develop my ability to articulate the thoughts and ideas that I have about my work. When I started, I didn’t have a clear plan set for what the content would be exactly or who my target audience would be, but I knew that it would involve UI design and programming UIs with Cocoa and that it should be interesting to both designers and developers. I thought that each could learn a little about the working processes of the other. I truly believe that these two fields are going to merge in a big way in the coming years. The Mac platform is a perfect place for this kind of union because it has a very rich history in both fields. We stand on the shoulders of giants like Alan Kay, Susan Kare, and Jean-Marie Hullot. I can’t think of a place I’d rather be.

My first post was a criticism of a feature in the Cocoa framework. I’m going to continue to do this because I see a specific weakness in Cocoa that I believe needs to be addressed. It’s probably obvious by now, but I’ll go ahead and name it clearly. Cocoa should be on the cutting-edge when it comes to support for interface development, but as it is, there are very important features that are barely usable – the layout and styling features. These are the most basic tools that an interface designer and developer needs to do their job. In the case of styling, there is no strategy at all and in the case of layout, there is one very broken mechanism. Imagine if something like the key-value observing mechanism sometimes forgot notify observers of changes in the values they’ve registered to observe. That’s how bad it is to an interface developer when they resize their views and they get totally messed up and stick to some seemingly arbitrary place in their superview. The work-arounds are messy, laborious and hard to maintain when the project is semi-big. I’m going to try to keep further criticism on this topic and the topic of styling constructive but I imagine there will be emotional rants every once in a while.

I want to keep exploring the Cocoa framework with tutorials. The tutorials I post on this site are as much for my own learning as they are for others, so please take it all with a grain of salt since I will be exploring some unconventional ways of doing things. In fact, maybe I’ll stop using the word ‘tutorial’. I’ll think of something else. The reason that I use this format is that there’s no better way to learn than to have to explain something to someone else. I really appreciate getting people’s feedback on these posts. I work on certain assumptions I’ve formed through my own experience and I’m glad when someone can show me a different way to look at something. I’m self-taught, so I have a very cathy-oriented way of approaching things.

I also plan on writing more design-specific posts. I want to talk about graphic design as well as interaction design and also about the products and concepts, old and new, that inspire me to do the work that I do. Since I spend so much time coding, it’s hard to get into the frame of mind necessary to write about these things, but it is important for me to keep them on the forefront of my thinking as much as possible.

So that’s me and those are my intentions. Thanks for reading and for your comments. I apologize again to Luca, who I snapped at in public today. I’m totally embarrassed about that.

NSView and Autresizing. Yes, Again.

Thursday, March 20th, 2008

The first post I made on this blog had to do with NSView’s autoresizing behavior being unreliable. As a Cocoa UI developer, this is a real problem that I just have to deal with. But, that’s not the worst thing about autoresizing in NSView. The worst thing about autoresizing in NSView is that setting an autoresizing mask requires that your view controller or window controller explicitly set the size and position of the view within its superview before it can set the autoresizing mask. And whenever the view hierarchy changes, lets say that you need your layout to accommodate a new sibling view at some level of the hierarchy, you have to recalculate everyone’s frame and reset everyone’s mask at that level. Icky, dirty code.

I can see that Cocoa’s getting more sophisticated with how it approaches layout. We can use “layout managers” with CALayers that have this great “constraints” system. From the docs:

Constraint-based layout allows you to describe the position and size of a layer by specifying relationships between a layer and its sibling layers or its superlayer

Relationships. That makes more sense. I want my views to have relationships to each other and I want the views or some layout manager to have the responsibility of maintaining those relationships when the view hierarchy changes, not the controller classes. That’s not what view controllers and window controllers are for.

So I wonder why all this great layout development for layers and not views? Since layers aren’t in the responder chain, they’re not a viable view substitute and I can’t imagine that CALayer will ever become an NSResponder because of its relationship to another framework that we can’t talk about. Maybe NSView is on its way out? There are slight hints that some changes are underway, but even if that’s the case, I can’t imagine that it’ll happen any time soon. NSView does a lot. So, how about an NSViewLayoutManager class in the mean time? Or new options for autoresizing masks? Yay! We Love View!

I filed a bug report requesting a layout manager class for NSView: Bug ID# 5809928.

The Next Device

Friday, March 14th, 2008

The iPhone and its SDK inspire our imaginations. But maybe the thing in our imaginations is not this device, but the next device. There seems to be a general mood of frustration among developers stemming from the restrictions Apple is placing on third-party iPhone applications. As John Gruber points out today in his post, One App at a Time, the restrictions aren’t unreasonable considering the hardware. So maybe the angst and frustration we feel is coming from the fact that we have the tools in our hands and we finally have a real sense for this new platform and the possibilities are so clear to us but we have the wrong device. Maybe the thing that we’re all hungry for is not the iPhone, but something else. Something that’s a little less of a phone and not quite a laptop. When you read though the CocoaTouch documentation, do your imaginings end with a phone?

It’s important for us to be patient with Apple and with ourselves right now. This is something totally new and we need to take small steps to make the transition a smooth one. As developers, we have to completely re-conceptualize our idea of how an application should behave and how to engage users without a keyboard or mouse. We’ve been using the same model for software design since windowed GUIs were first introduced in the 80’s. We don’t know any different and we shouldn’t underestimate the challenges we’ll inevitably encounter. I can’t blame Apple for being so protective of their new platform. We have a lot to learn and so do they. Small steps, patience, a little bit of trust and compromise from both sides is going to be key to their and our success in this new endeavor.

But it’s hard to be patient when we’re all feeling so passionate. I look at this beautiful device and I read the documentation of its SDK and it makes me feel restless. I know that this is much more than a phone. But for now, all it is is a phone. It’s the iPhone. It’s not the next device. That will come next and there will be another after that. In the mean time, we have plenty to explore and have fun with. If you think about it, they’re giving us a lot. We’ll find out their next move soon enough.

Welcome, Designers

Monday, March 10th, 2008

My head has been reeling all weekend after watching the iPhone SDK presentation. The excitement I felt the first time I saw an iPhone has returned full-force after it was crushed by Steve Jobs during last year’s WWDC keynote address. I still think he owes all of us an apology for trying to pass off Ajax techniques as an SDK like we’re stupid. Anyway, I’m super happy and optimistic about all the new technology we get to play with and like many others, I can’t help but think about the future. There’s one thing in particular that I can’t get off my mind.

There are going to be lots of new Cocoa developers and many of them will come from the field of design, not software development. Some will have never programmed before in their life. This happened with HTML and CSS when the web became popular. It’s going to happen in the world of iPhone development and Mac desktop application development will naturally follow.

This influx of new developers means that the usability of the Cocoa’s APIs are going to be put to the test like never before. Bugs like NSView’s autoresizing not working as advertised or the fact that the methods to customize the drag and drop highlights of an NSTableView are private and not legally accessible to developers are not going to sit well with this new generation of Mac devs, who will demand control over every aspect of their visual design.

It should be possible for the Cocoa engineers to give developers complete control over the visual properties and interactive behaviors of their classes. I don’t mean in that convoluted creating a custom cell way, it should be easy — HTML and CSS easy (in fact, why don’t we use style sheets?). If there are points where this is technically impossible, it’s a design flaw of the framework and should be fixed. If it means writing a new NSTableView, so be it.

Sorry, you’re screwed

I keep thinking of an article I read about the history of CSS and this part in particular:

Meanwhile, writers of Web pages were complaining that they didn’t have enough influence over how their pages looked. One of the first questions from an author new to the Web was how to change fonts and colors of elements. HTML at that time did not provide this functionality - and rightfully so. This excerpt from a message sent to the www-talk mailing list early in 1994, gives a sense of the tensions between authors and implementors:

In fact, it has been a constant source of delight for me over the past year to get to continually tell hordes (literally) of people who want to — strap yourselves in, here it comes — control what their documents look like in ways that would be trivial in TeX, Microsoft Word, and every other common text processing environment: “Sorry, you’re screwed.”

The author of the message was Marc Andreessen, one of the programmers behind NCSA Mosaic. He later became a co-founder of Netscape and by then his views - if they ever were his views - on formatting had changed.

I doubt that any Cocoa engineer has such a flippant attitude towards designers. After all, this is OS X — the best looking operating system on the market. I just hope that they will take these types of issues into more serious consideration when mapping out their priorities. Sure, advertising “New Table Views!” in Cocoa isn’t as sexy a feature as Core Animation, but it’ll make everyone’s life easier in the long run. Imagine this question popping up on the Cocoa-dev list: “How do I customize the background color of a column in an NSTableView”. Now think of the answer…

The Knowledge Navigator

Monday, March 10th, 2008

Features from this 1987 video that have been realized:

  • Touch interface (iPhone)
  • Voice commands (Speakable items)
  • Animated views (Core Animation)
  • Video conferencing (iChat)
  • Looking at documents in a video conference (iChat Theater)

Someone needs to implement the bow-tied digital butler.

Creating a Custom Control with NSView

Friday, March 7th, 2008
AsimoControl.jpg

This tutorial is about implementing a custom control class. The goal is not to explain how to subclass NSControl or NSCell, but how to think more generally about controls, what they are and how to implement one with an NSView subclass. As with the last tutorial, it will be most useful if you download the example project and follow along in the source code. The comments will provide the implementation details. It’s also fun to give Asimo a pompadour.

The tutorial is based on a similar presentation that I gave at a New York City CocoaHeads meeting a few months ago.

What is a Control?

A control is an interface element that allows users to manipulate data in an application. It’s main responsibilities are to draw itself, handle events, and communicate its values to other parts of the application.

Cocoa’s AppKit framework comes with several controls out of the box:

Picture 43.png
Picture 45.png
Picture 46.png

All of the controls pictured above are decedents of Cocoa’s control class, NSControl. As the documentation states, NSControl works closely with NSCell to provide the basic features of a user interface object. In most cases, you can use an AppKit control with no extra code.

Why Make a Custom Control?

With all of AppKit’s ready-made controls, why would you need to implement your own? Here are two situations where the Cocoa control classes are not a good choice for your interface:

1. None of the AppKit controls support the interaction model you want to implement

Take Photoshop’s curves interface as an example. This interface requires that the user is able to add control points to a bezier curve. They must then be able to select the control points and drag them around to adjust the values of the curve. If they click on the pencil button, the interface must switch to a mode that lets them draw the curve directly into the view.

PSCurves.jpg

You’re not going to find an out of the box control that supports this interface in AppKit and NSControl may not be an appropriate place to start your custom implementation. If you look at its API, it is clear that NSControl was designed to create the AppKit controls. They all have fairly straightforward drawing, event handling and target/action needs. Point-and-click. If you were to use NSControl as a starting point for this interface, you would need to create a few custom cell classes that you would manage in your control’s custom cell. At this point, you’re asking for a headache. This interface is not what NSControl was designed for. You’re going to spend more time fighting with the framework than implementing your ideas.

2. You are drawing with OpenGL instead of Quartz

Picture 53(2).tiff

All of the controls in the iTunes Cover Flow interface are drawn with OpenGL textures. That includes the buttons, scrollbar and slider. These are absolutely not NSControls, which can only draw in a Quartz graphics context. If you want users to interact with your NSOpenGL view, you’re going to have to start from scratch with your controls. Don’t let this scare you away from using OpenGL views for your interface. They’re fast fast fast and if your app is displaying lots and lots of images, especially in a full screen situation with animation, they’re the way to go.

Leopard’s LayerKit (Core Animation Layers) technology advertises mixed Quartz and OpenGL drawing, but I just have a hard time getting behind that idea. More on this thought another time.

Using NSView

The subject of creating controls for an OpenGL view is a little out of the scope of this tutorial. Again, I will come back to the issue of OpenGL views in the not-too-distant future.

Working with NSView will let us touch on the very basics of what we need to make a control. For really specialized, one-off interfaces that you can add to your window’s view hierarchy with no fuss, it makes a lot of sense to start here.

As I mentioned before, there are three parts to a control. Let’s make a to-do list for our class design. Our view must be able to handle:

  1. Drawing
  2. Event Handling
  3. Communication with other parts of your app

We’ll handle number one with NSView and number two with its superclass, NSResponder. For number three, we’ll use Cocoa bindings.

What Are we Controlling?

Before we subclass anything, we need to figure out what our control will control. The example project contains a control for adjusting the values of a twirl distortion CoreImage filter. To start the design, I looked at the documentation for CITwirlDistortion. It lists the three attributes of the filter: center postition, radius and angle. I decided to create a circular design for controling all three attributes at once. This seemed to be a nicer interface than providing three sliders. Of course this design has some usability issues that will be obvious once you play around with it a bit, but it’s fun to experiment.

KDTwirlDistortionControl has this design:

conrolDiagram.jpg

It’s basically a circle. Users can drag on the main gray part of the circle to adjust its radius - this will change the radius attribute of the filter. Dragging on the blue center point will change the position of the circle and the center position attribute of the filter. Finally, dragging the small black circle around its perimeter will adjust the angle attribute of the filter.

We have an interface design. Time for code.

Defining the NSView subclass

Our NSView subclass, KDTwirlDistortionControl, needs to keep track of three rectangles (pictured in the diagram) that it will use for drawing and hit detection. These rectangles will also be used to calculate the adjustment values of the filter. It declares the following ivars for the rectangles in the header file:

// drawing and hit detection
NSRect	mPositionControlRect;
NSRect	mAngleControlRect;
NSRect	mRadiusControlRect;

The control also contains 4 float values that can be bound to. It does this by declaring the following ivars and their getters and setters:

// the values we bind to
float	mPositionX;	// a value between 0 and 1
float	mPositionY;	// a value between 0 and 1
float	mRadius;	// a value between 0 and 1
float	mAngle;		// a value between 0 and 6 radians

The getters and setters will always return normalized values for these ivars, which are calculated based on the positions and sizes of the rectangles in the view’s coordinate system. Look in the code for more details about this part of the implementation.

With the getters and setters in place, our control can communicate changes to these values to other classes with no more code. We can check number three off of our to-do list. Thanks Cocoa.

The last thing our control needs to manage is its state. There are four possible states in our design:

  1. the control is inactive
  2. the position rectangle is active
  3. the radius rectangle is active
  4. the angle rectangle is active

We’ll use this information when we draw to give the user feedback about what they are adjusting. We’ll also use it in our mouse drag handler so that we know which rectangle the user is adjusting. To define the states of the control, create an enum type like this:

typedef enum
{
	kKDTwirlDistortionControlState_Inactive = 0,
	kKDTwirlDistortionControlState_AngleControlActive,
	kKDTwirlDistortionControlState_RadiusControlActive,
	kKDTwirlDistortionControlState_PositionControlActive

}KDTwirlDistortionControlState;

The view subclass declares an ivar that it will use to keep track of its current state:

// state
KDTwirlDistortionControlState		mControlState;

This is all the data we need for our control. Now we will use NSView’s drawing and NSResponder’s event handler methods to implement the control.

Drawing

All we need to do to draw in an NSView subclass is to override the following NSView method:

- (void)drawRect:(NSRect)theRect;

To keep things organized, KDTwirlDistortionControl uses separate drawing methods for each part of the control. It also creates a separate method to lay out the rectangles in the view. It declares them as private methods in a category in the .m file:

@interface KDTwirlDistortionControl (Private)
- (void)drawPositionControlInContext:(CGContextRef)theContext;
- (void)drawAngleControlInContext:(CGContextRef)theContext;
- (void)drawRadiusControlInContext:(CGContextRef)theContext;
- (void)layoutControls;
@end

KDTwirlDistortionControl’s drawRect method looks like this:

- (void)drawRect:(NSRect)theRect
{
 CGContextRef aCGContextRef = [[NSGraphicsContext currentContext] graphicsPort];

 // draw the background
 CGContextSetRGBFillColor(aCGContextRef, 0.8, 0.8, 0.8, 1.0);
 CGRect aCGBackgroundRect = *((CGRect*)&theRect);
 CGContextFillRect(aCGContextRef, aCGBackgroundRect);

 // layout the controls
 [self layoutControls];

 // draw the radius control rect
 [self drawRadiusControlInContext:aCGContextRef];

 // draw the position control rect
 [self drawPositionControlInContext:aCGContextRef];

 // draw the angle control rect
 [self drawAngleControlInContext:aCGContextRef];
}

If you look at the draw methods, you’ll notice that before they draw, they check the control state ivar to determine what color they will use to draw. This gives the user feedback about the state of the control. You might also notice that the control uses Quartz2D drawing commands. Your control could just as well use Cocoa’s NSBezierPath class to draw itself. It’s good to be familiar with both drawing APIs. There might be cases where you will notice the performance difference, so it’s nice to be able to fall back on Quartz for drawing straight into the view instead of using another object.

We can check number 1 off of our to-do list. The drawing is finished. One more.

Event Handling

NSView is a subclass of NSResponder. The mechanism of the view hierarchy ensures that any view you add to it will be placed in the application’s “Responder Chain”. The window will use the view hierarchy to deliver mouse events to the view that the user clicked on. Keyboard events will be delivered through the responder chain. It’s up to the view to implement the NSResponder event handlers that it is interested in.

Most mouse events will be sent automatically to the appropriate view, but there is one step that a view must take to receive keyboard events. The keyboard events go to the “firstResponder” first and then travel through the responder chain. If your view is to receive keyboard events it must accept first responder status. To do this, the view needs to override the NSResponder method:

- (BOOL)acceptsFirstResponder

to return YES. By default it returns NO.

Here’s a list of the most common mouse event handlers that a custom view can impelment:

- (void)mouseDown:(NSEvent*)theEvent;
- (void)mouseDragged:(NSEvent*)theEvent;
- (void)mouseUp:(NSEvent*)theEvent;
- (void)mouseMoved:(NSEvent*)theEvent;
- (void)mouseEntered:(NSEvent*)theEvent;
- (void)mouseExited:(NSEvent*)theEvent;

And common keyboard event handlers:

- (void)keyDown:(NSEvent*)theEvent;
- (void)keyUp:(NSEvent*)theEvent;

NSEvent

All of the event methods are going to pass us an NSEvent object that describes the event. If it’s a mouse event, we can ask the event for the location of the mouse point using the NSEvent method:

- (NSPoint)locationInWindow;

This is going to give us the location of the mouse event in the window (duh). We need to convert this point to a coordinate in our view’s internal coordinate system. We’ll use NSView’s method

- (NSPoint)convertPoint:(NSPoint)thePoint fromView:(NSView*)theView;

If the event is a keyboard event, we can ask the NSEvent object for a string of characters associated with the event with the following methods:

- (NSString *)charactersIgnoringModifiers;
- (NSString *)characters;

Another useful bit of information we can get from NSEvent is whether or not a modifier key is associated with the event. Use the method

- (unsigned int)modifierFlags

to get the current modifier flags, which you can find documented in the list of NSEvent’s constants. Check this against the modifier flag you are looking for. This bit of code is checking for the command key:

if(([theEvent modifierFlags] & NSCommandKeyMask) != 0)
{
 // do something special
}

Check the NSEvent documentation for more information on getting neat things like tablet data.

Ok, Let’s get back to our control.

Mouse Down

When our view receives a mouse down, we want to check to see if the mouse point is inside any of the rectangles. There’s a handy function we can use to check a point struct against a rectangle struct:

BOOL NSPointInRect(NSPoint thePoint, NSRect theRect);

KDTwirlDistortionControl’s mouse down method checks all of the rects against the mouse point. If it determines that a click has occured inside any of its rects, it sets the control state to indicate which rectangle has been hit, tells itself to redraw and returns. Here’s an excerpt:

// down on the position control rect
if(NSPointInRect(aMousePoint,mPositionControlRect))
{
 mControlState = kKDTwirlDistortionControlState_PositionControlActive;
 [self setNeedsDisplay:YES];
 return;
}

If none of the rectangles have been hit, the view centers the position rectangle around the mouse point. This lets user click the background to set the position - hint: this feature comes in handy if you happen to drag it to an unusable position, like I said there are problems in this design ; )

Mouse Dragged

This might be the most important method our class implements. The mouse drag is going to change the values of our active rectangle. It’s also going to call the setter methods of our control’s float values, which will automatically trigger notification of a value change to any class that is bound to the control.

The first thing the mouse dragged handler does is check the control state to determine which rectangle the user is adjusting. It will then perform the calculations it needs to make adjustments to both the rectangle and the associated filter value. It sets the value, tells itself to redraw and returns. Here’s an excerpt:

// dragging the position control rect
if(mControlState == kKDTwirlDistortionControlState_PositionControlActive)
{
 // center the rect around the mouse point
 float aNewXPosition = aMousePoint.x-mPositionControlRect.size.width*.5;
 float aNewYPosition = aMousePoint.y-mPositionControlRect.size.height*.5;
 mPositionControlRect.origin = NSMakePoint(aNewXPosition, aNewYPosition);

 [self setPositionX:aNewXPosition];
 [self setPositionY:aNewYPosition];
 [self setNeedsDisplay:YES];

 return;
}

Mouse Up

The mouse up simply sets the control state to be inactive and tells itself to redraw.

Key Down and Key Up

The key down event handler looks for arrow key presses. When an arrow key is pressed, it adjusts the origin of the position rectangle a few points in the appropriate direction. It also sets the current control state so that the position rectangle is active and redraws. Here’s an excerpt from the method. It uses a switch statement to find the arrow keys:

case NSUpArrowFunctionKey:
   mControlState = kKDTwirlDistortionControlState_PositionControlActive;
   mPositionControlRect.origin.y = mPositionControlRect.origin.y+5;
   [self setPositionY:mPositionControlRect.origin.y];
   [self setNeedsDisplay:YES];
break;

The key up method also checks for arrow keys. If the event is from one of the arrow keys, it resets the control state to inactive and redraws.

Event handling was the last thing on our to-do list. Scratch it off, we’re done. We have a control.

Hi Asimo! Nice Hair-do!

Picture 21.png

The KDTwirlDistortionControl example is very specific to the kind of data it is controlling and it could be used with any Core Image filter that has the same attributes, despite its name.

In a future post, I will explore ways to abstract the design a little more to make the control more useful in more situations. This will be necessary for designing OpenGL controls and could be useful if you’re using CALayers as the basis for a control. I haven’t had a chance to work with CALayers, but I notice that they’re not descendants of NSResponder. Odd for such a view-like class.

Resources

The Example Project

Cocoa Event handling Guide

The Responder Chain

Cocoa drawing guide

Quartz2D Programming Guide

NSView - Working With the View Hierarchy

Cocoa Bindings

More advanced bindings examples: mmalc’s bindings examples, look at graphics bindings