Making NSView show itself (noob question)

Discussion in 'Mac Programming' started by Amigalander, Sep 26, 2009.

  1. Amigalander macrumors regular

    Jan 13, 2008
    I was following the XCode tutorial by Apple which uses an NSView and the -(void)drawRect:(NSRect)dirtyRect method for all the action.

    I added a for loop to move the NSPoint around and re-draw Hello World! several times. However I noticed that the window doesn't appear until the drawing is done. Meaning if I loop a million times drawing Hello World! in different places, which may take 60 seconds, I won't see any output until it's completed 60 seconds later.

    How do I make the window show itself early on so I can see the drawing in progress?
  2. kpua macrumors 6502

    Jul 25, 2006
    You're missing a key concept of application programming. I'll try to explain briefly.

    Applications are driven by what is commonly called an event loop. The application sits and waits until a mouse, keyboard, or some other event happens, and then processes and reacts to that event. Once it's done processing the event, it starts waiting again for another.

    You're probably familiar with the beach ball, or spinning pizza of death, right? Well, what's happening there is the program is "hanging" or stuck processing some event and doesn't return to the "waiting for another event" state, so the program is unable to respond to any further events.

    In Cocoa, drawing is triggered by a special kind of event. It happens whenever a part of a view "invalidates" itself, or says it needs to be redisplayed. When this event occurs, the application calls -drawRect: on the views that need updating, and they draw themselves. However, the drawing doesn't immediately appear on screen. After all the views in a window draw themselves, all the drawing for that window is "flushed" to the screen and becomes visible. (This is done for performance's sake.) Then, the application starts waiting for the next event again.

    Attempting to draw "Hello World" a million times will definitely "block" the application from returning to the "waiting for next event" state, as well as the "flush" to the screen. That's why you don't see anything until the drawing is done.

    If you want to see one "Hello World" drawn at a time over a period of time, you might want a timer that, when fired, causes an event during which you can add more point to draw Hello World at to your view, and tell the view to redraw itself (setNeedsDisplay:YES).

    If you want to just experiment, go ahead and take a look at -[NSGraphicsContext flushGraphics], which should flush all pending drawing. (You may need -[NSWindow flushWindow] too.) Be aware though, this is not something that can reasonably be done in a real application. You'll need to use another technique.
  3. Amigalander thread starter macrumors regular

    Jan 13, 2008
    Thank you for that excellent reply. If I've understood the concepts you explained, then... the correct way to see Hello World drawn repeatedly is to loop somewhere outside of the drawRect and have drawRect be called repeatedly either manually (setNeedsDisplay:YES) or automatically? How does the NSview know when to flush the graphics if not done manually?
  4. kpua macrumors 6502

    Jul 25, 2006
    Sort of. Let the event loop do the looping for you though. Use NSTimer to add a point at which to render the Hello World every .05 seconds or whatever and setNeedsDisplay:. The setNeedsDisplay: will cause a -drawRect: in a very near future "event". The graphics will always be flushed some time after NSView calls drawRect:, so don't worry about that too much.

Share This Page