Dealing with drawRect multiple calls - iphone

I discovered that my drawRect is being called more than once. Unfortunately this had the unfortunate side-effect of double-drawing everything because all my subviews are drawn in drawRect (I'm s strict atheist w.r.t. Interface Builder).
What is the best way to deal with a multiple calls to drawrect? A flag to check if it's being called again? Or clear the whole view and redraw from scratch (as I have done?)

What do you mean by "your subviews are drawn in drawRect"? If you mean that you're putting calls to -addSubview: in your drawRect, don't. Move those to a more appropriate place that gets called only when it needs to (probably the -initWithFrame: method for your view if the subviews are always there), and use drawRect only to do custom drawing.
Strictly speaking, being called multiple times is the entire point of -drawRect. It's called to update small parts of your view when needed. It's actually called quite often if you're updating your view (moving it around, updating the superview, etc.), so it should be as simple and fast as possible to avoid performance problems.

When UIKit calls drawRect:, the graphics context set up to draw into should already be cleared for you (unless you have set the clearsContextBeforeDrawing property to NO). Are you perhaps calling drawRect: manually instead of calling setNeedsDisplay/setNeedsDisplayInRect:?
Also, each view is responsible for drawing only its own content and not the content of its subviews.

Related

Losing all drawing when app comes in foreground

I am drawing with multiple colors by dragging fingers on the iPhone app but when I minimize the app and then maximizes, I am losing all my drawing as it calls the drawrect method. I don't know from where this method is being called but I am not able to see any solution to this problem. Mainly it is happening when I am running many apps on the device. I think the iOS deletes all the data in the buffer in order to prevent low performance of the iPhone. What is the solution to this issue? And why it is losing the drawing? Please help! I didn't find any solution of this problem.
Because your view is unloaded in the background , and when you are back to the foreground , the view will be recreat , so it will redraw.
When you enter background , if the device memory is low , Apple will unload the background app`s view(even close your app) , to enlarge the memory for the current running app. Your view is unloaded , when you be back to foreground , the view will recreat by the "data" , so make sure do not hold the "data" in the view.
And maybe in IOS6 , Apple modify the mechanism
I am losing all my drawing as it calls the drawrect method.
From your Question, it seems as if you are not drawing in your drawRect method,
drawrect method is called every time your view is updated / loaded. So when you enter from background drawrect method is called. If you are not handling in your drawing mechanism in drawrect method, you loose all data.
Apple States that
The default implementation of this method does nothing. Subclasses
that use native drawing technologies (such as Core Graphics and UIKit)
to draw their view’s content should override this method and implement
their drawing code there. You do not need to override this method if
your view sets its content in other ways. For example, you do not need
to override this method if your view just displays a background color
or if your view sets its content directly using the underlying layer
object. Similarly, you should not override this method if your view
uses OpenGL ES to do its drawing.
If you are handling it other ways then you need to handle that after your view is drawn on in viewwillAppear.
Else there can be memory reason, since if you get memory warning, ViewDidLoad is called where you generally initialise data.
The framework will call drawRect as needed, it is your responsibilty to draw the view when it is called. You should be storing whatever state you need from the users actions in order to redraw the view whenever drawRect is called.

Iphone - Drawing into a view : philosophy and interactions

I've understood that I need to subclass a UIView t be able to draw inside it.
The thing I don't understand yet, is the philosophy of the way i must be done...
Let's say I have a view controller, and depending on context, I may want to draw a line into one of the subviews it manages, or a circle, or a rect, or a processed graphic. Or lets say two points that are moving inside a view into a defined rect and that display a bigger point when they are close.
How may I subclass and define the subview to make it able to do this only into its drawRect method ?
How does the controller, that manages more than this simple UIView (let's imagine you have a view controller that manages a view inside which there are many other view, and you want to make some drawings in two of them), and that knows what is needed to be drawn into the correct view (it's a controller, isn't it ?), may interact with the views ? And when the drawing is done, how may the views interact with the controller ?
I've read many doc about drawings (apple, web, forums, tutorials, ...), but I still can't touch the philosophy of the way this must be done.
it's very simple. Make a new class, OliverView, which is a UIView. (ie, it is a subclass of UIView.) In that view, make it draw stuff in a fancy way, inside drawRect.
Now make a UIViewController, called OliverVC. In storyboard put an OliverView inside OliverVC. (beginner explanation of how to do that).
In the OliverView, have properties "hours", "minutes", "seconds".
Now, in OliverView - in the drawRect - have a fancy way to display those values. (Pie chart, glowing letters, animation - whatever you want.)
Now, up in OliverVC, do some calculations to determine the time in Zimbabwe, for example.
Once you want a time displayed, simply set those properties in OliverView - - and you are done.
Your colleague could be programming the OliverView. You need know nothing about how she is going to display the time. Conversely, your colleague need know nothing about your calculations in OliverVC..
So, it's simpleL One part has the job of displaying the data. One part has the job of coming up with the data (doing whatever sort of calculation is relevant in the app).
It's the only architecture possible in a "real time" screen device where the views can and do change at any time.
In answer to your question below: you've forgotten that quite simply, if you have a button that would be a whole separate element. (Perhaps sitting "on top of" the OliverView.) So, it's easy!
The -drawRect method in your UIView subclass defines the onscreen appearance of the view. All drawing is done in -drawRect. Your UIViewController calls methods on its UIView to tell it to draw something differently or to perform some other action.
The UIViewController manages everything to do with the view that is not inherently associated with the drawing of the content. Data associated with the view is often stored in the controller.

UIScrollView - how to draw content on demand?

I want to create a scroll view with a massive contentSize that will display content inside it. The content will be mostly text (a few small images will be drawn for content-boundaries).
So, think like a tiled map application, but tiling labels instead of tiled images.
Performance is critical in this application, so I think I should avoid using UILabels or any UIViews at all inside the scroll view.
How can I draw these labels as the user scrolls around? Some options I've considered:
override drawRect: in the scroll view and draw everything within the window - this seems like it would be really slow. Sometimes drawRect is called with only a 1 pixel difference.
Same, but keep track of which ones I've already drawn
Draw them from the "outside" somehow, like from the scroll view delegate - I can't figure out how to use [#"mystring" drawInRect:] outside of drawRect: (context problems)
Is there something I haven't thought of? I know the scroll views were designed to be able to handle this kind of problem, but I'm not sure what the designed way is. Thanks!
The standard way to achieve this in an iPhone application is to create a normal UIScrollView that is the size you want it to be, and to populate it either directly with a CATiledLayer if you're feeling brave or with a custom UIView subclass that uses a CATiledLayer and implements - (void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context.
CATiledLayer provides the same experience as Safari — it's good for views that are much larger than the screen and which are expensive to render, but which you don't want to ruin the fluidity of the user experience. It'll request tiles as and when it needs them on a background thread, then fade them in (according to a fade of any length, so you can cause them to appear instantly if you desire) when they're ready. If your program really can always keep up with the scrolling and you've requested an instant appearance then there'll be no evidence that there's an asynchronous step involved.
An example people tend to point to is this one, but if you're new to implementing your own UIView subclasses then it might be worth seeing e.g. this tutorial. Then look into the + layerClass property on UIView and the various properties of CATiledLayer (which I think you'll also possibly need to subclass since + fadeDuration is a class method).

When is 'drawRect' called?

I have some custom drawing code in drawRect which also performs some calculation of sizes.
When is the earliest I can be sure that this code has been loaded, e.g. if I want to modify it's containers size accordingly?
-[NSView viewWillDraw] is a reasonable place for last minute layout.
I have some custom drawing code in drawRect which also performs some calculation of sizes.
When is the earliest I can be sure that this code has been loaded, e.g. if I want to modify it's containers size accordingly?
An object can't exist until its class is fully loaded. If you have an instance, the class that it's an instance of is completely loaded, because you wouldn't have an instance of it if it wasn't.
As for when it's called: It's called when you need to draw. This normally happens as part of the event loop, if anything has marked the view as needing display. It is possible to directly tell an NSView to display, but, as far as I can tell, this is not possible for UIViews.
So, if you need to do something to it before it gets told to, either do it immediately after creating it or, if you're about to set the view as needing display, do it before you do that.
Just before the view is displayed or when you call
[aView setNeedsDisplay];
I just created my first customView app. This was one of my questions. my drawRect method was called once upon creating my window (or recreating). And millions of time when resizing my window.

Cocoa-Touch: When implementing drawRect, how do you redraw the transparent background?

I'm implementing a UIView's (UITableViewCell to be more exact) drawRect method.
My view has a transparent background, so when something is changed in the view, the older drawn version still remains there. So if on the first drawRect draw an "A", then a "B" on the same point, I get both of them drawn on top of each other.
How can I tell the framework to redraw the background?
(which I suppose it doesn't do because is not always needed, but in this case it badly is)
I guess what I need is the equivalent of win32's invalidateRect, however I went thru UIViews members and didn't find anything.
Btw, i've tried setNeedsDisplay, it didn't help.
I think I've used CGContextClearRect(CGContextRef context, CGRect rect) for this before.
you should set clearsContextBeforeDrawing
I wonder if, since it is your own view that is retaining the drawing, whether you yourself should erase the rect being passed in?
What happens if your background is not transparent?
It's possible having come from the Windows world that you're thinking about the problem wrong. Having also come from the Windows world, I've done the same many times myself. Why do you need to override drawRect? Whenever you do that, you are responsible to take care of everything. Is it possible to do what you are wanting another way? What are you drawing in drawRect? Can you just add sub views or sub layers to your cell instead?
BTW, are you calling [super drawRect:rect] at the beginning of your drawRect override?
I'm no longer reusing cells.
This is working for me because I always have < 20 cells in my table. I guess this is not a solution for everyone, but it's the way I'm planning to go.
Rather than have the framework create and reuse 10ish cells, I'm creating my 20 while fetching the data for them, and later at display time things go smoother because they don't need to be re-customized every time.