您的位置:首页 > 其它

Cocoa Programming for Mac OS X 第十八章(Images and Mouse Events)摘录

2011-08-22 16:56 246 查看

Chapter 18. Images and Mouse Events

NSResponder

NSView inherits from NSResponder.All the event-handling methods are declared in NSResponder.We discuss keyboard events in the next chapter. For now, we are interested only in mouse events. NSResponder declaresthese methods:
- (void)mouseDown:(NSEvent *)theEvent;
- (void)rightMouseDown:(NSEvent *)theEvent;
- (void)otherMouseDown:(NSEvent *)theEvent;
- (void)mouseUp:(NSEvent *)theEvent;
- (void)rightMouseUp:(NSEvent *)theEvent;
- (void)otherMouseUp:(NSEvent *)theEvent;
- (void)mouseDragged:(NSEvent *)theEvent;
- (void)rightMouseDragged:(NSEvent *)theEvent;
- (void)otherMouseDragged:(NSEvent *)theEvent;
- (void)scrollWheel:(NSEvent *)theEvent;
Note that the argument is always an NSEvent object.

NSEvent

An event object has all the information about what the user did to trigger the event. When you are dealing with mouse events, you might be interested in the following methods:
- (NSPoint)locationInWindow
Thismethod returns the location of the mouse event.
- (unsigned int)modifierFlags
The integer tells you which modifier keys the user is holding down on the keyboard. This enables the programmer to tell a Control-click from a Shift-click, for example. The code wouldlook like this:
- (void)mouseDown:(NSEvent *)e{unsigned int flags;flags = [e modifierFlags];if (flags & NSControlKeyMask) {...handle control click...}if (flags & NSShiftKeyMask) {...handle shift click...}}
Here are the constants that you commonly AND (&) against the modifier flags:
NSShiftKeyMask
NSControlKeyMask
NSAlternateKeyMask
   NSCommandKeyMask
- (NSTimeInterval)timestamp
This method gives the time interval in seconds between the time the machine booted and the time of the event. NSTimeInterval is a double.
- (NSWindow *)window
This method returns the window associated with the event.
- (int)clickCount
Was it a single-, double-, or triple-click?
- (float)pressure
If the user is using an input device that gives pressure (a tablet, for example), this method returns the pressure. It is between 0 and 1.
- (float)deltaX;- (float)deltaY;- (float)deltaZ;
These methods give the change in the position of the mouse or scroll wheel.

Getting Mouse Events

Toget mouse events, you need to override the mouse event methods in StretchView.m:
#pragma mark Events- (void)mouseDown:(NSEvent *)event{
NSLog(@"mouseDown: %d", [event clickCount]);
}- (void)mouseDragged:(NSEvent *)event{
NSPoint p = [event locationInWindow];
NSLog(@"mouseDragged:%@", NSStringFromPoint(p));
}- (void)mouseUp:(NSEvent *)event{
NSLog(@"mouseUp:");
}
Build and run your application. Try double-clicking, and check the click count. Note that the first click is sent and then the second click. The first click has a click count of 1; thesecond click has a click count of 2.Note the use of #pragma mark. At the top of any Xcode editing window there is a pop-upthat enables you to jump to any of the declarations and definitions in the file. #pragma mark putsa label into that pop-up. Stylish programmers (like you, dear reader) use it to group their methods.

Composite an Image onto Your View

You will also need to change StretchView sothat it uses the opacity and image.First, declare variables and methods in your StretchView.h file:
#import <Cocoa/Cocoa.h>@interface StretchView : NSView{
NSBezierPath *path;
NSImage *image;
float opacity;
}
@property (readwrite) float opacity;
- (void)setImage:(NSImage *)newImage;
- (NSPoint)randomPoint;
@end
Now implement these methods in your StretchView.m file:
#pragma mark Accessors- (void)setImage:(NSImage *)newImage{
[newImage retain];
[image release];
image = newImage;
[self setNeedsDisplay:YES];
}- (float)opacity{
return opacity;
}
- (void)setOpacity:(float)x{
opacity = x;
[self setNeedsDisplay:YES];
}
Atthe end of each of the methods, you inform the view that it needs to redraw itself. Near the end of the initWithFramemethod,set opacity to be 1.0:
[path closePath];
opacity = 1.0;
return self;
}
Also in StretchView.m, you need to add compositing of the image to the drawRect: method:
- (void)drawRect:(NSRect)rect{
NSRect bounds = [self bounds];
[[NSColor greenColor] set];
[NSBezierPath fillRect:bounds];
[[NSColor whiteColor] set];
[path fill];
if (image) {
NSRect imageRect;
imageRect.origin = NSZeroPoint;
imageRect.size = [image size];
NSRect drawingRect = imageRect;
[image drawInRect:drawingRect
 fromRect:imageRect
operation:NSCompositeSourceOver
 fraction:opacity];
}
}
Note that the drawInRect:fromRect:operation:fraction: methodcomposites the image onto the view. The fraction determines the image's opacity.Youshould release the image in your dealloc method:
- (void)dealloc{[path release];[image release];[super dealloc];}
Build and run your application. You will find a few images in /Developer/Examples/AppKit/Sketch .When you open an image, it will appear in the lower-left corner of your StretchView object.

The View's Coordinate System

The final bit of fun comes from being able to choose the location and dimensions of the image, based on the user's dragging. The mouseDown willindicate one corner of the rectangle where the image will appear, and the mouseUp willindicate the opposite corner. The final application will look something like Figure 18.8.Figure 18.8. Completed ApplicationEach view has its own coordinate system. By default, (0, 0) is in the lower-left corner. This is consistent with PDF and PostScript. If you wish, you can change the coordinate system ofthe view. You can move the origin, change the scale, or rotate the coordinates. The window also has a coordinate system.Ifyou have two views, a and b, and you need to translate an NSPointp from b's coordinate system to a's coordinate system, itwould look like this:
NSPoint q = [a convertPoint:p fromView:b];
If b is nil, the point is converted from the window's coordinatesystem.Mouse events have their locations in the window's coordinate system, so you will nearly always have to convert the point to the local coordinate system. You are going to create variablesto hold onto the corners of the rectangle where the image will be drawn.Add these instance variables to StretchView.h:
NSPoint downPoint;NSPoint currentPoint;
The location of the mouseDown: will be downPoint and currentPoint willbe updated by mouseDragged: and mouseUp:.Edit the mouse event-handling methods to update downPoint and currentPoint:
- (void)mouseDown:(NSEvent *)event{NSPoint p = [event locationInWindow];downPoint = [self convertPoint:p fromView:nil];currentPoint = downPoint;[self setNeedsDisplay:YES];}- (void)mouseDragged:(NSEvent *)event{NSPoint p = [event locationInWindow];currentPoint = [self convertPoint:p fromView:nil];[self setNeedsDisplay:YES];}- (void)mouseUp:(NSEvent *)event{NSPoint p = [event locationInWindow];currentPoint = [self convertPoint:p fromView:nil];[self setNeedsDisplay:YES];}
Add a method to calculate the rectangle based on the two points:
- (NSRect)currentRect{float minX = MIN(downPoint.x, currentPoint.x);float maxX = MAX(downPoint.x, currentPoint.x);float minY = MIN(downPoint.y, currentPoint.y);float maxY = MAX(downPoint.y, currentPoint.y);return NSMakeRect(minX, minY, maxX-minX, maxY-minY);}
(Idon't know why, but many people mistype that last method. Look at yours once more before going on. If you get it wrong, the results are disappointing.)Declare the currentRect method in StretchView.h.So that the user will see something even if he or she has not dragged, initialize downPoint and currentPoint inthesetImage: method:
- (void)setImage:(NSImage *)newImage{[newImage retain];[image release];image = newImage;NSSize imageSize = [newImage size];downPoint = NSZeroPoint;currentPoint.x = downPoint.x + imageSize.width;currentPoint.y = downPoint.y + imageSize.height;[self setNeedsDisplay:YES];}
In the drawRect: method,composite the image inside the rectangle:
- (void)drawRect:(NSRect)rect{NSRect bounds = [self bounds];[[NSColor greenColor] set];[NSBezierPath fillRect:bounds];[[NSColor whiteColor] set];[path stroke];if (image) {NSRect imageRect;imageRect.origin = NSZeroPoint;imageRect.size = [image size];NSRect drawingRect = [self currentRect];[image drawInRect:drawingRectfromRect:imageRectoperation:NSCompositeSourceOverfraction:opacity];}}
Build and run your application. Note that the view doesn't scroll when you drag past the edge. It would be nice if the scroll view would move to allow users to see where they have draggedto, a technique known as autoscrolling.

Autoscrolling

Toadd autoscrolling to your application, you will send the message autoscroll: tothe clip view when the user drags. You will include the event as an argument. Open StretchView.m andadd the following line to the mouseDragged: method:
- (void)mouseDragged:(NSEvent *)event{NSPoint p = [event locationInWindow];currentPoint = [self convertPoint:p fromView:nil];[self autoscroll:event];[self setNeedsDisplay:YES];}
Build and run your application.Note that autoscrolling happens only as you drag. For smoother autoscrolling, most developers will create a timer that sends the view the autoscroll: methodperiodically while the user is dragging. Timers are discussed in Chapter 24.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: