vector - 应该使用什么 CALayer draw(in ctx:) 坐标以正确的比例在正确的点上绘制?
问题描述
我有一个 NSView,它使用 CALayers 创建内容并希望以矢量格式生成 pdf 输出。首先这是可能的,其次苹果在他们的文档中引用的逻辑坐标系是什么,最后你应该如何让 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() 函数中使用图层框架来计算大小/位置,但是当图层被转换时,它的框架会改变大小和形状以适应原始旋转的矩形!所以提示 - 将您的原始矩形保存在其他地方,然后当您应用旋转或使用边界时,您的绘图不会突然变得奇怪!
EDIT2: - 所以 render(in:) 有效,“有点” - 一些 3D 变换被忽略,例如 CATransform3DMakeTranslation()。旋转变换似乎很幸运,所以只需确保设置正确的原点以避免需要平移变换。
在 pdf 中绘制时,CALayers 调用 NSView 的 draw(dirtyRect:) 函数 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()
}
}
推荐阅读
- swift - 从 GKTurnBasedMatchMakerViewController 检索 GKTurnBasedMatch 对象 - Swift 4
- android-studio - 如何设置 ButterKnife 逐行代码样式?
- c# - WPF 跳跃或闪烁按钮
- soapui - SoapUI - 将 JWT 令牌设置为全局项目参数
- ios-charts - 将条形中心点调整到左侧点
- python - 在 Python 中使用 urllib 从网页中仅下载文本(而不是图像)
- excel - Excel if 或查询多个选项
- java - Java RPG 游戏:字体加载器不会加载 FontFile .ttf(未找到文件)
- github - 无法使用 Gitbash 在 Github 上推送我的代码
- bixby - 结果的音频支持