iOS PDFKit 在 PDFView 中调整 pdf 框架

iOS PDFKit adjust pdf frame in PDFView

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

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

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

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

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

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

为了尝试解决 "tall page problem"(第 2 页),我尝试在新页面出现时调整 PDFView 的比例因子。可悲的是,.PDFViewPageChanged 通知仅在 之后触发 滚动视图结束减速,因此页面在滚动时缩小,然后当我调整比例因子时它会跳转以适合屏幕的宽度。然后我转向 .PDFViewVisiblePagesChanged,当 next/previous 页面进入视图时确实会触发(还有几次,这很好),这与 [=18= 有相同的问题].

我尝试过的其他事情:

有没有人知道我如何才能实现我所追求的目标?

新技巧:

结果是,在深入研究了一些私有 API 之后(是的,这个解决方案仍然很糟糕),我在 PDFScrollView(一个私有的)上完全忽略了这个神奇的 属性 , 内部视图) 称为...

forcesTopAlignment

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

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

成功了!


老黑客:

在碰壁几天后,我终于想出了一个方法让 PDFView 表现得像我想要的那样。

披露:此解决方案使用 method swizzling 并依赖于私有视图层次结构,并且可能随时中断。 也就是说,它目前有效,我对解决方案感到满意.

this gist 中提供了完整的解决方案。 (肉在overrideCenterAlign.)

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

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

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

综上所述,我愿意接受不依赖私有方法和视图层次结构的更简洁的解决方案。如果您知道更好的方法来完成此操作,请 post 回答!

只需将 forcesTopAlignment 设置为 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 上没有 属性 作为 scrollView 可用。所以你直接给pdfView设置值就可以了。

我认为问题在于添加 PDFView 以查看约束。当我仅使用 addSubview() 和用户适当的框架添加时,它不需要调用 setValue。

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

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

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

如果目标为 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。