在 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 上的白色文本作为标签背景。 友情提示,你需要#import 到你的 UILabel 子类中 我发现我需要在我的UIView 及其子视图上递归调用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];

【讨论】:

如果我没记错的话,大多数视图没有正确的viewPrintFormatterview. viewPrintFormatter.view 将返回view,即不会更改任何内容。 正如 MrMage 预测的那样,这似乎没有什么不同。文本仍然是像素化/光栅化的。

以上是关于在 iPad 上将 UIView 作为矢量呈现为 PDF - 有时呈现为位图,有时呈现为矢量的主要内容,如果未能解决你的问题,请参考以下文章

在 Appstore 上将版本设置为仅限 iPad

无法在 NewPad 9.3.5 上将 UIView 转换为 UIImage

UIView中矢量图形的平滑缩放

带有 renderInContext 的 iPad 3 设备崩溃

Flask 在 aws elastic beanstalk 上将 css 文件呈现为空

如何在 iOS13 上的 iPad 上呈现 iPhone 风格的 actionSheet?