应该使用什么 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()
}
}
我有一个 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()
}
}