在 iPad 上将 UIView 作为矢量呈现为 PDF - 有时呈现为位图,有时呈现为矢量
Posted
技术标签:
【中文标题】在 iPad 上将 UIView 作为矢量呈现为 PDF - 有时呈现为位图,有时呈现为矢量【英文标题】:Rendering a UIView into a PDF as vectors on an iPad - Sometimes renders as bitmap, sometimes as vectors 【发布时间】:2011-09-19 08:46:49 【问题描述】:我有一个 iPad 应用程序,我正在尝试从 UIView 生成 PDF,它几乎运行良好。
代码真的很简单,如下:
UIGraphicsBeginPDFContextToFile( filename, bounds, nil );
UIGraphicsBeginPDFPage();
CGContextRef pdfContext = UIGraphicsGetCurrentContext();
[view.layer renderInContext:pdfContext];
UIGraphicsEndPDFContext();
除了一个奇怪的例外,这非常有效。如果视图在呈现为 PDF 之前已显示在屏幕上,则视图上的 UILabel 将作为美妙的矢量呈现到 PDF 中。如果视图尚未出现在屏幕上(即控制器是 initWithNib 等,但尚未被推入导航控制器或其他任何东西),则文本将呈现为 'ipad' 分辨率的位图。
这就像渲染到屏幕的行为将视图设置为向量,然后我随后将其渲染到 pdf 上下文。
是否有一些我可以调用的方法或我可以在视图、图层或其他地方设置的属性来模拟这种行为,而不必在屏幕上显示视图?
和 UIViewPrintFormatter 有关系吗?
【问题讨论】:
你有没有得到一个很好的答案?另外,您对生成的 PDF 有任何问题吗?我已经看到很多关于无效 PDF 的讨论以及使用 libHaru 的建议,但我还没有机会亲自测试一下。 还没有解决方案,另一个开发人员在我的工作中接管了这个问题,也没有任何运气。奇怪的是,它现在有时呈现为向量的旧行为似乎并非如此。在我们尝试设置的任何情况下,它现在似乎都不会呈现为矢量。不确定这是因为我们的代码库发生了变化,还是因为升级到了更新版本的 ios SDK。 【参考方案1】:我发现使标签呈现矢量化的唯一方法是使用 UILabel 的子类和以下方法:
/** Overriding this CALayer delegate method is the magic that allows us to draw a vector version of the label into the layer instead of the default unscalable ugly bitmap */
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
BOOL isPDF = !CGRectIsEmpty(UIGraphicsGetPDFContextBounds());
if (!layer.shouldRasterize && isPDF)
[self drawRect:self.bounds]; // draw unrasterized
else
[super drawLayer:layer inContext:ctx];
Swift 5.x:
override func draw(_ layer: CALayer, in ctx: CGContext)
let isPDF = !UIGraphicsGetPDFContextBounds().isEmpty
if !self.layer.shouldRasterize && isPDF
self.draw(self.bounds)
else
super.draw(layer, in: ctx)
这对我有用:标签在生成的 PDF 视图中是未光栅化的和可选择的,并且在呈现到屏幕时表现正常。
【讨论】:
做到了!我们必须将所有标签更改为子类版本,这还不错。 (我们只需要在 xibs 中更改它们)。带有灰色背景的白色文本标签存在问题,我们必须将其替换为灰色 UIView 上的白色文本作为标签背景。 友情提示,你需要#importUIView
及其子视图上递归调用setNeedsDisplay
,这样当我调用renderInContext:
时,视图将被重绘。否则,永远不会调用 drawLayer:inContext:
并使用先前的光栅化层。此外,在调用renderInContext:
之后,需要在视图及其子视图上再次递归调用setNeedsDisplay
,以便它能够正确渲染以再次显示在屏幕上。
我试过这个,我在生成的 uilabels 上得到了黑框。如果它们已经预设,它们可以正常工作。有什么想法吗?
已经预设,我的意思是它们是通过 gui/通过拖动标签将其添加到情节提要中完成的。如果我以编程方式执行此操作,那我会得到一个覆盖标签的黑色矩形。不知道为什么会发生这种情况。我尝试 setNeedDisplay 刷新当前屏幕绘图【参考方案2】:
如果您在屏幕上添加视图但在屏幕外坐标处会怎样。这看起来更像是一种 hack,但它可能会起作用。
【讨论】:
实际上你可能在正确的轨道上。我至少在 ios 8.3 中发现的是,如果你在上下文中渲染一个也在屏幕上的视图,它会产生一个非常好的矢量化 PDF,即使对于带有背景的标签,它也能很好地显示,否则会被绘制为黑框。但是如果你渲染一个不在屏幕上的视图,它会光栅化【参考方案3】:我想建议一个替代 mprudhom 的绝佳解决方案:
使用UIString
扩展,您还可以将UILabel
中的文本呈现为字体(支持select'n'copy 等)
这样字体的字形就可以正确嵌入到 PDF 中了。
为了支持右对齐和居中文本对齐以及默认的垂直居中对齐,我必须为 drawInRect
方法计算一个边界框。
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
BOOL isPDF = !CGRectIsEmpty(UIGraphicsGetPDFContextBounds());
if (!layer.shouldRasterize && isPDF)
// [self drawRect:self.bounds];
CGSize fitSize = [self sizeThatFits:self.bounds.size];
float x = self.bounds.origin.x;
float y = self.bounds.origin.y;
if (self.textAlignment == NSTextAlignmentCenter)
x += (self.bounds.size.width - fitSize.width) / 2.0f;
y += (self.bounds.size.height - fitSize.height) / 2.0f;
else if (self.textAlignment == NSTextAlignmentRight)
x += self.bounds.size.width - fitSize.width;
y += self.bounds.size.height - fitSize.height;
[self.textColor set];
[self.text drawInRect:CGRectMake(x, y, fitSize.width, fitSize.height) withFont:self.font lineBreakMode:NSLineBreakByWordWrapping alignment:self.textAlignment];
else
[super drawLayer:layer inContext:ctx];
【讨论】:
【参考方案4】:使用drawInContext
而不是renderInContext
。
【讨论】:
将“renderInContext”更改为“drawInContext”似乎只是什么都不画,即生成的 PDF 是空的。我会继续四处寻找,看看是否可以让 drawInContext 工作。 仅供参考,对 drawInContext 的进一步检查表明,如果您想手动处理绘制视图,则它是用于覆盖的。所以除非它被覆盖调用它不会做任何事情。 您还需要为每个子视图调用它(在应用适当的坐标系变换之后)。 你有没有解决这个问题?【参考方案5】:尝试使用视图的 viewPrintFormatter。
而不是[view.layer renderInContext:pdfContext];
试试这个
CALayer* formattedLayer = [view viewPrintFormatter].view.layer;
[formattedLayer renderInContext:pdfContext];
【讨论】:
如果我没记错的话,大多数视图没有正确的viewPrintFormatter
和view. viewPrintFormatter.view
将返回view
,即不会更改任何内容。
正如 MrMage 预测的那样,这似乎没有什么不同。文本仍然是像素化/光栅化的。以上是关于在 iPad 上将 UIView 作为矢量呈现为 PDF - 有时呈现为位图,有时呈现为矢量的主要内容,如果未能解决你的问题,请参考以下文章
无法在 NewPad 9.3.5 上将 UIView 转换为 UIImage
带有 renderInContext 的 iPad 3 设备崩溃