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

  • 本文关键字:绘制 坐标 CALayer ctx vector calayer nsview
  • 更新时间 :
  • 英文 :


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

该视图具有以下层:

  • 大于绘图区域的视图图层
  • 包含图像和子图层的绘图图层
  • 具有一些矢量绘图(包括文本(的子图层

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

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

我认为这是因为绘图层应用了转换,而下面的绘图没有考虑到这一点。 这是问题所在吗,我将如何将图层转换应用于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(( 函数中的大小/位置,但是当图层被转换时,它的框架会改变大小和形状以适应原始旋转的矩形! 所以提示 - 将原始矩形保存在其他地方,然后当您应用旋转或使用边界时,您的绘图不会突然变得奇怪!!

编辑2: - 所以渲染(in:(工作,"有点" - 一些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()
}
}

最新更新