drawRect: is not executed after setNeedsDisplay:YES

Moridin
Unregistered
 
Post: #1
Hi,

this is my first try at cocoa graphics programming. I have, however, already had a look at DotView and I managed to rewrite it, so that it got to become a kind of painting programm.
This time, I wanted to create a kind of snake game - from scratch. I have put a custom View in my window and created a class MyView for it. Below is the implementation of this class - all methods are also declared in the MyView.h.
The problem is: When I resize the window, drawRect is executed. This is okay. But I have a timer, that calls a method "redrawIt", which does a [self setNeedDisplay:YES]. This method redrawIt is correctly called, but drawRect is not. So i tried it with a button and an action "stepForward", which should make the view redraw itself just one time. It does not.

I have no idea what´s wrong. Please help me!!

P.S.: Of course my class is a subclass of NSView.

Code:
#import "MyView.h"

@implementation MyView

- (id)initWithFrame:(NSRect)frameRect
{
    [super initWithFrame:frameRect];
    snake.x = 5.0;
    snake.y = 5.0;
    radius = 1.0;
    color = [[NSColor redColor] retain];
    direction = 'R';
    return self;
}

- (BOOL)isOpaque {
    return YES;
}

- (void)drawRect:(NSRect)rect
{
    NSLog (@"Drawing...");
    // removed the code - I would be happy if I would just just see the log output
}

- (IBAction)stepForward:(id)sender{
    [self setNeedsDisplay:YES];    
    NSLog(@"Stepping...");
}

- (void)redrawIt{
    [self setNeedsDisplay:YES];
    NSLog (@"redrawing %@...",self);
}


- (IBAction)start:(id)sender
{
    NSLog (@"Started timer...");
    timer = [[NSTimer scheduledTimerWithTimeInterval:0.2
                                             target:self
                                            selector:@selector(redrawIt)
                                           userInfo:nil
                                            repeats:YES]retain];
}

- (IBAction)stop:(id)sender
{
    [timer invalidate];
    [timer release];
}

@end
Quote this message in a reply
Member
Posts: 370
Joined: 2002.04
Post: #2
You are, of course, returning control to the run loop every frame (this means not sitting in a while loop waiting for events - if you want the display to get shown, you have to return control back to AppKit), right? If you sit in a while loop your drawRect will never get called. That's the problem I had with one of my first few programs, at least...

Did you ever wonder why we had to run for shelter when the promise of a brave new world unfurled beneath the clear blue sky?
Quote this message in a reply
Moridin
Unregistered
 
Post: #3
Thought it would be something like that.
I rewrote the whole application, splitting it into a View and a Controller Class. Now drawRect is called.
Thanks.
Quote this message in a reply
Member
Posts: 196
Joined: 2003.10
Post: #4
Yeah, the MVC thing works well for beginning with views the first few times. I find that I understand more and more how it's supposed to work as I get more experienced.
Quote this message in a reply
kensuguro
Unregistered
 
Post: #5
so what was the problem with the posted code? Or the the "while loop problem" was in the code for the snake somewhere outside this snippet?

For me, I couldn't even get the original dotview to work, even by copying the code straight out of the book. The initial radius and color is set correctly. Places the ball at correct places on mousedown (setNeedsDisplay calls drawRect), but sliders or colorWell does nothing. (setNeedsDisplay doesn't call drawRect) Stranger yet, sliders set the radius to whatever float value correctly, but clicking the mouse resets radius to the initial value.

The code is same as the book, except for my funky comments... Any ideas? heh, and it's kinda cool lots of people go through the same excercises.. gives structure to share experience on. :-)

Code:
#import "DotView.h"

@implementation DotView

- (id)initWithFrame:(NSRect)frameRect
{
//    Call the original init before adding our own init code
    self = [super initWithFrame:frameRect];
    
//    Initializes the variables declared in .h file
    center.x = 50.0;
    center.y = 50.0;
    radius = 50.0;
    color = [[NSColor redColor] retain];

    return self;
}


- (void)awakeFromNib
{
//    Initialize interface elements
    [colorWell setColor: color];
    [slider setFloatValue: radius];
}

- (void)dealloc
{
    [color release];
    [super dealloc];
}

- (void)drawRect:(NSRect)rect
{
    NSRect dotRect;
    
//    Draw background
    [[NSColor whiteColor] set];
    NSRectFill([self bounds]);
    
//    set location of the dot
    dotRect.origin.x = center.x - radius;
    dotRect.origin.y = center.y - radius;
//    NSLog(@"%f", radius);
    
//    set size of the dot
    dotRect.size.width = 2 * radius;
    dotRect.size.height = 2 * radius;
    
//    set color and draw the dot
    NSLog(@"drawcenter%f", center);
    [color set];
    [[NSBezierPath bezierPathWithOvalInRect:dotRect] fill];
}

- (BOOL)isOpaque
{
    return YES;
}

- (void)mouseDown:(NSEvent *)event
{
    NSPoint eventLocation = [event locationInWindow];
    center = [self convertPoint:eventLocation fromView:nil];
    [self setNeedsDisplay:YES];
    NSLog(@"setcenter%f", center);
}

- (IBAction)setColor:(id)sender
{
    NSColor * newColor = [sender color];
    [newColor retain];
    [color release];
    color = newColor;
    [self setNeedsDisplay:YES];
}

- (IBAction)setRadius:(id)sender
{
    radius = [sender floatValue];
    [self setNeedsDisplay:YES];
}

@end
Quote this message in a reply
kensuguro
Unregistered
 
Post: #6
oops, figured it out. In the interface builder, I made an instance of dotview class and connected the slider and colorWell to that.. I figured I was supposed to make the connection to the dotview custom view. (read the book wrong.. it said "connect to dotview", didn't know which)
Quote this message in a reply
Member
Posts: 257
Joined: 2004.06
Post: #7
You know, I almost suggested that this morning but I wasn't sure it was the right comment and I didn't have enough time to really think about it so I said nothing. Smile

The brains and fingers behind Malarkey Software (plus caretaker of the world's two brattiest felines).
Quote this message in a reply
Moridin
Unregistered
 
Post: #8
kensuguro Wrote:so what was the problem with the posted code? Or the the "while loop problem" was in the code for the snake somewhere outside this snippet?

The problem was, that you have to use more than one object, if you want to draw something again and again in a loop, because drawRect will never be executed if the framework does not get it´s events. (Maybe someone can put this into better words)

In another application I recently wrote, I recursively work on all files in a directory. I use a while-loop in an "worker"-object and start and stop the action in another (controller-)object and output to the window is managed by a third (viewcontroller-)object. The problem was, that the window did not respond anymore as long as the while loop kept working on a directory. So the cancel button did not work, progress bar was not updated and I got the spinning ball all the time.
The solution was using a second thread. The worker-object runs in a second thread and the application is responding perfectly while the directory is parsed.
See TrivialThreads example about how to make your application multi-threaded.
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  drawRect not being called IckyThump 4 6,996 Aug 13, 2009 06:48 AM
Last Post: IckyThump
  how to call fathers setNeedsDisplay from child. iluzone 5 6,138 Dec 7, 2006 04:11 AM
Last Post: iluzone
  drawRect in NSOpenGLView help fakeOne 3 4,382 Sep 3, 2006 09:04 AM
Last Post: Xenos
  drawRect before awakeFromNib unknown 4 4,734 Aug 25, 2005 04:17 PM
Last Post: unknown
  drawRect without resetting the NSView jspoon 4 4,017 Apr 1, 2005 09:38 AM
Last Post: jspoon