How can I get CALayer to render sublayers correctly when generating PDF ?

I am trying to get an image drawn of a CALayer containing a number of sublayers positioned at specific points, but at the moment it does not honour the zPosition of the sublayers when I use CALayer.render(in ctx:). It works fine on screen but when rendering to PDF it seems to render them in the order they were created.

These sublayers that are positioned(x, y, angle) on the drawing layer.

One solution seems to be to override the render(in ctx:) method on the drawing layer, which seems to work except the rendering of the sublayers is in the incorrect position. They are all in the bottom left corner (0,0) and not rotated correctly.
Code Block
override func render(in ctx: CGContext) {
if let layers:[CALayer] = self.sublayers {
let orderedLayers = layers.sorted(by: {
$0.zPosition < $1.zPosition
})
for v in orderedLayers {
v.render(in: ctx)
}
}
}

If I don't override this method then they are positioned correctly but just in the wrong zPosition - i.e. ones that should be at the bottom (zPosition-0) are at the top.

What am I missing here ? It seems I need to position the sublayers correctly somehow in the render(incts:) function?

How do I do this ? These sublayers have already been positioned on screen and all I am trying to do is generate an image of the drawing. This is done using the following function.
Code Block
func createPdfData()->Data?{
DebugLog("")
let scale: CGFloat = 1
let mWidth = drawingLayer.frame.width * scale
let mHeight = drawingLayer.frame.height * scale
var cgRect = CGRect(x: 0, y: 0, width: mWidth, height: mHeight)
let documentInfo = [kCGPDFContextCreator as String:"MakeSpace(www.***.com)",
kCGPDFContextTitle as String:"Layout Image",
kCGPDFContextAuthor as String:GlobalVars.shared.appUser?.username ?? "",
kCGPDFContextSubject as String:self.level?.imageCode ?? "",
kCGPDFContextKeywords as String:"XXXXX, Store Layout"]
let data = NSMutableData()
guard let pdfData = CGDataConsumer(data: data),
let ctx = CGContext.init(consumer: pdfData, mediaBox: &cgRect, documentInfo as CFDictionary) else {
return nil}
ctx.beginPDFPage(nil)
ctx.saveGState()
ctx.scaleBy(x: scale, y: scale)
self.drawingLayer.render(in: ctx)
ctx.restoreGState()
ctx.endPDFPage()
ctx.closePDF()
return data as Data
}

Replies

Here is a sample app that illustrates the problem. For some reason the editor complains about the URL below so please remove the space after the https to download the test app

https ://duncangroenewald.com/files/SampleApps/CALAyerRendering.zip
This is what I ended up doing - should this really be necessary ?

Code Block
class ZOrderDrawingLayer: CALayer {
    override func render(in ctx: CGContext) {
        if let layers:[CALayer] = self.sublayers {
            let orderedLayers = layers.sorted(by: {
                $0.zPosition < $1.zPosition
            })
            for v in orderedLayers {
                ctx.saveGState()
                let w = v.bounds.width/2
                let ww = w*w
                let h = v.bounds.height/2
                let hh = h*h
                let c = sqrt(ww + hh)
                let theta = asin(h/c)  
                let angle = atan2(v.transform.m12, v.transform.m11)
                let x = c * cos(theta+angle)
                let y = c * sin(theta+angle)
                ctx.translateBy(x: v.position.x-x, y: v.position.y-y)
                ctx.rotate(by: angle)
                v.render(in: ctx)
                ctx.restoreGState()
            }
        }
    }
}