While in an NSView subclass, how do you draw to a view created on-the-fly, and outside of the drawRect: method of the original subclass? Is this even possible? I’m trying to avoid subclassing at all costs.
The code below is called whenever I need to generate a background (whenever a pref is changed). The drawing is then stored as an image for the drawRect: method to draw with dissolveToPoint:fraction: . http://goo.gl/OeSCu
// Other drawing code here…
// I’m storing all of the previous drawing inside an image imageData = [self dataWithPDFInsideRect: [self bounds]]; mainBgImage = [[NSImage alloc] initWithData: imageData];
[imageData release]; }
If you want to draw into a view outside of its -drawRect: call, you need to call -lockFocus on the view beforehand and -unlockFocus afterwards. – Bo
Isn’t it bad practice to draw to a view outside of its drawRect: method? Why not set some variable or other to tell it what to draw, and then call setNeedsDisplay:?
What reason do you have to avoid subclassing at all costs?
No reason really, its just that I think it’d be much more neat.
Going against the proper way of doing things is not neat.
I think he means that the code would be neater, and more manageable.
My first custom view did all its drawing outside of drawRect:, because I didn’t understand how views were supposed to work. It caused some problems because it wouldn’t keep the image after being minimized, or in a few other circumstances which I forget. To avoid these problems, it is necessary to do drawing in drawRect:, and if the code to draw is in there, there’s no point in duplicating it somewhere else. I’ve worked on other software in my job where some drawing is done where it should be and some isn’t, and it’s a pain when something needs to be changed to draw a different way and I fix it in the normal drawing routine but there is still some code elsewhere which is drawing it the wrong way. It’s hard to find the drawing code which is in the wrong place.
To understand if drawing outside of drawRect: is right or wrong, it helps to understand what drawRect: does for you. I know what you’re thinking: “That’s easy! AppKit calls my view’s “drawRect:” method when it’s time for the view to draw.” Uh huh. When is it time for your view to draw?
It’s not too hard to figure out when the window appears on screen that first time – or at least you can figure out when you think it’s going to appear. And you know when your own underlying state changed. But the other cases are just too far out of your control to get a handle on. You have no choice but to trust the system.
And not so fast! Your answer was only half-right. The other thing drawRect: does for you is help coalesce redundant copy requests. Say you have a view that displays 3 different pieces of data: a file name, a time stamp, and an icon. You want to make sure the view re-draws when any one of those things change. But what happens when all three of those things change at once? Do you want to redraw your view three times? No, you want to redraw it just once. Are you going to redraw it three times anyway? Depends on how smart you are I guess. Me? I take the easy way out and just get drawRect: to do all this for me.
So here is where you say “Well if it does so much for me, how am I supposed to get my stuff to draw?” And I reply “SRTD: Simply Read The Documentation.” But don’t get mad – the best way to get good at how these Cocoa objects actually work is to pour over the docs and let it all sink in. In the meantime let’s see how drawRect: actually works.
The idea here is that NSView is responsible for drawing itself. Thus, the drawRect: method, see above. But NSView is not responsible for knowing when to draw, since that decision is often delegated to model-level or controller-level code (what does this mean? Review ModelViewController.). NSView provides several ways to trigger a redraw (How many? SRTD!) but one of the most common is:
This method marks a view as needing display (nor not) and schedules a redraw to happen at the end of this pass through the event loop (or not). AppKit will make sure the right number of subviews (and superviews) get redrawn as well to make sure your view’s area looks exactly right. (How exactly? SRTD!). Since the re-draw happens at the end of this pass through the event loop, multiple redraw requests are coalesced into one. Consider the following pseudo code: