如何在正确的位置渲染 CALayer 子层

Posted

技术标签:

【中文标题】如何在正确的位置渲染 CALayer 子层【英文标题】:How do I render CALayer sublayers in the correct position 【发布时间】:2021-05-17 14:22:47 【问题描述】:

我正在尝试绘制包含许多位于特定点的子层的 CALayer 的图像,但目前当我使用 CALayer.render(in ctx:) 时,它不尊重子层的 zPosition。它在屏幕上运行良好,但在呈现为 PDF 时,它似乎按照它们的创建顺序呈现它们。

在绘图层上定位(x,y,角度)的这些子层。

一种解决方案似乎是覆盖绘图层上的 render(in ctx:) 方法,除了子层的渲染位置不正确外,该方法似乎有效。它们都在左下角 (0,0) 并且没有正确旋转。

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)
        
    

如果我不重写此方法,那么它们的位置是正确的,但只是在错误的 zPosition 中 - 即应该在底部的那些 (zPosition-0) 在顶部。

我在这里缺少什么?看来我需要在 render(incts:) 函数中以某种方式正确定位子层?

我该怎么做?这些子层已经定位在屏幕上,我要做的就是生成绘图的图像。这是使用以下函数完成的。

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.xxxx.com)",
    kCGPDFContextTitle as String:"Layout Image",
    kCGPDFContextAuthor as String:GlobalVars.shared.appUser?.username ?? "",
    kCGPDFContextSubject as String:self.level?.imageCode ?? "",
    kCGPDFContextKeywords as String:"XXXX, 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

【问题讨论】:

问候邓肯同胞。如果更改父层的sublayers 数组中的层顺序会怎样? 我相信这会起作用,因为它们目前似乎是按该顺序呈现的。 【参考方案1】:

这就是我最终做的事情——而且它似乎奏效了。

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()

            // Translate and rotate the context using the sublayers
            // size, position and transform (angle)
            
            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()
        

    

【讨论】:

以上是关于如何在正确的位置渲染 CALayer 子层的主要内容,如果未能解决你的问题,请参考以下文章

如何保持 CALayer(CATiledLayer 的子层)在缩放后不改变其比例?

CALayer 子层在设备旋转期间不优雅

在 UIImageView 上一致地居中 CALayer 子层

立即更新 CALayer 子层

将子层添加到 view.layer 会改变框架位置?

IOS CALayer