Introduction to Cocoa Graphics
Pages: 1, 2, 3
The brush
Our brush in Cocoa is the AppKit class NSBezierPath (also know as bezier path). Like NSView, NSBezierPath is a class jam-packed with all sorts of neat behaviors. Unfortunately, we'll only scratch the surface of today, but we will hit much more of it later on.
Bezier paths are objects that define shapes as a series line and curve segments. For example, a rectangle could be represented as a bezier path with four straight-line segments. With bezier paths, you can make shapes with arbitrary complexity. The procedure for drawing a shape to a view is to first create a path, and then either fill the path with a color, or draw it as an outline. We'll see examples of both shortly.
All drawing code that we write will be done in the method drawRect:. For those of you with experience with Java 2D graphics, drawRect: is analogous to the method paint(). The argument of this method is the rectangle in which the drawing is to be done.
Normally we will never call drawRect: directly. Rather, whenever something changes with our view that requires the contents of the view to be redrawn, that process will tell the view that it needs to be redrawn. Today we won't have any need or reason to manually tell the view to redraw itself, but that will come in a later column.
What we'll work on today is simply how to draw rectangles and ellipses, which are actually almost identical in code. The two methods we will concern ourselves are the convenience constructors +bezierPathWithRect: and +bezierPathWithOvalInRect:. Let's dive right into our first example of drawing and see how it works.
In the drawRect: method, we will add the following code:
- (void)drawRect:(NSRect)rect
{
NSRect r = NSMakeRect(10, 10, 50, 60);
NSBezierPath *bp = [NSBezierPath bezierPathWithRect:r];
NSColor *color = [NSColor blueColor];
[color set];
[bp fill];
}
In the first line, we defined the rectangle which would soon become our bezier path. Following that, we created a bezier path object that simply follows the perimeter of the rectangle. In the next line, we created a color that we would fill the rectangle with, and following that, made the current color of subsequent drawing using the -set method of NSColor. The message [color set] tells the graphics engine in the background that any drawing operations will be done with the color blue (as indicated by the object color). Finally, we send a fill message to the bezier path that fills the path with the color we set. This snippet of code should produce the following output:

Alternatively, we could have made the last line [bp stroke], which would draw a line along the path, as shown below:

Now, if we changed the bezier path creation line of code (the second line) to the following:
NSBezierPath *bp = [NSBezierPath bexierPathWithOvalInRect:r];
The shape drawn to the screen would be an ellipse that fits into the specified rectangle, as is shown in the image below:

Another way we can draw a rectangle is using the Foundation function NSRectFill, which takes a rectangle as its argument. The color of the fill operation performed by this function is the current color of the graphics environment. For example, we could change the first example to circumvent the use of NSBezierPath by using the following code:
- (void)drawRect:(NSRect)rect
{
NSRect r = NSMakeRect(10, 10, 50, 60);
NSColor *color = [NSColor blueColor];
[color set];
NSRectFill(r);
}
NSRectFill is also a convenient way of coloring the background of a view. We can do this by setting a color as the background color, and then passing the drawRect argument variable rect to NSRectFill. Reverting back to our original example, we can make the background black in the following way:
- (void)drawRect:(NSRect)rect
{
NSRect r;
NSBezierPath *bp;
[[NSColor blackColor] set];
NSRectFill(rect);
r = NSMakeRect(10, 10, 50, 60);
bp = [NSBezierPath bezierPathWithRect:r];
[[NSColor blueColor] set];
[bp fill];
}
In this example, I eliminated the color variable by sending a set message directly to the object returned by [NSColor blueColor]. This code produces the output shown below.

This method of coloring the background of the view works because when the method drawRect: is automatically invoked by the view, the boundaries of the view are passed as the rect argument. Another note about the way drawing works -- the background painting code must come before any other drawing commands. This is because objects that are drawn to the view are drawn over anything that was previously there.
End
So that's essentially how you can create shapes using the Cocoa drawing classes! It's pretty simple -- there's not too much to it. In the next column, we will continue with our graphics and drawing discussion by learning how to make more complex paths. Until then, have fun with what you've learned today, experiment, and play around with your ideas. I leave you with a small app that draws a collection of randomly generated rectangles and ellipses to the screen, as is shown in the image below. It's a simple app that doesn't use much else than what we've seen today in terms of drawing code. It can be downloaded here See you next time!
Michael Beam is a software engineer in the energy industry specializing in seismic application development on Linux with C++ and Qt. He lives in Houston, Texas with his wife and son.
Read more Programming With Cocoa columns.
Return to the Mac DevCenter.
You must be logged in to the O'Reilly Network to post a talkback.
Showing messages 1 through 7 of 7.
-
help tutorials in project builder navigation on NSView
2001-10-24 09:04:06 psheldon [Reply | View]
-
help tutorials in project builder navigation on NSView
2001-10-26 17:07:54 psheldon [Reply | View]
I have thought that, were my only objective tutorials, these are listed in an accessible place in either help viewer or project builder's viewer.
Having found a part of a tutorial in help viewer, I can click on previous pages until I get to the start and title page of the tutorial, then click on the path to see where the table of contents containing that title was and then hypothesize which of the titles in that toc would get me back to the beginning of the tutorial, check it out and back arrow until I hit on it. Then I could find it in project builder to have ongoing help in a project.
It might still be neat to save bookmarks or shortcuts found with help viewer via some sort of interapplication communication or interapplication drag and drop awareness . I am suspecting this to be a bit more complicated than merely my wish because the lists constructed by help viewer might be temporary ram urls and someone would have to show drag enabled or disabled at times . Perhaps such interface complication isn't needed . Sometimes forcing a single aps metaphor might force users to explore that metaphor .
-
NSRectFillUsingOperation
2001-11-03 13:31:34 sork [Reply | View]
I have tried this function with NSCompositeXOR but it doesn't seem to work as expected, it just draws a black rectangle.
-
Custom View Attributes
2004-12-09 09:58:00 matt_scase [Reply | View]
I noticed I'm posting this long after the other comments. I'm using interface builder 2.4.2 and the CustomView is set to CocoaDrawing under the "Custom Class" menu in "show info" rather than the "Attributes" menu.
-
Coordinates
2006-06-29 10:11:16 hmalissa [Reply | View]
I've been working through this article and I think I can figure it out quite well. But I don't understand this coordinate system, for example the numbers given as arguments in the NSMakeRect command. How do I know how large my CustomView is? If I want my rectangle to scale with window size, how do I do this? Is there a way to determine the width and height of the "drawing area" in the CustomView?
-
bexierPathWithOvalInRect is misspelled causes crash
2010-04-11 08:22:44 mbs402 [Reply | View]
Now, if we changed the bezier path creation line of code (the second line) to the following:
NSBezierPath *bp = [NSBezierPath bexierPathWithOvalInRect:r];
This causes the demo to crash, it is misspelled: bexierPathWithOvalInRect
bezier does not have an 'x', this fixes the problem : bezierPathWithOvalInRect






In project builder, find definition (of selected text, such as NSView) in find menu finds a list of stuff, one of which contains a book icon. Clicking on this icon gets help on the class. But there are other sorts of items in developer help, evidentally, tutorials!
I looked up NSView in the help viewer ap with finder text entry field and found a tutorial near the top of the list of hyperlinks, a tutorial, mind you, itself with hyperlinks. That is, I found "Creating a subclass of NSView" was part of a long tutorial on Java Interface to Cocoa with mention of NSView.
In project builder's help, I don't see that finder text entry field and don't know a gesture to rapidly navigate to the tutorial once I found it with the help viewer.
All help files are html maybe in the contents of ?-icon files. If you drag such a file, an Apple Store guy called them "shortcut files", to the help viewer icon on the dock or in a finder window, you go instantly there. So, it's called a shortcut (something you don't have to navigate through various hyperlinks to get to, once you have found your way there once).
I don't know a similar gesture for the help in project builder.
Maybe someone could make an applescript to get help viewer and project help on the same page by looking at the referenced html address of help viewer or some way of saving shortcuts from pages found in help viewer ap and using those url's for project builder help.
Since tutorials have pastes of code such a gesture in help from project builder would be nice.
I don't know that I want to read through humugous bunch of tutorials amongst other things that I study (this column might be enough cocoa), but I think it would be nice to have those gestures to fall back to when I need to and when I have my own project "catch on spiritual fire".