feat: EPUB阅读器搜索、注释、CFI模块及大书远距跳转优化
- 实现EPUB阅读器搜索功能及选中注释功能 - 优化CFI模块,修复代码审查发现的11个问题 - 实现大书远距目录跳转与后台补全优化方案 - 优化设置面板与章节运行时联动 - 重构及大量改进优化
This commit is contained in:
@@ -1,27 +1,18 @@
|
||||
import UIKit
|
||||
|
||||
/// 翻页控制器:管理 UIPageViewController 的创建、切换、故障修复和翻页请求队列。
|
||||
///
|
||||
/// RDReaderView 持有此控制器,将 pageCurl 模式的容器管理职责下沉。
|
||||
struct RDReaderPagingController {
|
||||
/// 当前 pageCurl 翻页请求模型
|
||||
|
||||
struct PageTransitionRequest: Equatable {
|
||||
let pageNum: Int
|
||||
let animated: Bool
|
||||
}
|
||||
|
||||
/// 排队中的翻页请求(pageCurl 动画过程中收到新跳转时排队)
|
||||
var pendingTransitionRequest: PageTransitionRequest?
|
||||
|
||||
/// 是否正在执行页面转场动画
|
||||
var isTransitioning: Bool = false
|
||||
|
||||
/// UI 是否已经构建完成(避免重复构建)
|
||||
var didBuildUI = false
|
||||
|
||||
// MARK: - PageViewController 工厂
|
||||
|
||||
/// 创建带指定 spine 位置的 UIPageViewController
|
||||
static func createPageViewController(isDualPage: Bool) -> UIPageViewController {
|
||||
let options: [UIPageViewController.OptionsKey: Any]?
|
||||
if isDualPage {
|
||||
@@ -34,16 +25,12 @@ struct RDReaderPagingController {
|
||||
return pageVC
|
||||
}
|
||||
|
||||
// MARK: - 翻页请求队列管理
|
||||
|
||||
/// pageCurl 动画过程中如果收到新的跳转请求,则排队等待当前动画结束。
|
||||
mutating func shouldQueuePageTransition(_ request: PageTransitionRequest, currentDisplayType: RDReaderView.DisplayType) -> Bool {
|
||||
guard currentDisplayType == .pageCurl, isTransitioning else { return false }
|
||||
pendingTransitionRequest = request
|
||||
return true
|
||||
}
|
||||
|
||||
/// 收尾 pageCurl 转场,返回排队中的后续请求(如果有的话)。
|
||||
mutating func finishPageCurlTransition() -> PageTransitionRequest? {
|
||||
isTransitioning = false
|
||||
guard let pending = pendingTransitionRequest else { return nil }
|
||||
@@ -51,7 +38,6 @@ struct RDReaderPagingController {
|
||||
return pending
|
||||
}
|
||||
|
||||
/// 重置所有排队状态(用于故障修复)。
|
||||
mutating func resetPendingState() {
|
||||
isTransitioning = false
|
||||
pendingTransitionRequest = nil
|
||||
|
||||
@@ -1,49 +1,52 @@
|
||||
//
|
||||
// RDReaderPreloadController.swift
|
||||
// ReadViewSDK
|
||||
//
|
||||
// 文件职责:页面预加载控制器,负责提前渲染即将显示的页面视图以减少翻页卡顿。
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
/// 页面预加载控制器
|
||||
/// 负责管理页面视图的预加载和缓存,在当前页面基础上提前渲染前后若干页,
|
||||
/// 以减少翻页时的等待时间。支持仿真翻页和滚动模式下的不同复用策略。
|
||||
final class RDReaderPreloadController {
|
||||
/// 预加载半径,表示在当前页前后各预加载的页数,默认为 1
|
||||
|
||||
var radius: Int = 1
|
||||
|
||||
private let preloadHostView = UIView()
|
||||
|
||||
private var preloadedPageViews: [Int: UIView] = [:]
|
||||
|
||||
private var pageCurlCachedViews: [Int: UIView] = [:]
|
||||
|
||||
private var cacheSignature: CacheSignature?
|
||||
|
||||
/// 预加载环境参数,封装翻页模式、屏幕方向、页面尺寸等上下文信息
|
||||
struct Environment {
|
||||
|
||||
let displayType: RDReaderView.DisplayType
|
||||
|
||||
let isLandscape: Bool
|
||||
|
||||
let pagesPerScreen: Int
|
||||
|
||||
let boundsSize: CGSize
|
||||
|
||||
let landscapeDualPageEnabled: Bool
|
||||
|
||||
let coverPageIndex: Int?
|
||||
|
||||
let totalPages: Int
|
||||
|
||||
let spreadResolver: RDReaderSpreadResolver
|
||||
}
|
||||
|
||||
private struct CacheSignature: Equatable {
|
||||
|
||||
let displayType: RDReaderView.DisplayType
|
||||
|
||||
let isLandscape: Bool
|
||||
|
||||
let pagesPerScreen: Int
|
||||
|
||||
let boundsSize: CGSize
|
||||
}
|
||||
|
||||
/// 设置预加载宿主视图的 frame
|
||||
func setHostFrame(_ frame: CGRect) {
|
||||
preloadHostView.frame = frame
|
||||
}
|
||||
|
||||
/// 将预加载宿主视图添加到父视图中(如果尚未添加),置于最底层且不可见
|
||||
func ensureHostView(in parentView: UIView) {
|
||||
guard preloadHostView.superview == nil else { return }
|
||||
preloadHostView.isHidden = true
|
||||
@@ -54,12 +57,10 @@ final class RDReaderPreloadController {
|
||||
parentView.insertSubview(preloadHostView, at: 0)
|
||||
}
|
||||
|
||||
/// 根据当前环境初始化缓存签名,用于后续检测环境是否发生变化
|
||||
func initializeSignature(_ environment: Environment) {
|
||||
cacheSignature = currentCacheSignature(environment)
|
||||
}
|
||||
|
||||
/// 清除所有已缓存的页面视图并更新缓存签名
|
||||
func invalidate(environment: Environment) {
|
||||
pageCurlCachedViews.values.forEach { $0.removeFromSuperview() }
|
||||
preloadedPageViews.values.forEach { $0.removeFromSuperview() }
|
||||
@@ -68,7 +69,6 @@ final class RDReaderPreloadController {
|
||||
cacheSignature = currentCacheSignature(environment)
|
||||
}
|
||||
|
||||
/// 获取指定页码的页面视图用于显示,优先复用已缓存的视图
|
||||
func pageViewForDisplay(
|
||||
pageNum: Int,
|
||||
environment: Environment,
|
||||
@@ -80,14 +80,12 @@ final class RDReaderPreloadController {
|
||||
return view
|
||||
}
|
||||
|
||||
/// 取出指定页码的预加载视图并从缓存中移除,供外部复用
|
||||
func takePreloadedView(for pageNum: Int) -> UIView? {
|
||||
let preloaded = preloadedPageViews.removeValue(forKey: pageNum)
|
||||
preloaded?.removeFromSuperview()
|
||||
return preloaded
|
||||
}
|
||||
|
||||
/// 预加载当前页周围指定半径内的页面,清除不再需要的缓存视图
|
||||
func prime(
|
||||
around pageNum: Int,
|
||||
preferredForward: Bool? = nil,
|
||||
|
||||
@@ -1,17 +1,8 @@
|
||||
//
|
||||
// RDReaderSpreadResolver.swift
|
||||
// ReadViewSDK
|
||||
//
|
||||
// 文件职责:页面展开(spread)解析器,负责计算双页模式下的页面配对和翻页目标。
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// 页面展开解析器
|
||||
/// 处理横屏双页模式下的页码配对逻辑,包括封面页独占、双页对齐、
|
||||
/// 以及相邻页面跳转等计算。
|
||||
struct RDReaderSpreadResolver {
|
||||
/// 判断指定页码是否为全屏独占页面(如封面页在双页模式下独占一屏)
|
||||
|
||||
func isFullScreenPage(
|
||||
_ pageNum: Int,
|
||||
landscapeDualPageEnabled: Bool,
|
||||
@@ -22,8 +13,6 @@ struct RDReaderSpreadResolver {
|
||||
return pageNum == coverIndex
|
||||
}
|
||||
|
||||
/// 计算指定页码在双页模式下的左右页配对,返回 (左页, 右页?)。
|
||||
/// 封面页独占时单独处理,后续页面按两页一组配对。
|
||||
func dualPagePair(
|
||||
for pageNum: Int,
|
||||
totalPages: Int,
|
||||
@@ -45,7 +34,6 @@ struct RDReaderSpreadResolver {
|
||||
return (left, right)
|
||||
}
|
||||
|
||||
/// 计算从指定页码出发的下一个(或上一个)双页展开的起始页码
|
||||
func adjacentDualPage(
|
||||
from pageNum: Int,
|
||||
totalPages: Int,
|
||||
@@ -63,7 +51,6 @@ struct RDReaderSpreadResolver {
|
||||
return dualPagePair(for: prevEnd, totalPages: totalPages, coverPageIndex: coverPageIndex).left
|
||||
}
|
||||
|
||||
/// 计算下一页(或上一页)的页码,自动根据单页/双页模式选择合适的算法
|
||||
func nextPage(
|
||||
from currentPage: Int,
|
||||
totalPages: Int,
|
||||
|
||||
@@ -1,20 +1,8 @@
|
||||
//
|
||||
// RDReaderTapRegionHandler.swift
|
||||
// ReadViewSDK
|
||||
//
|
||||
// 文件职责:点击区域判定处理器,将屏幕点击坐标映射为翻页或工具栏操作事件。
|
||||
//
|
||||
|
||||
import CoreGraphics
|
||||
|
||||
/// 点击区域判定处理器
|
||||
/// 将屏幕三等分为左、中、右三个区域,根据点击位置和工具栏可见状态
|
||||
/// 决定触发上一页、下一页还是切换工具栏。
|
||||
struct RDReaderTapRegionHandler {
|
||||
/// 根据点击坐标和视图尺寸,解析点击事件类型
|
||||
/// - 左1/3区域:上一页(工具栏可见时转为 center)
|
||||
/// - 中1/3区域:切换工具栏
|
||||
/// - 右1/3区域:下一页(工具栏可见时转为 center)
|
||||
|
||||
func resolveTapEvent(
|
||||
point: CGPoint,
|
||||
viewFrame: CGRect,
|
||||
|
||||
Reference in New Issue
Block a user