Online Book Reader

Home Category

Learn Objective-C on the Mac - Mark Dalrymple [138]

By Root 1014 0
fields for displaying each control point’s x and y values will be connected to our controller object with Cocoa Bindings, as will the CurveView itself. The model in this app will just be a set of instance variables in our controller class, but all the views could just as easily be bound to a real model object if we chose to use or create one.

Make a new NSView subclass called CurveView, and leave its implementation as-is for now. We’ll get back to it soon enough. Switch over to the app delegate class, and put the following in its .h and .m files:

As you can see, our controller class is very simple. All it does is declare properties for accessing the control points’ x and y values, establish some bindings on behalf of our curveView (because we can’t do those in Interface Builder), and set some default starting values for the control points.

Now open MainMenu.xib in Interface Builder. From the Library, drag out a Custom View, and use the Identity Inspector to set its class to CurveView. Resize it to about 240x240. Connect the app delegate’s curveView outlet to the new view. Because we’ve already set up this object’s bindings in code, the CurveView instance is now all set (as far as our nib is concerned).

Go back to the library and pull out an NSForm, dropping it below the CurveView. This is a handy control that combines multiple text entry fields, each with its own label, into a single view. This form will show us the x and y values for the first control point in a

Bezier curve. Change its two labels to “X1:” and “Y1:”, and create bindings for each of the cells in the form. For each, you want to bind its Value attribute to the CurveEdit_AppDelegate object, using the model key paths cp1X and cp1Y, respectively.

Duplicate the NSForm and place it to the right of the first one. This form will show us the values for the second control point, so rename its labels “X2:” and “Y2:”, and set up its bindings similar to the previous form’s, but using cp2X and cp2Y as the key paths instead.

Refer to Figure 14-1 to see what you’re shooting for, lay things out nicely, and resize the window to match the content you’ve added. Save your work, go back to Xcode, and Build & Run your project, just to make sure you haven’t made any errors at this point. The resulting app won’t do anything but let you edit four text fields, but we’re about to change that!

Bezier Plumbing


Let’s get started with the CurveView class by establishing some infrastructure. CurveView needs to keep track of two control points, which we’ll set up as four floats, each accessible through a property, just like we did for the controller class. We also want to use a technique similar to the one we used for MrSmiley in Chapter 13, so that the GUI scales to match whatever size it’s rendered at. This time, we’re going to set up fixed bounds so that we can always draw our curve in a square between (0,0) and (1,1) on the plane, leaving a little extra surrounding space, so we’ll add some code that sets the bounds to a square between (-0.1,-0.1) and (1.1,1.1), and maintain those bounds no matter how our view is resized. Take care of all that by adding the bold lines shown here:

Note that we don’t stop at just synthesizing the accessors for our properties. We actually implement setters for each of our properties, and in each we enforce a limited range on the input value, making it fit in the range from 0.0 to 1.0. We also mark the window as “dirty,” forcing the system to redraw it whenever a property changes.

Drawing a Curve


Now let’s move on to the fun part: drawing the curve itself. We’ll use preprocessor #defines to establish values for colors and line widths, making it easier to spot them and change them in order to tweak the appearance. Add these lines somewhere near the top of CurveView.m:

Now implement the drawControlPointAtX:y: and drawRect: methods as shown in the following example. The code for drawing the control points demonstrates the use of the NSGradient class, which can be used to fill the inside of a Bezier path instead of just a

Return Main Page Previous Page Next Page

®Online Book Reader