7

我有一个 PDF,其中包含不同高度的页面,我想以水平分页的方式显示,一次一页,页面之间没有间隙,并且每页的顶部与视图的顶部对齐。例如:

所需的渲染

我想使用PDFKit's PDFView,特别是因为这些设置可以通过以下方式实现:

let pdfView = PDFView()
pdfView.displayDirection = .horizontal
pdfView.displaysPageBreaks = false
pdfView.usePageViewController(true, withViewOptions: nil)

但是,这会产生以下结果:

实际渲染

这是由于页面的高度不同。每个页面在 中垂直居中PDFView,如果页面的高度大于视图的高度(例如第 2 页),则按纵横比缩放以适应。

为了尝试解决第一个问题,垂直偏移,我尝试了几件事。首先,这很严重,我知道这是错误的,在检查视图层次结构并找到在其中呈现页面的私有视图(名为 的类的视图PDFTextInputView)之后,我尝试将其框架调整origin.y0最外层滚动视图(另一个私人视图)滚动。这是因为每当下一页或上一页滚动到视图中时,页面都会呈现在视图的顶部。但是,一旦用户在该页面上进行捏合缩放,它的框架就会自动跳回居中。除了依赖私有视图层次结构之外,这种方法是行不通的。

为了尝试解决“高页面问题”(第 2 页),我尝试在出现新页面时调整 PDFView 的比例因子。可悲的是,.PDFViewPageChanged通知仅在滚动视图结束减速触发,因此页面在滚动时被缩小,然后当我调整比例因子时它会跳转以适应屏幕的宽度。然后我转向.PDFViewVisiblePagesChangedwhich 确实会触发,因为下一页/上一页正在进入视野(以及更多次,这很好),这与.PDFViewPageChanged.

我尝试过的其他事情:

  • 调整transform文档视图
  • 拦截捏合手势并调整文档视图的中心
  • 创建我自己的页面控制器并每页呈现一个 PDF 页面(不使用pdfView.usePageViewController

有人对我如何实现我所追求的有任何想法吗?

4

4 回答 4

5

新黑客:

所以事实证明,在挖掘了一些更多的私有 API(是的,这个解决方案仍然很糟糕)之后,我完全忽略了这个神奇的属性PDFScrollView(一个私有的内部视图),称为......

forcesTopAlignment

要启用此功能,请PDFScrollView在您的PDFView和中找到:

pdfScrollView.setValue(true, forKey: "forcesTopAlignment")

这样就行了!


老黑客:

在靠墙几天后,我终于想出了一种方法来PDFView表现我想要的样子。

披露:该解决方案使用方法混合并依赖于私有视图层次结构,并且可能随时中断。也就是说,它现在有效,我对解决方案感到满意。

此要点中提供了完整的解决方案。(肉在overrideCenterAlign.)

有一个恰当命名的私有方法_centerAlign,当pdf页面出现在屏幕上并使用捏合手势缩放时,它垂直居中并缩放它们。Swizzling 该方法允许我注入我自己的逻辑并应用我自己的转换来定位我想要的 pdf 视图(页面顶部与视图顶部对齐。)

有两种情况需要考虑。“短页”案例(示例中的第 1、3、4 页)和“高页”案例(示例中的第 2 页)。在这两种情况下,我首先调用 的原始实现,_centerAlign以便它可以应用其转换并管理更新内部滚动视图。

  • 对于“短页”的情况,我应用相同的转换和垂直平移来将 pdf 的顶部与视图的顶部对齐。
  • 对于“长页面”的情况,我调整内部滚动视图的缩放比例,因为它出现在屏幕上,以便缩放以适应视图的宽度。

综上所述,我对不依赖私有方法和视图层次结构的更清洁的解决方案持开放态度。如果您知道实现此目的的更好方法,请发布答案!

于 2020-05-20T17:18:48.653 回答
2

只需将 forceTopAlignment 设置为 true

例子:

lazy var pdfView: PDFView = {
    let pdf = PDFView()
    pdf.displayMode = .singlePageContinuous
    pdf.autoScales = true
    pdf.translatesAutoresizingMaskIntoConstraints = false
    pdf.setValue(true, forKey: "forcesTopAlignment")
    return pdf
}()

我的答案与接受的答案有何不同?

pdfView 上没有可用的滚动视图属性。因此,您只需将值直接设置为 pdfView。

于 2020-09-11T14:23:49.313 回答
0

我认为问题在于将 PDFView 添加到有约束的视图中。当我只使用 addSubview() 和用户正确的框架添加时,它不需要调用 setValue。

让 pdfView = PDFKit.PDFView(frame: self.view.bounds) pdfView.document = PDFKit.PDFDocument(url: url) pdfView.autoresizingMask = [.flexibleHeight, .flexibleWidth] view.addSubview(pdfView)

于 2020-12-11T01:51:51.480 回答
0

注意:此答案旨在帮助其他到达这里尝试解决类似问题的人。

自从苹果在 iOS13 中引入“拖动隐藏”(Apple “https://developer.apple.com/design/human-interface-guidelines/ios/app-architecture/modality/”)以来,我在 iOS12 中发现了一个令人讨厌的错误。

如果目标是 iOS13 或更高版本,一切正常。

在 iOS12 中,可能是由于计算大小不同,(我猜..)pdf 呈现错误:一些内容向右并被剪掉。

一个肮脏的修复包括不打电话

self?.pdfView.autoScales = true

在 viewDidLoad 中,

但是在加载每个 PDF 等待一些毫秒并设置它之后:

 if #available(iOS 13.0, *) {
    // NADA.. go on..
 }else{
     self.pdfView.autoScales = false // disable it.
     self?.pdfView.alpha = 1
}


let pdf = load ....... 

self.pdfView.document = pdf

 if #available(iOS 13.0, *) {
        //nada..
        } else {
            // hack for ios 12
            let when = DispatchTime.now() + 0.25
            DispatchQueue.main.asyncAfter(deadline: when, execute: { [weak self] in
                self?.pdfView.autoScales = true // hack for ios 12
                self?.pdfView.alpha = 1
            })
        }
  

使用 alpha 防止查看尚未采用的 pdf。

于 2021-06-13T17:09:15.190 回答