CALayer -displayLayer:inContext: never called during frame/bounds animation - uikit

I need to animate the frame of a CALayer instance; during the animation I should also update/redraw the content of the layer based upon the new size (frame by frame) of it.
I've tried to watch and use -displayLayer:inContext: of CALayer (it's a delegate property) but It does not work: both with frame (it's a derived property of bounds/location/anchorPoint) and bounds animation I can't receive this message (it should be called by CALayer's internal drawInContext: method) during the animation process.
Using needsDisplayOnBoundsChange I can get it called at the end of the animation, but nothing with bounds.
It works fine with custom properties (I need to return YES on needsDisplayForKey method).
Any idea?
Is possible to force redraw of CALayer contents during bounds/frame animation?

Related

getRectsBeingDrawn returns NULL

I have custom NSView class which I am porting from OpenGL to Metal for GPU rendering. A lot of places in the code, earlier we used to call setNeedsDisplayInRect with an invalidated rect on the NSView, which I queried during the call to drawRect for rendering final content to the screen.
For moving to Metal, I have added a CAMetalLayer to my custom NSView class. With the addition of this CAMetalLayer, getRectsBeingDrawn returns NULL.
Are we saying that getRectsBeingDrawn only works for non-layer/non-metal layer backed NSViews?
Moreover, I also see a setNeedsDisplayInRect in CALayer, but I don't see a getter like getRectsBeingDrawn in CALayer.
Should I move to setNeedsDisplayInRect in CALayer for dirty rects in case of layer backed views? But, then again how do I retrieve those dirty rects?

can drawRect be triggered on a SCNView (macOS)?

I have two macOS apps that are very similar. One app renders an animation in 2-D, with Quartz calls, in a subclass of NSView, the other app, a 3-D animation in a subclass of SCNView (itself a subclass of NSView) using SceneKit geometries. In each case the "view" is owned by a view controller and that ownership is set in a storyboard. In each case I use a timer to dirty the view every second so its drawRect gets triggered to drive the animated movements. In each case I have used: self.view.needsDisplay = true
In the 2-D case, drawRect is called in the view instance, in the 3-D case it is not (even for the initial render).
I'm puzzled! Does SCNView suppress calls to drawRect? If so, how might I get around this? If not, what voodoo secret have I missed?
If this behavior is not what readers would expect, I will post a sample project which exhibits it.
I know that SceneKit can take advantage of Core Animation but I want to keep the same general timer mechanism in both apps because the animated content is, essentially, the same action, what was flat in 2-D is spherical in 3-D so using SceneKit rendering made sense.
Added an Xcode project to show different NSView and SCNView behaviors:
https://www.dropbox.com/s/qtymzitkqcqhfje/SCN.zip?dl=0
You're fighting the framework.
SceneKit has its own timers for rendering and animation. Hook into those to update your objects' properties (locations, colors, etc). Let SceneKit handle the draw calls.
The methods you need may be in unexpected places. Take a look at documentation for SCNSceneRenderer and SCNSceneRendererDelegate protocols. The renderer delegate documentation explains the render loop and shows you where to customize your app's animations and physics.

Misunderstanding with CALayer in CGContextRef

I have a custom view in which I want to display a CALayer with PDF content. To do so I implemented a delegate NSObject subclass as shown in the first answer to Using CALayer Delegate.
Since I have a document-based app, I have a starting window from which I can open documents. From the custom document I initWithWindowNibName: a custom windowController from the makeWindowControllers method. From the windowController, in windowDidLoad, I set a custom NSView's variable values and initialize the CALayer. In the same place I run this line of code to draw the content:
[[[PDFViewLayerDelegate alloc] initWithUrl:url andPageIndex:currentPageIndex] drawLayer:layer1 inContext:[[NSGraphicsContext currentContext] graphicsPort]];
What happens is: while before running that line the background of the CALayer was set to green and would appear only in the correct window, now the PDF content is drawn to the initial window only while both layers are filled with white (which is also done in the delegate method).
My questions are:
Why is my CALayer being drawn to a view which is not of the custom
NSView subclass which creates it? and furthermore in different windows?
Are both views in each window sharing the same graphicsContext? This might be the cause..
I understood the problem... As I presumed the problem was both windows share the same graphicsContext, as the currentContext is:
"The current graphics context of the current thread."
I simply resolved this problem placing the CALayer drawing function call inside the drawRect: method of it's container NSView since drawRect: is responsible for drawing exclusively inside it's view and probably already handles the graphicsContext stuff in somewhat way.

How do I prevent a CALayer from redrawing as its bound change?

i have a CALayer with a custom draw method I've added to my view's base layer. I set needsDisplayOnBoundsChange to NO. However, when I resize the parent view's frame, the layer's drawInContext: is getting called continuously. I'd like the contents to scale while the resize is occurring. Any clues?
Interesting, I have a case where I have a CALayer that correctly scales its contents until I call setNeedsDisplay on it to redraw its contents. One thing that may be different is that in my case the layer is being drawn by its delegate and not by a subclass of CALayer. Another thing that may be different is that this is on iOS and not OSX (I don't know which you are using in this case). It is possible that there could be behavior differences between subclasses and delegate drawn layers and/or iOS and OSX.
Another thing to note is that needsDisplayOnBoundsChange is documented to be NO by default, so one should not need to set it. I am not specifically setting needsDisplayOnBoundsChange on my layer.
You could try using a delegate to do the drawing to see if that makes a difference. Note that a UIView cannot be a delegate to a CALayer. In my case I made a simple delegate object that forwards the draw call to my view.

iPad - Handling orientation change via reloaded loadView

I have a custom view which needs to be relaid on orientation change. My UIView code can correctly redraw for the any orientation as it express itself in screen frame size.
But how do I get the loadView method to run on orientation change for the right view to get drawn ?
I put [self.view setNeedsDisplay] in the following rotation handling methods with no luck.
willRotateToInterfaceOrientation:duration
didRotateFromInterfaceOrientation:
Any suggestions would be most welcome.
loadView actually causes the view to be completely re-created from scratch. It should be sufficient to call setNeedsDisplay in didRotateFromInterfaceOrientation on the view that needs to redraw.
If you have to change the layout of your views upon rotation, do so in willAnimateRotationToInterfaceOrientation (so the changes get animated), and/or set the autoresizingMask property to the combination of UIViewAutoresizingMask values that suits your needs.

Resources