Learn Objective-C on the Mac - Mark Dalrymple [139]
This sort of drawing code can make for some pretty long methods, but it’s often pretty straightforward, as in drawRect: shown previously. Not a single loop or if construct in the whole method! Note that unlike the Mr Smiley code in chapter 13, this drawing code doesn’t refer to our view’s bounds rect at all. Because we know that the bounds are always adjusted to contain a square from (0,0) to (1,1), we make use of simple hardcoded values to draw our graphics in and around this unit square.
Compile and run the app, and you should now see something like Figure 14-1. You should be able to edit the values in the text fields (any values between 0.0 and 1.0 work well), and see the control points and curve change accordingly.
Watching the Mouse
But entering numeric values into text fields isn’t the point of this exercise, we want to drag those control points around. As it turns out, this is pretty simple to do. NSView contains methods that are automatically called whenever a user interacts with the view by clicking, dragging, and so on. All we have to do is override a few methods, and we can respond to every click, drag, and release of the mouse.
Let’s start by adding a pair of BOOL instance variables to our view, to keep track of whether one of the control points is currently being dragged. Add the bold lines below to the interface declaration in CurveView.h:
Now let’s add some methods to the @implementation section of CurveView.m, in order to start intercepting the mouse activity we want to watch. The first method, mouseDown:, will be called whenever a user clicks in our view:
In the mouseDown: method, we first ask the window for the current mouse location, then use a built-in NSView method to convert the coordinates from the window’s coordinate system to our own. This means that a click in the upper right-hand corner of our unit square, for instance, which starts off being the number of horizontal and vertical pixels from the window’s lower left-hand corner, will end up being converted to (1,1) or somewhere nearby. Then we do a pair of tests, to see if one of our control points is being clicked on. This test is done using the following function, which you should add to the top of CurveEdit.m, somewhere above the @implementation section:
The pointsWithinDistance function makes use of the Pythagorean formula to determine whether the distance between the two points (in our case, the center of a control point, and the location of the mouse) is less than the distance we pass in (the control point radius). Using this, we are able to check to see whether the user clicked on a control point, and if so we set the corresponding flag (draggingCp1 or draggingCp2) to YES.
The next method to implement is mouseDragged:, which is called every time the mouse is moved while the button is held down. Note that this method is called in every view the mouse is dragged over. It’s always called in the view where the original mouse click occurred; in a sense, whichever view receives the click “owns” all subsequent dragging. In this method, we once again grab the mouse location from the event, transform it into our view’s own coordinate system, and then update the coordinates for the control point that’s currently being dragged. If none of them are currently being dragged, then nothing happens.
The final method we need for dealing with the mouse is mouseUp:, which lets us handle the release of the button. Like mouseDragged:, mouseUp: is always called on the view which originated the drag, which means that after the user clicks in our view, no matter where the user lets go of the mouse button, we will receive this message. All we do here is simply set the flags to indicate that nothing is being dragged.
With all that in place, Build & Run your app. You should now find that you can drag the controls around, with the curve following every move, and the numbers in the text fields changing as you drag.
A Little Polish
That’s pretty cool, but as we’ve often noticed when toying around with