views:

28

answers:

2

Hello,

Im working on a drag n' drop view and found some handlers for drag and drop actions on the web. I want to make it so it turns blue when the user drags a file over the drag and drop area and gray again when they exit the drag and drop area. The issues is its not updating when you drag your mouse over it or exit it. Heres some of the code:

- (void)drawRect:(NSRect)rect
{
   NSRect bounds = [self bounds];
   [[NSColor grayColor] set];
   [NSBezierPath fillRect:bounds];
}

- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender  {
    NSRect bounds = [self bounds];
    [[NSColor blueColor] set];
    [NSBezierPath fillRect:bounds];

    return NSDragOperationCopy;

}

- (void)draggingExited:(id <NSDraggingInfo>)sender {
    NSRect bounds = [self bounds];
    [[NSColor grayColor] set];
    [NSBezierPath fillRect:bounds];

}

Thanks for any help.

+1  A: 

Are you calling [yourView: setNeedsDisplay] anywhere?

This is how you let the drawing framework know it needs to message your UIView subclass with drawRect:, so you should do it whenever things have changed. In your case, this probably means when the mouse enters or exits the drop area.

Chris Cooper
I added [self setNeedsDisplay:YES]; after each of the drawings but no luck. Adding an nslog to it shows the methods are being triggered.
happyCoding25
+1  A: 

Drawing only works when a context (like a canvas for painting) is set up for you to draw into. When the framework calls -drawRect: it has set up a drawing context for you, so drawing commands like -[NSColor set] and -[NSBezierPath fillRect:] work as you expect.

Outside of -drawRect: there is usually no drawing context set up. Using drawing commands outside of -drawRect: is like waving a paintbrush in the air; there's no canvas, so no painting happens.

In 99.99% of cases, all view drawing should be kept within -drawRect: because NSView does a lot of work that you don't want to do to get the drawing context set up correctly and efficiently.

So, how do you change your view's drawing within your -draggingEntered: and -draggingExited: methods? By side effects.

You're doing the same thing in all three cases: 1) Setting a color and 2) Drawing a rectangle. The only difference is the color changes in each method. So, why not control which color you use in -drawRect: with an ivar, like so:

- (void)draggingEntered:(id <NSDraggingInfo>)sender {
    drawBlueColorIvar = YES;
    // ...
}

Then in -drawRect: you do this:

- (void)drawRect:(NSRect)rect {
    NSColor *color = drawBlueColorIvar ? [NSColor blueColor] : [NSColor grayColor];
    [color set];
    [NSBezierPath fillRect:rect];
}

(Notice I didn't use [self bounds]. It is more efficient to just draw into the "dirty" rect, when possible.)

Finally, you need some way to tell the framework that your view needs to redraw when drawBlueColorIvar changes. The framework won't draw anything unless it's told it needs to. As Chris Cooper said, you do this with [self setNeedsDisplay:YES]. This should go after any place you change drawBlueColorIvar.

kperryua
Im still somewhat new to cocoa and haven't heard of an ivar. When I try and compile I get error: 'drawBlueColorIvar' undeclared (first use in this function) which seems logical because I haven't declared it. How do I declare an ivar?
happyCoding25
Oh I got it I just added NSColor *drawBlueColorIvar; to the view header. Thanks for all the help!
happyCoding25