I'm experiencing that the drawRect method is called after the modalViewController has shown. Is it possible call the drawRect method before, so there won't be any delay?
Best regards
Sebastian Søndergaard
No, the drawRect method is called at the time of drawing. If your drawing code is doing too much however, you can pre-calculate it and put in in a CGLayerRef for example. Then your drawRect method can just paint the layer into the context.
You can do calculations on a background thread (carefully), then send it back to the main thread and call setNeedsDisplay on the view, if your main thread is choppy.
Related
I have an NSView (lets call it mainview) with a few subviews. One of these subviews has a time based (called via a timer) animation which is done in its drawRect method. Also I have some CIFilter effects applied to the mainview.
All works as expected under 10.6.x and 10.7.x ( I.e. I see my animation happening and filters applied).
The problem shows up only under 10.8 - the animation stops right after I apply a filter (using setContentFilters: and setWantsLayer:) to the mainview and resumes only after I remove a filter (using setContentFilters:nil). My subview's drawRect method doesn't even gets called when filters are applied...
I would really appreciate if someone could shed some light on the situation.
thank you.
Turns out a setNeedsDisplay: is not thread safe. I had a setNeedsDisplay: called for my subview not on the main thread. Calling in on my main thread fixed the issue.
If a Single View app is created, with a FooView that subclasses UIView, and do a
NSLog(#"hello");
in drawRect, then it is printed.
And if I create a subclass of CALayer called CoolLayer, and add this method to FooView.m:
+(Class) layerClass {
return [CoolLayer class];
}
and at the end of FooView.m's drawRect, do a
NSLog(#"layer's class is %#", self.layer.class);
then CoolLayer is printed. So now the view's underlaying layer is CoolLayer.
But when the following is added to CoolLayer.m:
-(void) display {
}
which is the method that is automatically called to redraw the layer (similar to drawRect), then no NSLog whatsoever was printed. It might be that the app went into an infinite loop. (even my touchesBegan that prints out NSLog messages is not printing). If a breakpoint is set at display, it will stop there once but when I continue the program, it will never arrive at display again. What is wrong with this and how can it be fixed?
The layer's display method will not be called again unless the layer is dirty, i.e. set to need a redisplay. This is usually a good thing, and is why you don't see the method being called more than once.
Also, the normal implementation of display will call the drawInContext: method. Since you override this in your subclass, the drawRect: method of the view is never called. You need to either replicate the standard behavior of CALayer, or call the superclass' display method in your own implementation.
That does not sound like an infinite loop. If you were in an infinite loop your app would freeze, and after a few seconds the springboard app would kill it for being unresponsive.
Call setNeedsDisplay or setNeedsDisplayInRect on your layer to make it "dirty" and require drawing again. Note that you don't want to call setNeedsDisplay any more than you have to, because it takes a lot of work to re-render the layer and push it's contents onto the screen. Only display when something has changed
You're not seeing an infinite loop here. If you were, as Duncan points out, you'd eventually crash either due to the watchdog timer or from an infinite recursion that would be immediately obvious in the stack trace you'd see in the debugger.
If you put an NSLog into your UIView's -drawRect: method, then override its default layer class with your own custom CALayer that does its own drawing, your UIView's -drawRect: would no longer be called. Drawing would now be handled by your custom backing layer.
As described in the "Providing CALayer Content by Subclassing" subsection of the Core Animation Programming Guide, you typically override -display in your CALayer if you want to somehow customize the contents CGImageRef for your layer. Normally, you'll be overriding -drawInContext: if you want to render custom Quartz drawing within your CALayer. Making your overridden -display method totally blank, and not writing anything to the contents property, is not standard behavior, so I'm not surprised that you're seeing odd results from doing that.
Based on your series of recent questions, I highly recommend you stop and spend some time reading the Core Animation Programming Guide and looking at some sample code involving CALayers before proceeding further.
In an iPad app, I have a bunch of UIImageViews inside a bigger UIView. Each UIImageView contains a thumbnail that is generated in a separate thread (so as not to freeze the application). After thumbnail has been successfully generated I call setNeedsDisplay on main thread, however, it doesn't update the UIImageViews as the thumbs become available (I can see them in the log), rather it takes about 5 seconds and then displays all of them at once.
here's what I am doing when a thumbnail has been created in a separate thread:
[self performSelectorOnMainThread:#selector(setNeedsDisplay)
withObject:nil
waitUntilDone:NO];
any ideas?
What's your main thread up to? If your application waits until all the thumbnails are available before redrawing, then it sounds like maybe you are inadvertently blocking on your main thread until your thumbnailing queue has emptied. How are you setting up the threads?
The problem is that you can't tell cocoa that it have to redraw "now".
With setNeedsDisplay you can onely order a redraw, because drawing is ra rather expensive procces.
setNeedsDisplay
You can use this method or the
setNeedsDisplayInRect: to notify the
system that your view’s contents need
to be redrawn. This method makes a
note of the request and returns
immediately. The view is not actually
redrawn until the next drawing cycle,
at which point all invalidated views
are updated.
Mybe its a better and more performant solution to wait for all generated thumbnials?!
I would use NSNotification. Send a notification from your thread loading the images when an image is ready. Your view controller can observe these notifications and update the view as they arrive.
I have seen many posts about this problem but didn't get an answer. I have a controller which view is added to the main window. The controller's view has a subview which has a drawRect. The problem is that this function is never called even if I call [self setNeedsDisplay].
Thanks
It is -(void)drawRect:(CGRect)rect right? Make sure the method signature is correct, and you don't omit the rect argument even if you don't use it.
-setNeedsDisplay should be called the the subview, not self.
Also, -setNeedsDisplay won't call -drawRect: immediately. It only flushes the graphics cache so that -drawRect: is forced to be called in the next update of the frame.
My object a sub class of NSObject has CALayer instance variable. I use it for drawing and caching content with its delegate set to my object.
But for some reason drawLayer:inContext: method NEVER gets called. Whereas actionForLayer:forKey: delegate method does get called implying the delegate is getting set properly with layer.delegate = self in the object's init method.
Any suggestions on what is preventing my layer drawing method drawLayer:inContext: from getting called ?
I am called the [layer setNeedDisplay] often. So I guess it is some fundamental error.
drawLayer:inContext: won't get called if your frame is CGRectZero or offscreen. Also, if your CALayer isn't attached to an existing onscreen layer, it will never draw, no matter how many times you call setNeedsDisplay
Implement an empty drawRect:
- (void)drawRect:(CGRect)rect {
}
Taken from the ZoomingPDFViewer project:-
UIView uses the existence of -drawRect: to determine if it should
allow its CALayer to be invalidated, which would then lead to the
layer creating a backing store and -drawLayer:inContext: being
called. By implementing an empty -drawRect: method, we allow UIKit
to continue to implement this logic, while doing our real drawing work
inside of -drawLayer:inContext:
The layer object's setNeedsDisplay must be called. Simply adding the layer as a sublayer does not do that for you. Got this from Ray Wenderlich's CALayer tutorial.
If you're eventually using the CALayer with a UIView then the delegate must be the view object itself:
From the iOS CALayer documentation:
"In iOS, if the layer is associated with a UIView object, this property must be set to the view that owns the layer."
If you have a multi-threaded app where background processing drives the need to update the CALayer, you must call setNeedsDisplay in the main thread