Basic question about drawing to a view

Discussion in 'Mac Programming' started by bahlquist, Oct 25, 2010.

  1. macrumors member

    Joined:
    Oct 6, 2010
    #1
    I am having trouble drawing to a view more than once. I want the following program to fill my view with white and then draw a black path from a given point (coordinates (100,100)) to the coordinates of a mouseDown event.

    I have set up a custom view in IB that uses my class BADraw which is as follows:

    BADraw.h:

    Code:
    #import <Cocoa/Cocoa.h>
    @interface BADraw : NSView {}
    @end


    BADraw.m:

    Code:
    #import "BADraw.h"
    
    @implementation BADraw
    -(void)mouseDown:(NSEvent*)event{
    	NSPoint p;
    	p.x = 100;
    	p.y = 100;
    	NSBezierPath* path = [[NSBezierPath alloc] init];
    	[path moveToPoint:p];
    	[path setLineWidth:1.0];
    	[[NSColor blackColor] set];
    	[path lineToPoint:[event locationInWindow]];
    	[path stroke];
            
            //I tried using the following methods to get the path to display:
    	[self lockFocus];
    	[self setNeedsDisplay:YES];
    	[self display];
    	[self unlockFocus];
    
    	[path release];
    }
    
    -(void)drawRect:(NSRect)rect{
    	[[NSColor whiteColor] set];
    	[NSBezierPath fillRect:[self bounds]];
    }
    
    @end
    IB information:
    I dragged a custom view from the IB palette into my window and set it to use BADraw.

    Output:
    The output is a white rectangle filling the window. No paths appear when I click.

    I'm obviously missing something basic here, but I don't know what. What do I need to do?
     
  2. Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #2
    Several things.

    1. To get the mouse location relative to your view's bounds, use:
    Code:
    [self convertPoint:[event locationInWindow] fromView:nil]

    2. All drawing for your view should go in the drawRect: method.

    3. lockFocus/unlockFocus/display are really only used on an NSView when you're drawing into it from a background thread, and you probably will never be doing that :)

    4. To get the view to redraw, just use setNeedsDisplay:YES

    So, here's how it should look:
    Code:
    #import <Cocoa/Cocoa.h>
    @interface BADraw : NSView
    {
        NSPoint mouseLoc;
    }
    @end
    
    #import "BADraw.h"
    
    @implementation BADraw
    
    - (void)awakeFromNib
    {
        mouseLoc = NSMakePoint(-1, -1);
    }
    
    - (void)mouseDown:(NSEvent*)event
    {
        mouseLoc = [self convertPoint:[event locationInWindow] fromView:nil];
        [self setNeedsDisplay:YES];
    }
    
    - (void)drawRect:(NSRect)rect
    {
        [[NSColor whiteColor] set];
        [NSBezierPath fillRect:[self bounds]];
    
        if (NSEqualPoints(mouseLoc, NSMakePoint(-1, -1)))
            return;
    
        NSPoint p;
        p.x = 100;
        p.y = 100;
        NSBezierPath *path = [NSBezierPath bezierPath];
        [path moveToPoint:p];
        [path setLineWidth:1.0];
        [path lineToPoint:mouseLoc];
        [[NSColor blackColor] set];
        [path stroke];
    }
    
    @end
     
  3. thread starter macrumors member

    Joined:
    Oct 6, 2010
    #3
    Thanks! If I wanted the previously drawn paths to remain in the view, how could I do that? I could just append to a global path variable each time I click and then redisplay this path using setNeedsDisplay:, but in the application I am actually making each click creates a path that contains upwards of 50,000 points and this will slow things down after a dozen clicks. Is there a way to draw the new path without redisplaying everything? (This question is relevant to the above code.)
     
  4. macrumors 68000

    Sydde

    Joined:
    Aug 17, 2009
    #4
    -setNeedsDisplayInRect: might reduce the amount of drawing needed, if you have such a method, and it works to minimize drawing appropriately.

    Your other option might be to draw the existing lines into a temporary NSImage object, with a bitmap image rep, which you could use as a sort of offscreen cache (I would start the cache image after you have a few hundred lines and add lines to it incrementally so that you still have a few dozen uncached lines to draw over it).
     

Share This Page