Files
ReadViewSDK/Sources/RDPDFReaderView
shenleiandClaude Opus 5 d830311894 修复 PDF 仿真翻页模式下翻页后页面全白
打开普通 PDF 并使用仿真翻页阅读时,除首页外每翻一页都是纯白:没有页面
内容,也没有页面四周的衬底色。横向滚动与竖滑模式不受影响。样张
英汉大词典App需求说明.pdf 上第 2 到第 9 页无一例外。

根因是页面数据回来时找不到对应的视图。UIPageViewController 会通过
viewControllerBefore/After 提前构建相邻页的内容视图,构建时页面描述尚未
就绪,configure 发起异步 readerPage 请求后先返回一张空白页。描述返回后
acceptPageDescriptor 调用 refreshVisiblePage,而它经由
RDPDFReaderView.pageContentView(pageNum:) 定位视图,该方法在仿真翻页下
只检索 nativeCurlPageController.viewControllers,也就是已经上屏的 child。
预建的 child 此时还不在其中,图片因此从未被装配上去;翻页动画结束后又
没有任何路径重新 configure 该视图,白页就固定下来。此前偶尔能看到内容,
是 OCR 完成回调顺带触发了一次刷新,并非稳定行为。

改为按页码刷新该页的全部活动视图:控制器用弱引用表记录已创建的页面视图,
refreshVisiblePage 改名为 refreshPageViews(at:),除已上屏视图外一并装配
尚未上屏的预建视图,pageContentView 创建时即写入 tag 以便按页码匹配。
翻页落定时在 pageNum 回调中补一次装配,使内存告警清掉页面描述的场景也能
重新发起请求并恢复。另外把 image 为空的页面描述改按无效结果处理,走既有
的重试与失败重载入口,不再缓存出一张永远不会重试的白页。

新增 PDFPageTurnBlankPageTests 作为回归:翻页稳定后截屏统计画面中部深色
像素占比,白页为 0,扉页约 0.002,正文页在 0.01 以上,以 0.0005 为判定
阈值,覆盖仿真翻页正翻、仿真回翻和横向滚动三种路径。

验证:iPhone 17 Pro / iOS 26.4.1 模拟器执行 PDFPageTurnBlankPageTests、
PDFAnnotationTests、PDFDrawingModeTests、PageNavigationTests、
DisplayTypeTests 共 23 条用例,22 条通过。修复前仿真翻页第 2 到 9 页墨迹
占比全为 0.0000,修复后与横向滚动模式逐页吻合。唯一失败的
DisplayTypeTests.testDisplayTypeSwitchMidSession 属 EPUB 设置面板既有问题,
将本次改动还原后同样失败。文档补充了 Demo 因链接 FoundationModels 而必须
使用 iOS 26 及以上模拟器的说明。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 17:52:29 +09:00
..
2026-07-27 21:43:13 +08:00
2026-07-27 21:43:13 +08:00

RDPDFReaderView

独立的 UIKit PDF 阅读交互 SDK。当前第一阶段只包含与业务无关的部分:分页、双指缩放、双击缩放、放大后拖动,以及画笔模式下的手势隔离。

主工程的下载、解密、缓存、链接、标注和持久化仍由现有 PDF Feature 管理;后续通过适配层逐步迁移,避免把业务问题和手势问题混在一起。

PDF 数据来源

普通 PDF 可以由 SDK 使用 PDFKit 直接解析:

let reader = try RDPDFReaderViewController(
    pdfURL: fileURL,
    bookIdentifier: stableBookID,
    password: standardPDFPassword,
    persistence: persistence,
    annotationPersistence: annotationStore
)

自定义加密 PDF 继续由宿主解密并提供页面图片,不需要把原始文件交给 SDK:

let reader = RDPDFReaderViewController(
    pageProvider: encryptedImageProvider,
    persistence: persistence,
    annotationPersistence: annotationStore
)

两种入口最终都转换为 RDPDFReaderPageProvider,共用翻页、缩放、目录、书签、OCR、标注和画笔功能。内置 PDFKit 数据源按需渲染页面、限制最大图片尺寸并使用内存缓存;能够读取的原生 PDF 文字会直接用于选择和标注,图片扫描页则按配置回退到 OCR。

调试入口

在任意测试宿主中 push RDPDFReaderDebugViewController()

navigationController?.pushViewController(RDPDFReaderDebugViewController(), animated: true)

验证顺序:

  1. 双指捏合能连续放大和缩小。
  2. 双击能在适配尺寸与两倍缩放之间切换。
  3. 放大后单指只能拖动内容,不能翻页。
  4. 切到“画笔模式”后,单指不再翻页,双指仍能缩放和拖动。

如果这个独立页面工作正常,而主阅读页仍失效,问题就能确定在主工程的页面层/覆盖层,而不是缩放内核。

图片型 PDF 的文本、复制与标注

主程序即使只能提供每页图片,也可以接入文字功能:

  1. 若主程序已有 PDF 解析结果,在 RDPDFReaderPageDescriptor.textRuns 中传入文字和相对图片的 0...1 坐标;这条路径提供最准确的复制和高亮。
  2. 若没有文字坐标,使用 RDPDFReaderImageTextRecognizer 对当前页图片按需 OCR,再把结果赋给 RDPDFReaderImageTextLayerView.textRuns
  3. OCR 也不可用时,文字层会提供区域框选;区域标注只显示“高亮、注释”,不会错误地提供复制。

高亮与注释统一使用 RDPDFReaderAnnotation,坐标同样是相对图片的 0...1 比例,缩放、旋转或重新渲染页面后仍能对齐。使用 RDPDFReaderPersistenceStore 保存时,请传入宿主稳定的书籍 ID(可附账号和内容版本)对应的目录,并对 addAnnotationupdateAnnotationdeleteAnnotationthrows 结果做错误提示;文件损坏或版本不兼容时 SDK 会拒绝覆盖原数据。