SDK 化收尾: - 新增 RDPDFReaderView 本地 pod(阅读容器、成品控制器、划线/注释、画笔、 目录/设置面板、主题与持久化契约),Demo 精简为 PDFKit 页面提供者 + 持久化宿主 - Podfile/工程接入 RDPDFReaderView 与 SnapKit,示例书改用 PDF 阅读器示例文件 手机横屏单页竖滑修复: - cell 高度改为宽度适配后的纸张高度(sizeForItemAt + 宿主按页提供纵横比), 纵向超出一屏的内容由外层 collectionView 连续滚动承接,不再被裁掉 - 页面内部不再开启同向嵌套的兜底竖向拖动,修复外层滚动被吞、无法滚到下一页 - 修复 applyContentSize 先评估拖动开关后写 contentSize 的顺序缺陷 - 页码按视口中心命中 cell 计算,补 didEndDragging 兜底;旋转重锚点与 transitionToPage 改用真实布局位置 - 横竖屏同为竖滑时也重建 cell 并刷新全部可见页的适配配置 - 横屏单页点击不再跳页,只显隐工具栏;翻页交给连续竖滑 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
102 lines
4.5 KiB
Swift
102 lines
4.5 KiB
Swift
import XCTest
|
||
|
||
/// 固定布局(pre-paginated)书籍的旋转回归:
|
||
/// 每次旋转会使预加载缓存签名失效并批量丢弃 WKWebView 页面,
|
||
/// 历史上曾因 script message handler 保留环导致 WebView 泄漏累积,
|
||
/// 多次旋转后在 CA commit 阶段 abort(CA::Render::Encoder::grow)。
|
||
final class FixedLayoutRotationTests: XCTestCase {
|
||
private let app = XCUIApplication()
|
||
|
||
override func setUpWithError() throws {
|
||
continueAfterFailure = false
|
||
XCUIDevice.shared.orientation = .portrait
|
||
}
|
||
|
||
override func tearDownWithError() throws {
|
||
XCUIDevice.shared.orientation = .portrait
|
||
}
|
||
|
||
func testFixedLayoutBookSurvivesRepeatedRotation() {
|
||
app.launchAndOpenSampleBook(bookTitleQuery: "熊爷爷")
|
||
app.waitForReader()
|
||
|
||
for _ in 0..<3 {
|
||
XCUIDevice.shared.orientation = .landscapeLeft
|
||
RunLoop.current.run(until: Date().addingTimeInterval(2))
|
||
let landscapeState = app.waitForDemoReaderState(
|
||
timeout: 5,
|
||
description: "固定版式横屏保持单页"
|
||
) { $0.pagesPerScreen == 1 }
|
||
XCTAssertEqual(landscapeState.pagesPerScreen, 1, "交互型固定版式书横屏不应启用双页")
|
||
XCUIDevice.shared.orientation = .portrait
|
||
RunLoop.current.run(until: Date().addingTimeInterval(2))
|
||
}
|
||
|
||
XCTAssertEqual(app.state, .runningForeground, "反复旋转后 app 已退出(疑似崩溃)")
|
||
app.waitForReader()
|
||
}
|
||
|
||
/// PDF 手机横屏必须在旋转完成的第一轮布局中立即成为全宽单页。
|
||
/// 过去 flow layout 继续使用竖屏 itemSize,导致横屏首帧并排露出两页,
|
||
/// 只有点击触发下一轮布局后才恢复。
|
||
func testPDFLandscapeImmediatelyUsesSingleFullWidthPage() {
|
||
app.launchAndOpenSampleBook(
|
||
bookTitleQuery: "英汉大词典",
|
||
displayType: "pagecurl"
|
||
)
|
||
app.waitForReader()
|
||
|
||
XCUIDevice.shared.orientation = .landscapeLeft
|
||
|
||
let content = app.otherElements[IDs.readerContent].firstMatch
|
||
XCTAssertTrue(content.waitForExistence(timeout: 8), "PDF 阅读区域未出现")
|
||
let deadline = Date().addingTimeInterval(8)
|
||
while Date() < deadline {
|
||
let windowWidth = app.windows.firstMatch.frame.width
|
||
if windowWidth > 0, abs(content.frame.width - windowWidth) <= 1 {
|
||
break
|
||
}
|
||
RunLoop.current.run(until: Date().addingTimeInterval(0.1))
|
||
}
|
||
|
||
// 等待旋转动画和 PDF 页异步配置完成,避免只验证到过渡中的短暂正确状态。
|
||
RunLoop.current.run(until: Date().addingTimeInterval(1.5))
|
||
|
||
let windowFrame = app.windows.firstMatch.frame
|
||
XCTAssertGreaterThan(windowFrame.width, windowFrame.height, "设备没有进入横屏")
|
||
XCTAssertEqual(content.frame.width, windowFrame.width, accuracy: 1, "横屏首帧仍在使用竖屏页面宽度")
|
||
// 横屏单页按宽度适配:cell 高度是纸张按全宽换算的实际高度,竖版纸张
|
||
// 高于一屏,纵向超出部分由阅读器连续竖滑承接,因此不再等于窗口高度。
|
||
XCTAssertGreaterThanOrEqual(
|
||
content.frame.height,
|
||
windowFrame.height - 1,
|
||
"横屏单页高度应不小于一屏(宽度适配后的纸张高度)"
|
||
)
|
||
}
|
||
|
||
func testFixedLayoutCachedPageCanReturnAfterPageTurn() {
|
||
app.launchAndOpenSampleBook(bookTitleQuery: "熊爷爷")
|
||
app.waitForReader()
|
||
|
||
let content = app.otherElements[IDs.readerContent]
|
||
XCTAssertTrue(content.waitForExistence(timeout: 5), "固定版式阅读区域未出现")
|
||
let initialState = app.waitForDemoReaderState(timeout: 5, description: "固定版式初始页") {
|
||
$0.page != nil
|
||
}
|
||
let initialPage = initialState.page ?? 0
|
||
|
||
content.swipeLeft()
|
||
let nextState = app.waitForDemoReaderState(timeout: 8, description: "固定版式向后翻页") { state in
|
||
guard let page = state.page else { return false }
|
||
return page != initialPage
|
||
}
|
||
|
||
content.swipeRight()
|
||
let returnedState = app.waitForDemoReaderState(timeout: 8, description: "固定版式返回缓存页") {
|
||
$0.page == initialPage
|
||
}
|
||
XCTAssertNotEqual(nextState.page, returnedState.page, "固定版式返回缓存页后页码未恢复")
|
||
XCTAssertTrue(content.exists, "返回缓存页后阅读区域不应消失")
|
||
}
|
||
}
|