Skip to content

分页与连续滚动

滑动分页

TextKit 生成连续 UTF-16 字符范围。每页以实际 UITextView 渲染尺寸复测,溢出时只在完整 行边界回退。页面范围必须满足:第一页从 0 开始、相邻范围首尾相接、最后一页到正文末尾。

相邻章节预先分页并合并进稳定页面数组。跨章时更新章节/进度元数据,不替换整个 mounted 文档,减少白屏和跳动。

每次重新分页都会给每一页生成全新的标识(不是复用旧页面的 ID),即使字符内容完全没变。 滑动模式因此把“翻页容器需要整体重建”和“单页内容更新”绑在一起处理,而不是只依赖单页视图 自己去判断内容是否变化——重排、字号变化等触发的整页重建,靠的是给容器本身也换一个跟随 本次重排变化的标识,不是指望每个页面单元格自己发现内容变了。

连续滚动

每个 fragment 使用 TextKit 实测文本高度,而不是固定分页高度。章节顶部/底部边距只在整份 连续文档外层应用一次,相邻章节之间不额外插入空行。

页数较少(当前阈值是同一章节 ≤4 页,或系统 VoiceOver 开启时)会用非懒加载的容器一次性 铺开全部页面,减少切页时的布局抖动;超过阈值则改用懒加载容器,只在页面即将进入可视区域 时才真正构建,避免长章节一次性构建几十个 UITextView 拖慢首屏。这两种容器共用同一套分页 数据和渲染视图,切换只影响“什么时候构建”,不影响“构建出来的内容”。

章节边界

用户到达边界前,上一章/下一章内容、清洗结果和布局应已准备。准备完成后的切换不得执行:

  • 网络请求;
  • 磁盘读取;
  • 正文清洗或替换;
  • TextKit 分页;
  • 等待异步任务。

诊断

阅读快照包含 pageRangeGapCountpageRangeOverlapCount、当前页范围、viewport 匹配和帧 间隔指标,另外还会记录触发本次快照的原因(比如 repaginate)、当前字号和分页耗时相关的 容器尺寸。任何 gap/overlap 非零都视为正文完整性 bug——正常情况下相邻页面范围应该首尾 相接、互不重叠,一旦出现空隙或重叠,说明某次重排的边界计算出错,而不是简单的显示问题。

Readori 使用与开发文档