- 新增 RDEPUBResourceDataProvider 解密钩子协议 + 访问登记表,收敛章节 HTML/ 图片/内联 CSS/脚注/封面/图片查看器/正文取图等读取点,scheme handler 对加密书 禁用流式分支;新增 RDEPUBDecryptingImageAttachment 解密 DTCoreText 图片附件; RDEPUBReaderDependencies.live(resourceDataProvider:) 便捷注入。明文书零影响。 - 试读墙:configuration.trialPolicy + delegate epubReaderTrialWallView/ DidReachTrialWall,UI 由宿主提供(RDEPUBReaderController+Trial)。 - 工具栏定制:RDEPUBReaderTop/BottomToolViewProtocol 协议 + dependencies 工厂注入, 内置栏已 conform,configureTopToolView 改协议类型。 - 朝向锁定:RDEPUBMetadata.orientation(OPF rendition:orientation 主, iBooks display-options 兜底)+ RDEPUBReaderController+Orientation 重写支持朝向、 打开后主动转向。 注:RDEPUBReaderController+LocationResolution / PaginationCoordinator 为本次改动前 即存在的工作区修改,一并纳入。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
87 lines
3.5 KiB
Swift
87 lines
3.5 KiB
Swift
import UIKit
|
||
|
||
/// 试读墙支持。
|
||
///
|
||
/// 设计:`pageCountOfReaderView` 仍返回原始内容页数(内容页显示的总页数不受影响),
|
||
/// 仅 `numberOfPages(in:)` 在启用试读墙时 +1;试读墙页固定位于最后一个可读页之后。
|
||
/// 两种形态统一覆盖:
|
||
/// - readoor 独立试读 epub(整本都是试读章节):readableChapterCount 传 nil,墙追加在全书末尾;
|
||
/// - 整本 epub + 按章限制:readableChapterCount 传 N,超出章节页不展示(仅 bookPageMap 路径)。
|
||
extension RDEPUBReaderController {
|
||
|
||
/// 试读墙视图(首次访问经 delegate 获取并缓存)
|
||
var trialWallView: UIView? {
|
||
if let cached = cachedTrialWallView {
|
||
return cached
|
||
}
|
||
guard configuration.trialPolicy != nil else { return nil }
|
||
let view = delegate?.epubReaderTrialWallView(self)
|
||
cachedTrialWallView = view
|
||
return view
|
||
}
|
||
|
||
/// 是否启用试读墙:配置了试读策略且宿主提供了墙视图
|
||
var isTrialWallEnabled: Bool {
|
||
configuration.trialPolicy != nil && trialWallView != nil
|
||
}
|
||
|
||
/// 原始可读内容页数(不含试读墙页)。
|
||
/// readableChapterCount 指定且存在 bookPageMap 时按章截断,否则为全部内容页。
|
||
var readableContentPageCount: Int {
|
||
let rawCount = pageCountOfReaderView(readerView: readerView)
|
||
guard let policy = configuration.trialPolicy,
|
||
let readableChapterCount = policy.readableChapterCount,
|
||
readableChapterCount > 0,
|
||
let pageMap = readerContext.bookPageMap else {
|
||
return rawCount
|
||
}
|
||
// 第一个不可读章节(spineIndex >= readableChapterCount)的起始绝对页 = 可读页数
|
||
let firstLockedStart = pageMap.entries
|
||
.first { $0.spineIndex >= readableChapterCount }?
|
||
.absolutePageStart
|
||
return firstLockedStart ?? rawCount
|
||
}
|
||
|
||
/// 试读墙页的绝对索引(0-based);未启用时为 nil
|
||
var trialWallPageIndex: Int? {
|
||
guard isTrialWallEnabled else { return nil }
|
||
return readableContentPageCount
|
||
}
|
||
|
||
/// 供 RDReaderView 使用的有效页数:启用试读墙时为可读页数 + 1(墙页)
|
||
var effectiveNumberOfPages: Int {
|
||
guard isTrialWallEnabled else {
|
||
return pageCountOfReaderView(readerView: readerView)
|
||
}
|
||
return readableContentPageCount + 1
|
||
}
|
||
|
||
/// 构建试读墙页承载视图(铺满一页,居中放置宿主提供的墙视图)
|
||
func makeTrialWallPageView(reusableView: UIView?) -> UIView {
|
||
let container = (reusableView as? RDEPUBTrialWallContainerView) ?? RDEPUBTrialWallContainerView()
|
||
container.setWallView(trialWallView)
|
||
return container
|
||
}
|
||
}
|
||
|
||
/// 试读墙页容器:让宿主墙视图铺满整页
|
||
final class RDEPUBTrialWallContainerView: UIView {
|
||
|
||
private weak var wallView: UIView?
|
||
|
||
func setWallView(_ view: UIView?) {
|
||
guard wallView !== view else { return }
|
||
wallView?.removeFromSuperview()
|
||
guard let view else { return }
|
||
view.translatesAutoresizingMaskIntoConstraints = false
|
||
addSubview(view)
|
||
NSLayoutConstraint.activate([
|
||
view.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||
view.trailingAnchor.constraint(equalTo: trailingAnchor),
|
||
view.topAnchor.constraint(equalTo: topAnchor),
|
||
view.bottomAnchor.constraint(equalTo: bottomAnchor)
|
||
])
|
||
wallView = view
|
||
}
|
||
}
|