应该使用什么 CALayer draw(in ctx:) 坐标以正确的比例绘制正确的点?

What CALayer draw(in ctx:) coordinates should be used to draw in the correct point with the correct scale?

我有一个 NSView,它使用 CALayers 创建内容并希望生成矢量格式的 pdf 输出。首先这是可能的,其次苹果在他们的文档中提到的逻辑坐标系是什么,最后你应该如何让 CALayers 默认背景和边框属性来绘制?如果我使用这些属性,则它们不会被绘制到上下文中,如果我绘制边框,则会重复 属性 设置。关于何时应该或不应该使用这些属性,或者在创建矢量输出时是否应该使用 CALayers,例如pdf 文档.

视图有以下层:

下面列出了 NSView class 和 CALayer 子class。

视图层和绘图层绘制在正确的位置,但所有绘图层子视图都绘制在错误的位置并且大小不正确。

我认为这是因为绘图层应用了变换,而下面的绘图没有考虑到这一点。这是问题所在吗?我如何将图层转换应用于 CGContext - 如果这是解决方案 - 以将事情放在正确的位置?

class DrawingView: NSView {

    override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)
        guard let context = NSGraphicsContext.current?.cgContext else {
            return
        }
        // In theory should generate vector graphics
        self.layer?.draw(in: context)
        //Or
        // Generates bitmap image
        //self.layer?.render(in: context)
    }
}

class DrawingLayer: CALayer {

    override func draw(in ctx: CGContext) {
        drawBorder(in: ctx)
        if let subs = self.sublayers {
            for sub in subs {
                sub.draw(in: ctx)
            }
        }
    }
    func drawBorder(in ctx: CGContext){
        let rect = self.frame

        if let background = self.backgroundColor {
            ctx.setFillColor(background)
            ctx.fill(rect)
        }
        if let borders = self.borderColor {
            ctx.setStrokeColor(borders)
            ctx.setLineWidth(self.borderWidth)
            ctx.stroke(rect)
        }
    }
}

这是我的解决方案 - 使用不同的函数在 PDF 中绘图。我仍然不确定我是否理解为什么系统在绘制为 pdf 和屏幕时表现不一致。

编辑:- 问题的一部分似乎是我在 draw() 函数中使用图层框架来计算 size/position 但是当一个图层被转换时,它的框架会改变大小和形状以适应原始旋转矩形!所以提示 - 将您的原始矩形保存在别处,然后当您应用旋转或使用边界时,您的绘图不会突然变得奇怪!!

EDIT2: - 所以 render(in:) 有效,"kind of" - 一些 3D 变换被忽略,例如 CATransform3DMakeTranslation()。旋转变换似乎很幸运,所以只需确保设置正确的原点即可避免平移变换的需要。

当在 pdf 中绘图时,NSView 的 draw(dirtyRect:) 函数被 CALayers draw(in:) 函数调用时不会自动被调用,所以看起来你必须自己一直调用它们,记住要为每个子层转换坐标系 - 如果他们在本地坐标系中绘图。请注意,这并不能保证 pdf 中的矢量输出 - 似乎文本之类的东西被渲染为位图。如果有人对事物的行为方式和原因有解释,请解释。

无论如何,我已经退回到使用 CALayer.render(in:) 函数,该函数会导致渲染整个图层层次结构 - 并且仍然带有一些矢量和一些位图元素!

class DrawingView: NSView {

    var drawingLayer: DrawingLayer? {
        return self.layer?.sublayers?[0] as? DrawingLayer
    }

    override func draw(_ dirtyRect: NSRect) {
        //super.draw(dirtyRect)
        guard let context = NSGraphicsContext.current?.cgContext else {
            return
        }
        // In theory should generate vector graphics
        self.drawingLayer?.drawPdf(in: context)

        //Or
        // Generates bitmap image
        //self.layer?.render(in: context)
    }
}

class DrawingLayer: CALayer {

   var fillColor: CGColor?
   var lineColor: CGColor?
   var lineWidth: CGFloat = 0.5
    var scale: CGFloat = 1.0

    // No need for this since we don't need to call draw for each sublayer
    // that gets done for us!
    override func draw(in ctx: CGContext) {
        print("drawing DrawingLayer  \(name ?? "")")

//        if let subs = self.sublayers {
//            for sub in subs {
//                sub.draw(in: ctx)
//            }
//        }
    }
    func drawPdf(in ctx: CGContext) {


        ctx.saveGState()
        ctx.translateBy(x: self.frame.minX, y:  self.frame.minY)

       if let subs = self.sublayers {
           for sub in subs {

               (sub as! NormalLayer).drawPdf(in: ctx)
           }
       }

        ctx.restoreGState()
    }
}
class NormalLayer: CALayer {

    var fillColor: CGColor?
       var lineColor: CGColor?
       var lineWidth: CGFloat = 0.5
    var scale: CGFloat = 1.0

    override func draw(in ctx: CGContext) {
        drawBorder(in: ctx)
    }
    func drawBorder(in ctx: CGContext){
        let bds = self.bounds
        let rect = CGRect(x: bds.minX*scale, y: bds.minY*scale, width: bds.width*scale, height: bds.height*scale)
        print("drawing NormalLayer \(name ?? "")  \(rect)")

        if let background = self.fillColor {
            ctx.setFillColor(background)
            ctx.fill(rect)
        }
        if let borders = self.lineColor {
            ctx.setStrokeColor(borders)
            ctx.setLineWidth(self.lineWidth)
            ctx.stroke(rect)
        }
    }
    func drawPdf(in ctx: CGContext) {
        ctx.saveGState()
        ctx.translateBy(x: self.frame.minX, y:  self.frame.minY)
        drawBorder(in: ctx)
        ctx.restoreGState()
    }
}