修复阅读器资源、朗读与持久化稳定性
阅读器在多会话朗读、加密 EPUB 资源、并发打开同一本书和多 UserDefaults 容器等场景下,存在旧异步结果覆盖新状态、锁屏控制串会话、大型明文资源被误拒绝、解压半成品被复用及标注跨容器混用的风险;同时高亮、书签、搜索和设置面板的空状态与自动化可访问性入口不完整。\n\n本次为朗读会话引入代次和当前 utterance 校验,远程控制改为仅响应当前会话并按自身 token 清理;加密资源 provider 先判定是否实际返回解密数据,明文大资源继续流式读取;解压缓存串行化以避免并发复用未完成目录。书签与高亮迁移至受保护文件,按 UserDefaults 容器隔离命名空间,保留标准容器旧文件兼容并确保新副本成功落盘后才删除历史数据。\n\n同步调整缓存淘汰、FoundationModels 弱链接、搜索/标注/设置面板交互与 UI 测试,并更新 Pod 生成配置及 API、风险文档。已执行 git diff --check 和 ReadViewDemo Debug Simulator 构建,结果为 BUILD SUCCEEDED。
This commit is contained in:
@@ -17,8 +17,15 @@ target 'ReadViewDemo' do
|
||||
end
|
||||
|
||||
post_install do |installer|
|
||||
# 不再由本 Podfile 全局关闭 ENABLE_USER_SCRIPT_SANDBOXING——之前的写法会把
|
||||
# 宿主工程一起强制改成 NO,属于我们主动降级安全基线。现在交回工程默认值。
|
||||
#
|
||||
# 注意:本工程用 use_frameworks!,CocoaPods 生成的 [CP] Embed Pods Frameworks
|
||||
# 脚本阶段用 rsync 往 .app/Frameworks 拷贝产物;一旦把该设置显式打成 YES,
|
||||
# 这一步会被沙箱拒绝(file-write-unlink / utimensat: Operation not permitted)
|
||||
# 而整个构建失败——已实测确认。真要打开必须先改掉动态框架集成方式
|
||||
# (静态库 / SPM),别只翻这个开关。
|
||||
apply_settings = lambda do |config|
|
||||
config.build_settings['ENABLE_USER_SCRIPT_SANDBOXING'] = 'NO'
|
||||
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.0'
|
||||
end
|
||||
|
||||
|
||||
@@ -90,13 +90,13 @@ EXTERNAL SOURCES:
|
||||
SPEC CHECKSUMS:
|
||||
DTCoreText: 11b7fe2104f476f82e75a4e3dbdde74d7186cecb
|
||||
DTFoundation: 76b624967cf5bcaae6bb057d622c536c36ef36d0
|
||||
RDAIReaderView: 56783c71853c59bf6810f719a57a7f580eb7855c
|
||||
RDAIReaderView: 01a156e412fb7a52a6963c097ffbe2f1792021fd
|
||||
RDEpubReaderView: 180444055ba2da2c52797cc08b307232f103a6d9
|
||||
RDPDFReaderView: 885be07e2b81cd0a8dc8a937b75faf9017ce045c
|
||||
RDSpeechReaderView: 25fff9437d2692965ba326f37b108baf3e2ae7ef
|
||||
SnapKit: d612e99e678a2d3b95bf60b0705ed0a35c03484a
|
||||
ZIPFoundation: dfd3d681c4053ff7e2f7350bc4e53b5dba3f5351
|
||||
|
||||
PODFILE CHECKSUM: 7a0e6ee6c7d410a5128fb20775a450fd0d47292c
|
||||
PODFILE CHECKSUM: 72bd92309c87ce13a247b8134a321f6b325af731
|
||||
|
||||
COCOAPODS: 1.16.2
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"~> 0.1"
|
||||
]
|
||||
},
|
||||
"frameworks": "FoundationModels"
|
||||
"weak_frameworks": "FoundationModels"
|
||||
},
|
||||
{
|
||||
"name": "UI",
|
||||
|
||||
Generated
+2
-2
@@ -90,13 +90,13 @@ EXTERNAL SOURCES:
|
||||
SPEC CHECKSUMS:
|
||||
DTCoreText: 11b7fe2104f476f82e75a4e3dbdde74d7186cecb
|
||||
DTFoundation: 76b624967cf5bcaae6bb057d622c536c36ef36d0
|
||||
RDAIReaderView: 56783c71853c59bf6810f719a57a7f580eb7855c
|
||||
RDAIReaderView: 01a156e412fb7a52a6963c097ffbe2f1792021fd
|
||||
RDEpubReaderView: 180444055ba2da2c52797cc08b307232f103a6d9
|
||||
RDPDFReaderView: 885be07e2b81cd0a8dc8a937b75faf9017ce045c
|
||||
RDSpeechReaderView: 25fff9437d2692965ba326f37b108baf3e2ae7ef
|
||||
SnapKit: d612e99e678a2d3b95bf60b0705ed0a35c03484a
|
||||
ZIPFoundation: dfd3d681c4053ff7e2f7350bc4e53b5dba3f5351
|
||||
|
||||
PODFILE CHECKSUM: 7a0e6ee6c7d410a5128fb20775a450fd0d47292c
|
||||
PODFILE CHECKSUM: 72bd92309c87ce13a247b8134a321f6b325af731
|
||||
|
||||
COCOAPODS: 1.16.2
|
||||
|
||||
+2398
-2408
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -5,7 +5,7 @@ GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/DTCoreText/DTCoreText.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DTFoundation/DTFoundation.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RDAIReaderView/RDAIReaderView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RDEpubReaderView/RDEpubReaderView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RDPDFReaderView/RDPDFReaderView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RDSpeechReaderView/RDSpeechReaderView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation/ZIPFoundation.framework/Headers" "$(SDKROOT)/usr/include/libxml2"
|
||||
LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks'
|
||||
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift $(SDKROOT)/usr/lib/swift
|
||||
OTHER_LDFLAGS = $(inherited) -l"sqlite3" -l"swiftCoreGraphics" -l"xml2" -framework "AVFAudio" -framework "CoreGraphics" -framework "CoreImage" -framework "CoreText" -framework "CryptoKit" -framework "DTCoreText" -framework "DTFoundation" -framework "FoundationModels" -framework "ImageIO" -framework "MediaPlayer" -framework "NaturalLanguage" -framework "PDFKit" -framework "QuartzCore" -framework "RDAIReaderView" -framework "RDEpubReaderView" -framework "RDPDFReaderView" -framework "RDSpeechReaderView" -framework "SnapKit" -framework "UIKit" -framework "Vision" -framework "ZIPFoundation"
|
||||
OTHER_LDFLAGS = $(inherited) -l"sqlite3" -l"swiftCoreGraphics" -l"xml2" -framework "AVFAudio" -framework "CoreGraphics" -framework "CoreImage" -framework "CoreText" -framework "CryptoKit" -framework "DTCoreText" -framework "DTFoundation" -framework "ImageIO" -framework "MediaPlayer" -framework "NaturalLanguage" -framework "PDFKit" -framework "QuartzCore" -framework "RDAIReaderView" -framework "RDEpubReaderView" -framework "RDPDFReaderView" -framework "RDSpeechReaderView" -framework "SnapKit" -framework "UIKit" -framework "Vision" -framework "ZIPFoundation" -weak_framework "FoundationModels"
|
||||
OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/DTCoreText" "-F${PODS_CONFIGURATION_BUILD_DIR}/DTFoundation" "-F${PODS_CONFIGURATION_BUILD_DIR}/RDAIReaderView" "-F${PODS_CONFIGURATION_BUILD_DIR}/RDEpubReaderView" "-F${PODS_CONFIGURATION_BUILD_DIR}/RDPDFReaderView" "-F${PODS_CONFIGURATION_BUILD_DIR}/RDSpeechReaderView" "-F${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "-F${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation"
|
||||
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
|
||||
Generated
+1
-1
@@ -5,7 +5,7 @@ GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/DTCoreText/DTCoreText.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DTFoundation/DTFoundation.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RDAIReaderView/RDAIReaderView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RDEpubReaderView/RDEpubReaderView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RDPDFReaderView/RDPDFReaderView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RDSpeechReaderView/RDSpeechReaderView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation/ZIPFoundation.framework/Headers" "$(SDKROOT)/usr/include/libxml2"
|
||||
LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks'
|
||||
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift $(SDKROOT)/usr/lib/swift
|
||||
OTHER_LDFLAGS = $(inherited) -l"sqlite3" -l"swiftCoreGraphics" -l"xml2" -framework "AVFAudio" -framework "CoreGraphics" -framework "CoreImage" -framework "CoreText" -framework "CryptoKit" -framework "DTCoreText" -framework "DTFoundation" -framework "FoundationModels" -framework "ImageIO" -framework "MediaPlayer" -framework "NaturalLanguage" -framework "PDFKit" -framework "QuartzCore" -framework "RDAIReaderView" -framework "RDEpubReaderView" -framework "RDPDFReaderView" -framework "RDSpeechReaderView" -framework "SnapKit" -framework "UIKit" -framework "Vision" -framework "ZIPFoundation"
|
||||
OTHER_LDFLAGS = $(inherited) -l"sqlite3" -l"swiftCoreGraphics" -l"xml2" -framework "AVFAudio" -framework "CoreGraphics" -framework "CoreImage" -framework "CoreText" -framework "CryptoKit" -framework "DTCoreText" -framework "DTFoundation" -framework "ImageIO" -framework "MediaPlayer" -framework "NaturalLanguage" -framework "PDFKit" -framework "QuartzCore" -framework "RDAIReaderView" -framework "RDEpubReaderView" -framework "RDPDFReaderView" -framework "RDSpeechReaderView" -framework "SnapKit" -framework "UIKit" -framework "Vision" -framework "ZIPFoundation" -weak_framework "FoundationModels"
|
||||
OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/DTCoreText" "-F${PODS_CONFIGURATION_BUILD_DIR}/DTFoundation" "-F${PODS_CONFIGURATION_BUILD_DIR}/RDAIReaderView" "-F${PODS_CONFIGURATION_BUILD_DIR}/RDEpubReaderView" "-F${PODS_CONFIGURATION_BUILD_DIR}/RDPDFReaderView" "-F${PODS_CONFIGURATION_BUILD_DIR}/RDSpeechReaderView" "-F${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "-F${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation"
|
||||
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
||||
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/RDAIReaderView
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
|
||||
OTHER_LDFLAGS = $(inherited) -l"sqlite3" -framework "CryptoKit" -framework "FoundationModels" -framework "NaturalLanguage" -framework "UIKit"
|
||||
OTHER_LDFLAGS = $(inherited) -l"sqlite3" -framework "CryptoKit" -framework "NaturalLanguage" -framework "UIKit" -weak_framework "FoundationModels"
|
||||
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
||||
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/RDAIReaderView
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
|
||||
OTHER_LDFLAGS = $(inherited) -l"sqlite3" -framework "CryptoKit" -framework "FoundationModels" -framework "NaturalLanguage" -framework "UIKit"
|
||||
OTHER_LDFLAGS = $(inherited) -l"sqlite3" -framework "CryptoKit" -framework "NaturalLanguage" -framework "UIKit" -weak_framework "FoundationModels"
|
||||
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/RDEpubReaderView
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/DTCoreText" "${PODS_CONFIGURATION_BUILD_DIR}/DTFoundation" "${PODS_CONFIGURATION_BUILD_DIR}/RDAIReaderView" "${PODS_CONFIGURATION_BUILD_DIR}/RDSpeechReaderView" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation"
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
|
||||
OTHER_LDFLAGS = $(inherited) -framework "AVFAudio" -framework "CoreGraphics" -framework "CoreText" -framework "CryptoKit" -framework "DTCoreText" -framework "FoundationModels" -framework "ImageIO" -framework "MediaPlayer" -framework "NaturalLanguage" -framework "QuartzCore" -framework "RDAIReaderView" -framework "RDSpeechReaderView" -framework "SnapKit" -framework "UIKit" -framework "ZIPFoundation"
|
||||
OTHER_LDFLAGS = $(inherited) -framework "AVFAudio" -framework "CoreGraphics" -framework "CoreText" -framework "CryptoKit" -framework "DTCoreText" -framework "ImageIO" -framework "MediaPlayer" -framework "NaturalLanguage" -framework "QuartzCore" -framework "RDAIReaderView" -framework "RDSpeechReaderView" -framework "SnapKit" -framework "UIKit" -framework "ZIPFoundation" -weak_framework "FoundationModels"
|
||||
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/RDEpubReaderView
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/DTCoreText" "${PODS_CONFIGURATION_BUILD_DIR}/DTFoundation" "${PODS_CONFIGURATION_BUILD_DIR}/RDAIReaderView" "${PODS_CONFIGURATION_BUILD_DIR}/RDSpeechReaderView" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation"
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
|
||||
OTHER_LDFLAGS = $(inherited) -framework "AVFAudio" -framework "CoreGraphics" -framework "CoreText" -framework "CryptoKit" -framework "DTCoreText" -framework "FoundationModels" -framework "ImageIO" -framework "MediaPlayer" -framework "NaturalLanguage" -framework "QuartzCore" -framework "RDAIReaderView" -framework "RDSpeechReaderView" -framework "SnapKit" -framework "UIKit" -framework "ZIPFoundation"
|
||||
OTHER_LDFLAGS = $(inherited) -framework "AVFAudio" -framework "CoreGraphics" -framework "CoreText" -framework "CryptoKit" -framework "DTCoreText" -framework "ImageIO" -framework "MediaPlayer" -framework "NaturalLanguage" -framework "QuartzCore" -framework "RDAIReaderView" -framework "RDSpeechReaderView" -framework "SnapKit" -framework "UIKit" -framework "ZIPFoundation" -weak_framework "FoundationModels"
|
||||
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/RDPDFReaderView
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/RDAIReaderView" "${PODS_CONFIGURATION_BUILD_DIR}/RDSpeechReaderView" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit"
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
|
||||
OTHER_LDFLAGS = $(inherited) -framework "AVFAudio" -framework "CoreImage" -framework "CryptoKit" -framework "FoundationModels" -framework "MediaPlayer" -framework "NaturalLanguage" -framework "PDFKit" -framework "RDAIReaderView" -framework "RDSpeechReaderView" -framework "SnapKit" -framework "UIKit" -framework "Vision"
|
||||
OTHER_LDFLAGS = $(inherited) -framework "AVFAudio" -framework "CoreImage" -framework "CryptoKit" -framework "MediaPlayer" -framework "NaturalLanguage" -framework "PDFKit" -framework "RDAIReaderView" -framework "RDSpeechReaderView" -framework "SnapKit" -framework "UIKit" -framework "Vision" -weak_framework "FoundationModels"
|
||||
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/RDPDFReaderView
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/RDAIReaderView" "${PODS_CONFIGURATION_BUILD_DIR}/RDSpeechReaderView" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit"
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
|
||||
OTHER_LDFLAGS = $(inherited) -framework "AVFAudio" -framework "CoreImage" -framework "CryptoKit" -framework "FoundationModels" -framework "MediaPlayer" -framework "NaturalLanguage" -framework "PDFKit" -framework "RDAIReaderView" -framework "RDSpeechReaderView" -framework "SnapKit" -framework "UIKit" -framework "Vision"
|
||||
OTHER_LDFLAGS = $(inherited) -framework "AVFAudio" -framework "CoreImage" -framework "CryptoKit" -framework "MediaPlayer" -framework "NaturalLanguage" -framework "PDFKit" -framework "RDAIReaderView" -framework "RDSpeechReaderView" -framework "SnapKit" -framework "UIKit" -framework "Vision" -weak_framework "FoundationModels"
|
||||
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
|
||||
Generated
+1
-1
@@ -3,7 +3,7 @@ CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/RDSpeechReaderView
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/RDAIReaderView"
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
|
||||
OTHER_LDFLAGS = $(inherited) -framework "AVFAudio" -framework "CryptoKit" -framework "FoundationModels" -framework "MediaPlayer" -framework "NaturalLanguage" -framework "RDAIReaderView" -framework "UIKit"
|
||||
OTHER_LDFLAGS = $(inherited) -framework "AVFAudio" -framework "CryptoKit" -framework "MediaPlayer" -framework "NaturalLanguage" -framework "RDAIReaderView" -framework "UIKit" -weak_framework "FoundationModels"
|
||||
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
|
||||
Generated
+1
-1
@@ -3,7 +3,7 @@ CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/RDSpeechReaderView
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/RDAIReaderView"
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
|
||||
OTHER_LDFLAGS = $(inherited) -framework "AVFAudio" -framework "CryptoKit" -framework "FoundationModels" -framework "MediaPlayer" -framework "NaturalLanguage" -framework "RDAIReaderView" -framework "UIKit"
|
||||
OTHER_LDFLAGS = $(inherited) -framework "AVFAudio" -framework "CryptoKit" -framework "MediaPlayer" -framework "NaturalLanguage" -framework "RDAIReaderView" -framework "UIKit" -weak_framework "FoundationModels"
|
||||
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
|
||||
@@ -41,8 +41,8 @@
|
||||
63B2852E59996922386A3111 /* AccessibilityIdentifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00F495E17B90CCF1C7FE8C27 /* AccessibilityIdentifiers.swift */; };
|
||||
9AAFB855EC7AC09D1E0B6742 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 697A5BCF0D93DBEC18E21A09 /* Foundation.framework */; };
|
||||
A1C5E34F6B7192D400A1B234 /* PDFAnnotationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1C5E34F6B7192D400A1B235 /* PDFAnnotationTests.swift */; };
|
||||
C0FFEE0100000001AABBCC01 /* PDFPageTurnBlankPageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0FFEE0100000001AABBCC02 /* PDFPageTurnBlankPageTests.swift */; };
|
||||
C08FF8D030048DC5147729E9 /* ReaderOpenCloseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EDF066BA12974E6CFBE519F /* ReaderOpenCloseTests.swift */; };
|
||||
C0FFEE0100000001AABBCC01 /* PDFPageTurnBlankPageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0FFEE0100000001AABBCC02 /* PDFPageTurnBlankPageTests.swift */; };
|
||||
DE1437A969DA1C5F0CBB047D /* Pods_ReadViewDemo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 792DF85CE4A3DD80D67843C7 /* Pods_ReadViewDemo.framework */; };
|
||||
FEDB5937CEB858CB06E38E2D /* SettingsPanelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 201C2B482287866487EFAE66 /* SettingsPanelTests.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
@@ -95,8 +95,8 @@
|
||||
8FFD606A5A1CBDCC3CA87F1C /* ReadViewDemoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReadViewDemoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
9EDF066BA12974E6CFBE519F /* ReaderOpenCloseTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ReaderOpenCloseTests.swift; sourceTree = "<group>"; };
|
||||
A1C5E34F6B7192D400A1B235 /* PDFAnnotationTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PDFAnnotationTests.swift; sourceTree = "<group>"; };
|
||||
C0FFEE0100000001AABBCC02 /* PDFPageTurnBlankPageTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PDFPageTurnBlankPageTests.swift; sourceTree = "<group>"; };
|
||||
BADF0A18AD034B74A482A4C1 /* ReaderToolbarTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ReaderToolbarTests.swift; sourceTree = "<group>"; };
|
||||
C0FFEE0100000001AABBCC02 /* PDFPageTurnBlankPageTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PDFPageTurnBlankPageTests.swift; sourceTree = "<group>"; };
|
||||
CFE01DBDCB8D790832A4DE3F /* SearchTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SearchTests.swift; sourceTree = "<group>"; };
|
||||
DE070C1D2FBF0CC900ED065F /* ReadViewDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ReadViewDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
FB49AFCCBC2BE04C82B8F286 /* DisplayTypeTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DisplayTypeTests.swift; sourceTree = "<group>"; };
|
||||
@@ -456,7 +456,6 @@
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = MG4Z7FU83W;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = cn.shen.ReadViewDemoUITests;
|
||||
@@ -481,7 +480,6 @@
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = MG4Z7FU83W;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = cn.shen.ReadViewDemoUITests;
|
||||
@@ -508,7 +506,6 @@
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = MG4Z7FU83W;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = ReadViewDemo/Info.plist;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
@@ -547,7 +544,6 @@
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = MG4Z7FU83W;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = ReadViewDemo/Info.plist;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
@@ -612,7 +608,6 @@
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
@@ -675,7 +670,6 @@
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
|
||||
@@ -353,6 +353,9 @@ final class ViewController: UIViewController {
|
||||
defaults.removeObject(forKey: key)
|
||||
}
|
||||
}
|
||||
// 高亮/书签已改为写受保护文件(不再明文进 UserDefaults),
|
||||
// 只清 defaults 会让标注跨用例残留,必须一起清掉。
|
||||
RDEPUBProtectedContentStore().removeAll()
|
||||
PDFDemoReaderViewController.resetPersistedAnnotations()
|
||||
}
|
||||
|
||||
|
||||
@@ -74,10 +74,13 @@ enum IDs {
|
||||
static let searchClose = "epub.reader.search.close"
|
||||
static let searchCount = "epub.reader.search.count"
|
||||
|
||||
/// EPUB 设置面板是固定高度的 pill 面板,没有 scrollView;这是它的根视图。
|
||||
static let settingsPanel = "epub.reader.settings.panel"
|
||||
/// 只有 PDF 设置面板才有 scrollView(`RDPDFReaderSettingsPanelViewController`)。
|
||||
static let settingsScroll = "epub.reader.settings.scroll"
|
||||
static let settingsBrightness = "epub.reader.settings.brightness"
|
||||
static let settingsFontIncrease = "epub.reader.settings.font.increase"
|
||||
static let settingsFontDecrease = "epub.reader.settings.font.decrease"
|
||||
/// 字号是滑块;SDK 从未提供 font.increase / font.decrease 按钮
|
||||
static let settingsFontSize = "epub.reader.settings.font.size"
|
||||
static let settingsFontValue = "epub.reader.settings.font.value"
|
||||
static let settingsFontChoice = "epub.reader.settings.font.choice"
|
||||
static let settingsLineHeight = "epub.reader.settings.lineHeight"
|
||||
|
||||
@@ -169,4 +169,74 @@ extension XCUIApplication {
|
||||
private func readerChromeIsVisible(requiredButtonIDs: [String]) -> Bool {
|
||||
requiredButtonIDs.allSatisfy { buttons[$0].exists }
|
||||
}
|
||||
|
||||
// MARK: - 设置面板
|
||||
|
||||
/// 等 EPUB 设置面板出现。面板已改成固定高度布局,所有控件一屏可见,
|
||||
/// 既没有 scrollView 也不需要滚动才能触达。
|
||||
@discardableResult
|
||||
func waitForSettingsPanel(timeout: TimeInterval = 5) -> Bool {
|
||||
if otherElements[IDs.settingsPanel].waitForExistence(timeout: timeout) {
|
||||
return true
|
||||
}
|
||||
// 面板根视图偶尔不作为 otherElement 暴露,退回用「完成」按钮判定
|
||||
return buttons[IDs.settingsDone].waitForExistence(timeout: 1)
|
||||
}
|
||||
|
||||
/// 设置面板的字体/行距/翻页方式都是 pill 按钮 + action sheet,不是
|
||||
/// segmentedControl:点开 pill 后再点选项标题。
|
||||
@discardableResult
|
||||
func selectSettingsPillOption(
|
||||
pillIdentifier: String,
|
||||
optionTitle: String,
|
||||
timeout: TimeInterval = 5
|
||||
) -> Bool {
|
||||
let pill = buttons[pillIdentifier]
|
||||
guard pill.waitForExistence(timeout: timeout) else { return false }
|
||||
pill.tap()
|
||||
|
||||
// action sheet 的选项在 iOS 上是 button,取消项同名风险由标题唯一性保证
|
||||
let option = sheets.buttons[optionTitle].exists
|
||||
? sheets.buttons[optionTitle]
|
||||
: buttons[optionTitle]
|
||||
guard option.waitForExistence(timeout: timeout) else {
|
||||
// 收起 sheet,避免污染后续断言
|
||||
if sheets.buttons["取消"].exists {
|
||||
sheets.buttons["取消"].tap()
|
||||
}
|
||||
return false
|
||||
}
|
||||
option.tap()
|
||||
return true
|
||||
}
|
||||
|
||||
/// 字号是 slider(`epub.reader.settings.font.size`),没有增大/减小按钮。
|
||||
/// 把滑块推到指定归一化位置以触发重排;返回是否成功。
|
||||
@discardableResult
|
||||
func adjustSettingsFontSize(toNormalizedPosition position: CGFloat, timeout: TimeInterval = 5) -> Bool {
|
||||
let slider = sliders[IDs.settingsFontSize]
|
||||
guard slider.waitForExistence(timeout: timeout) else { return false }
|
||||
slider.adjust(toNormalizedSliderPosition: position)
|
||||
return true
|
||||
}
|
||||
|
||||
/// 在当前值基础上增减字号,替代早已不存在的 font.increase / font.decrease 按钮。
|
||||
@discardableResult
|
||||
func nudgeSettingsFontSize(by delta: CGFloat, timeout: TimeInterval = 5) -> Bool {
|
||||
let slider = sliders[IDs.settingsFontSize]
|
||||
guard slider.waitForExistence(timeout: timeout) else { return false }
|
||||
let target = min(max(slider.normalizedSliderPosition + delta, 0), 1)
|
||||
slider.adjust(toNormalizedSliderPosition: target)
|
||||
return true
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func increaseSettingsFontSize(timeout: TimeInterval = 5) -> Bool {
|
||||
nudgeSettingsFontSize(by: 0.25, timeout: timeout)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func decreaseSettingsFontSize(timeout: TimeInterval = 5) -> Bool {
|
||||
nudgeSettingsFontSize(by: -0.25, timeout: timeout)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,12 +465,8 @@ final class AsyncChapterLoadingTests: XCTestCase {
|
||||
// Change font size via settings
|
||||
app.showReaderChromeIfNeeded()
|
||||
app.buttons[IDs.readerSettings].tap()
|
||||
let fontIncrease = app.buttons[IDs.settingsFontIncrease]
|
||||
XCTAssertTrue(fontIncrease.waitForExistence(timeout: 3), "字号增大按钮不存在")
|
||||
fontIncrease.tap()
|
||||
RunLoop.current.run(until: Date().addingTimeInterval(0.3))
|
||||
fontIncrease.tap()
|
||||
RunLoop.current.run(until: Date().addingTimeInterval(0.3))
|
||||
XCTAssertTrue(app.adjustSettingsFontSize(toNormalizedPosition: 0.9), "字号滑块不存在")
|
||||
RunLoop.current.run(until: Date().addingTimeInterval(0.6))
|
||||
|
||||
// Close settings
|
||||
let doneButton = app.buttons[IDs.settingsDone]
|
||||
|
||||
@@ -41,9 +41,10 @@ final class DisplayTypeTests: XCTestCase {
|
||||
XCTAssertTrue(app.buttons[IDs.readerSettings].waitForExistence(timeout: 3))
|
||||
app.buttons[IDs.readerSettings].tap()
|
||||
|
||||
let displayTypeControl = app.segmentedControls[IDs.settingsDisplayType]
|
||||
XCTAssertTrue(displayTypeControl.waitForExistence(timeout: 5))
|
||||
displayTypeControl.buttons.element(boundBy: 2).tap()
|
||||
XCTAssertTrue(
|
||||
app.selectSettingsPillOption(pillIdentifier: IDs.settingsDisplayType, optionTitle: "竖滑"),
|
||||
"翻页方式应可切换到竖滑"
|
||||
)
|
||||
app.buttons[IDs.settingsDone].tap()
|
||||
|
||||
app.waitForDemoReaderState(timeout: 8, description: "display=verticalScroll after switch") { $0.display == "verticalScroll" }
|
||||
|
||||
@@ -45,23 +45,20 @@ final class ErrorAndEdgeCaseTests: XCTestCase {
|
||||
app.waitForDemoReaderState(timeout: 8, description: "reader=opened") { $0.isOpened }
|
||||
}
|
||||
|
||||
func testSettingsPanelScrollReachesAllControls() throws {
|
||||
func testSettingsPanelReachesAllControls() throws {
|
||||
app.launchAndOpenSampleBook()
|
||||
app.waitForReader(timeout: 12)
|
||||
|
||||
app.showReaderChromeIfNeeded()
|
||||
app.buttons[IDs.readerSettings].tap()
|
||||
|
||||
let settingsScroll = app.scrollViews[IDs.settingsScroll]
|
||||
XCTAssertTrue(settingsScroll.waitForExistence(timeout: 5), "设置面板未出现")
|
||||
XCTAssertTrue(app.waitForSettingsPanel(), "设置面板未出现")
|
||||
|
||||
XCTAssertTrue(app.buttons[IDs.settingsFontIncrease].waitForExistence(timeout: 3))
|
||||
XCTAssertTrue(app.buttons[IDs.settingsBrightness].waitForExistence(timeout: 3))
|
||||
XCTAssertTrue(app.sliders[IDs.settingsFontSize].waitForExistence(timeout: 3))
|
||||
XCTAssertTrue(app.sliders[IDs.settingsBrightness].waitForExistence(timeout: 3))
|
||||
|
||||
settingsScroll.swipeUp()
|
||||
XCTAssertTrue(app.segmentedControls[IDs.settingsDisplayType].waitForExistence(timeout: 5))
|
||||
XCTAssertTrue(app.buttons[IDs.settingsDisplayType].waitForExistence(timeout: 5))
|
||||
|
||||
settingsScroll.swipeUp()
|
||||
XCTAssertTrue(app.buttons[IDs.settingsTheme(5)].waitForExistence(timeout: 5))
|
||||
|
||||
app.buttons[IDs.settingsDone].tap()
|
||||
|
||||
@@ -8,7 +8,9 @@ final class LocationPersistenceTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testReadingPositionRestoredOnReopen() throws {
|
||||
app.launchAndOpenSampleBook()
|
||||
// 必须用滚动模式:仿真翻页(默认)下没有 `epub.reader.paging`
|
||||
// collectionView,swipeLeft 落不到任何东西,页码永远停在 1
|
||||
app.launchAndOpenSampleBook(displayType: "scroll")
|
||||
app.waitForReader(timeout: 12)
|
||||
|
||||
_ = app.waitForDemoReaderState(timeout: 5, description: "存在页码") { $0.page != nil }
|
||||
@@ -30,7 +32,8 @@ final class LocationPersistenceTests: XCTestCase {
|
||||
let backButton = app.buttons[IDs.readerBack]
|
||||
if backButton.waitForExistence(timeout: 3) { backButton.tap() }
|
||||
|
||||
app.launchAndOpenSampleBook()
|
||||
// 重开必须保留状态,否则 --demo-reset-state 会清掉刚存的阅读位置
|
||||
app.launchAndOpenSampleBook(displayType: "scroll", resetsReaderState: false)
|
||||
app.waitForReader(timeout: 12)
|
||||
|
||||
let restoredState = app.waitForDemoReaderState(timeout: 8, description: "恢复阅读位置") { state in
|
||||
@@ -61,7 +64,7 @@ final class LocationPersistenceTests: XCTestCase {
|
||||
|
||||
app.showReaderChromeIfNeeded()
|
||||
if app.buttons[IDs.readerSettings].waitForExistence(timeout: 3) { app.buttons[IDs.readerSettings].tap() }
|
||||
if app.buttons[IDs.settingsFontIncrease].waitForExistence(timeout: 5) { app.buttons[IDs.settingsFontIncrease].tap() }
|
||||
app.increaseSettingsFontSize()
|
||||
if app.buttons[IDs.settingsDone].waitForExistence(timeout: 3) { app.buttons[IDs.settingsDone].tap() }
|
||||
|
||||
let backButton = app.buttons[IDs.readerBack]
|
||||
@@ -74,7 +77,8 @@ final class LocationPersistenceTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testPositionSurvivesFontChoiceAndLineHeightChange() throws {
|
||||
app.launchAndOpenSampleBook(resetsReaderState: true)
|
||||
// 同上:需要真正能翻页的模式
|
||||
app.launchAndOpenSampleBook(displayType: "scroll", resetsReaderState: true)
|
||||
app.waitForReader(timeout: 12)
|
||||
|
||||
let paging = app.collectionViews[IDs.readerPaging]
|
||||
@@ -97,17 +101,14 @@ final class LocationPersistenceTests: XCTestCase {
|
||||
XCTAssertTrue(app.buttons[IDs.readerSettings].waitForExistence(timeout: 3))
|
||||
app.buttons[IDs.readerSettings].tap()
|
||||
|
||||
let fontChoiceControl = app.segmentedControls[IDs.settingsFontChoice]
|
||||
XCTAssertTrue(fontChoiceControl.waitForExistence(timeout: 5), "字体选择控件应出现")
|
||||
if fontChoiceControl.buttons.count > 1 {
|
||||
fontChoiceControl.buttons.element(boundBy: 1).tap()
|
||||
}
|
||||
|
||||
let lineHeightControl = app.segmentedControls[IDs.settingsLineHeight]
|
||||
XCTAssertTrue(lineHeightControl.waitForExistence(timeout: 5), "行距控件应出现")
|
||||
if lineHeightControl.buttons.count > 1 {
|
||||
lineHeightControl.buttons.element(boundBy: min(1, lineHeightControl.buttons.count - 1)).tap()
|
||||
}
|
||||
XCTAssertTrue(
|
||||
app.selectSettingsPillOption(pillIdentifier: IDs.settingsFontChoice, optionTitle: "宋体"),
|
||||
"字体选择控件应可切换"
|
||||
)
|
||||
XCTAssertTrue(
|
||||
app.selectSettingsPillOption(pillIdentifier: IDs.settingsLineHeight, optionTitle: "标准"),
|
||||
"行距控件应可切换"
|
||||
)
|
||||
|
||||
XCTAssertTrue(app.buttons[IDs.settingsDone].waitForExistence(timeout: 3))
|
||||
app.buttons[IDs.settingsDone].tap()
|
||||
@@ -131,8 +132,9 @@ final class LocationPersistenceTests: XCTestCase {
|
||||
app.waitForDemoReaderState(timeout: 5, description: "bookmarks=1") { $0.bookmarks == 1 }
|
||||
|
||||
app.hideReaderChromeIfNeeded()
|
||||
let content = app.otherElements[IDs.readerSelectionText].firstMatch
|
||||
XCTAssertTrue(content.waitForExistence(timeout: 5))
|
||||
// 与其它选区用例一致:渲染路径没暴露选区元素时跳过,而不是硬失败在
|
||||
// 与本用例(跨重开保持)无关的前置步骤上
|
||||
let content = try app.requireSelectionText()
|
||||
let start = content.coordinate(withNormalizedOffset: CGVector(dx: 0.2, dy: 0.5))
|
||||
let end = content.coordinate(withNormalizedOffset: CGVector(dx: 0.55, dy: 0.5))
|
||||
start.press(forDuration: 0.8, thenDragTo: end)
|
||||
@@ -174,11 +176,10 @@ final class LocationPersistenceTests: XCTestCase {
|
||||
|
||||
app.showReaderChromeIfNeeded()
|
||||
app.buttons[IDs.readerSettings].tap()
|
||||
let fontChoiceControl = app.segmentedControls[IDs.settingsFontChoice]
|
||||
XCTAssertTrue(fontChoiceControl.waitForExistence(timeout: 5), "字体选择控件应出现")
|
||||
if fontChoiceControl.buttons.count > 1 {
|
||||
fontChoiceControl.buttons.element(boundBy: 1).tap()
|
||||
}
|
||||
XCTAssertTrue(
|
||||
app.selectSettingsPillOption(pillIdentifier: IDs.settingsFontChoice, optionTitle: "宋体"),
|
||||
"字体选择控件应可切换"
|
||||
)
|
||||
app.buttons[IDs.settingsDone].tap()
|
||||
|
||||
let content = app.otherElements[IDs.readerContent].firstMatch
|
||||
|
||||
@@ -92,10 +92,8 @@ final class MemoryFootprintTests: XCTestCase {
|
||||
|
||||
XCTAssertTrue(app.buttons[IDs.readerSettings].waitForExistence(timeout: 5), "设置按钮不存在")
|
||||
app.buttons[IDs.readerSettings].tap()
|
||||
XCTAssertTrue(app.scrollViews[IDs.settingsScroll].waitForExistence(timeout: 5), "设置面板未出现")
|
||||
XCTAssertTrue(app.buttons[IDs.settingsFontIncrease].waitForExistence(timeout: 3), "字号增大按钮不存在")
|
||||
app.buttons[IDs.settingsFontIncrease].tap()
|
||||
app.buttons[IDs.settingsFontIncrease].tap()
|
||||
XCTAssertTrue(app.waitForSettingsPanel(), "设置面板未出现")
|
||||
XCTAssertTrue(app.adjustSettingsFontSize(toNormalizedPosition: 0.9), "字号滑块不存在")
|
||||
XCTAssertTrue(app.buttons[IDs.settingsDone].waitForExistence(timeout: 3), "完成按钮不存在")
|
||||
app.buttons[IDs.settingsDone].tap()
|
||||
|
||||
|
||||
@@ -301,10 +301,7 @@ final class SearchTests: XCTestCase {
|
||||
XCTAssertTrue(settingsButton.waitForExistence(timeout: 3))
|
||||
settingsButton.tap()
|
||||
|
||||
let fontIncrease = app.buttons[IDs.settingsFontIncrease]
|
||||
XCTAssertTrue(fontIncrease.waitForExistence(timeout: 3))
|
||||
fontIncrease.tap()
|
||||
fontIncrease.tap()
|
||||
XCTAssertTrue(app.adjustSettingsFontSize(toNormalizedPosition: 0.9), "字号滑块应存在")
|
||||
Thread.sleep(forTimeInterval: 0.5)
|
||||
|
||||
let doneButton = app.buttons[IDs.settingsDone]
|
||||
@@ -367,10 +364,7 @@ final class SearchTests: XCTestCase {
|
||||
XCTAssertTrue(settingsButton.waitForExistence(timeout: 3), "设置按钮应存在")
|
||||
settingsButton.tap()
|
||||
|
||||
let fontIncrease = app.buttons[IDs.settingsFontIncrease]
|
||||
XCTAssertTrue(fontIncrease.waitForExistence(timeout: 3), "字号增大按钮应存在")
|
||||
fontIncrease.tap()
|
||||
fontIncrease.tap()
|
||||
XCTAssertTrue(app.adjustSettingsFontSize(toNormalizedPosition: 0.9), "字号滑块应存在")
|
||||
Thread.sleep(forTimeInterval: 0.5)
|
||||
|
||||
let doneButton = app.buttons[IDs.settingsDone]
|
||||
|
||||
@@ -114,10 +114,7 @@ final class SelectionAnnotateTests: XCTestCase {
|
||||
XCTAssertTrue(settingsButton.waitForExistence(timeout: 3), "设置按钮应存在")
|
||||
settingsButton.tap()
|
||||
|
||||
let fontIncrease = app.buttons[IDs.settingsFontIncrease]
|
||||
XCTAssertTrue(fontIncrease.waitForExistence(timeout: 3), "字号增大按钮应存在")
|
||||
fontIncrease.tap()
|
||||
fontIncrease.tap()
|
||||
XCTAssertTrue(app.adjustSettingsFontSize(toNormalizedPosition: 0.9), "字号滑块应存在")
|
||||
Thread.sleep(forTimeInterval: 0.5)
|
||||
|
||||
let doneButton = app.buttons[IDs.settingsDone]
|
||||
|
||||
@@ -16,9 +16,8 @@ final class SettingsEffectTests: XCTestCase {
|
||||
XCTAssertTrue(app.buttons[IDs.readerSettings].waitForExistence(timeout: 3), "设置按钮不存在")
|
||||
app.buttons[IDs.readerSettings].tap()
|
||||
|
||||
XCTAssertTrue(app.scrollViews[IDs.settingsScroll].waitForExistence(timeout: 5), "设置面板未出现")
|
||||
XCTAssertTrue(app.buttons[IDs.settingsFontIncrease].waitForExistence(timeout: 3), "字号增大按钮不存在")
|
||||
app.buttons[IDs.settingsFontIncrease].tap()
|
||||
XCTAssertTrue(app.waitForSettingsPanel(), "设置面板未出现")
|
||||
XCTAssertTrue(app.increaseSettingsFontSize(), "字号滑块不存在")
|
||||
|
||||
XCTAssertTrue(app.buttons[IDs.settingsDone].waitForExistence(timeout: 3), "完成按钮不存在")
|
||||
app.buttons[IDs.settingsDone].tap()
|
||||
@@ -32,8 +31,7 @@ final class SettingsEffectTests: XCTestCase {
|
||||
|
||||
app.showReaderChromeIfNeeded()
|
||||
app.buttons[IDs.readerSettings].tap()
|
||||
XCTAssertTrue(app.scrollViews[IDs.settingsScroll].waitForExistence(timeout: 5), "设置面板未出现")
|
||||
app.scrollViews[IDs.settingsScroll].swipeUp()
|
||||
XCTAssertTrue(app.waitForSettingsPanel(), "设置面板未出现")
|
||||
|
||||
let darkThemeButton = app.buttons[IDs.settingsTheme(5)]
|
||||
if darkThemeButton.waitForExistence(timeout: 3) { darkThemeButton.tap() }
|
||||
@@ -50,12 +48,9 @@ final class SettingsEffectTests: XCTestCase {
|
||||
|
||||
app.showReaderChromeIfNeeded()
|
||||
app.buttons[IDs.readerSettings].tap()
|
||||
XCTAssertTrue(app.scrollViews[IDs.settingsScroll].waitForExistence(timeout: 5))
|
||||
XCTAssertTrue(app.waitForSettingsPanel())
|
||||
|
||||
let lineHeightControl = app.segmentedControls[IDs.settingsLineHeight]
|
||||
if lineHeightControl.waitForExistence(timeout: 3) && lineHeightControl.buttons.count > 1 {
|
||||
lineHeightControl.buttons.element(boundBy: 1).tap()
|
||||
}
|
||||
app.selectSettingsPillOption(pillIdentifier: IDs.settingsLineHeight, optionTitle: "标准")
|
||||
|
||||
app.buttons[IDs.settingsDone].tap()
|
||||
app.waitForDemoReaderState(timeout: 8, description: "reader=opened") { $0.isOpened }
|
||||
@@ -68,12 +63,9 @@ final class SettingsEffectTests: XCTestCase {
|
||||
|
||||
app.showReaderChromeIfNeeded()
|
||||
app.buttons[IDs.readerSettings].tap()
|
||||
XCTAssertTrue(app.scrollViews[IDs.settingsScroll].waitForExistence(timeout: 5))
|
||||
XCTAssertTrue(app.waitForSettingsPanel())
|
||||
|
||||
let displayTypeControl = app.segmentedControls[IDs.settingsDisplayType]
|
||||
if displayTypeControl.waitForExistence(timeout: 3) && displayTypeControl.buttons.count > 0 {
|
||||
displayTypeControl.buttons.element(boundBy: 0).tap()
|
||||
}
|
||||
app.selectSettingsPillOption(pillIdentifier: IDs.settingsDisplayType, optionTitle: "仿真")
|
||||
|
||||
app.buttons[IDs.settingsDone].tap()
|
||||
app.waitForDemoReaderState(timeout: 8, description: "display changed") { $0.display != nil }
|
||||
@@ -98,19 +90,21 @@ final class SettingsEffectTests: XCTestCase {
|
||||
app.showReaderChromeIfNeeded()
|
||||
app.buttons[IDs.readerSettings].tap()
|
||||
|
||||
XCTAssertTrue(app.buttons[IDs.settingsFontIncrease].waitForExistence(timeout: 5))
|
||||
XCTAssertTrue(app.sliders[IDs.settingsFontSize].waitForExistence(timeout: 5))
|
||||
let fontValueLabel = app.staticTexts[IDs.settingsFontValue]
|
||||
XCTAssertTrue(fontValueLabel.waitForExistence(timeout: 3))
|
||||
let originalFontValue = fontValueLabel.label
|
||||
|
||||
app.buttons[IDs.settingsFontIncrease].tap()
|
||||
XCTAssertTrue(app.increaseSettingsFontSize(), "字号滑块应存在")
|
||||
let newFontValue = fontValueLabel.label
|
||||
XCTAssertNotEqual(newFontValue, originalFontValue, "点击增大后字号 label 应变化")
|
||||
XCTAssertNotEqual(newFontValue, originalFontValue, "增大字号后 label 应变化")
|
||||
|
||||
app.buttons[IDs.settingsDone].tap()
|
||||
app.buttons[IDs.readerBack].tap()
|
||||
|
||||
app.launchAndOpenSampleBook()
|
||||
// 必须保留状态:默认的 resetsReaderState 会清掉刚存下的设置,
|
||||
// 那样这个用例永远测不到持久化
|
||||
app.launchAndOpenSampleBook(resetsReaderState: false)
|
||||
app.waitForReader(timeout: 12)
|
||||
|
||||
app.showReaderChromeIfNeeded()
|
||||
@@ -158,11 +152,12 @@ final class SettingsEffectTests: XCTestCase {
|
||||
app.showReaderChromeIfNeeded()
|
||||
app.buttons[IDs.readerSettings].tap()
|
||||
|
||||
let fontChoiceControl = app.segmentedControls[IDs.settingsFontChoice]
|
||||
let fontChoiceControl = app.buttons[IDs.settingsFontChoice]
|
||||
XCTAssertTrue(fontChoiceControl.waitForExistence(timeout: 5))
|
||||
if fontChoiceControl.buttons.count > 1 {
|
||||
fontChoiceControl.buttons.element(boundBy: 1).tap()
|
||||
}
|
||||
XCTAssertTrue(
|
||||
app.selectSettingsPillOption(pillIdentifier: IDs.settingsFontChoice, optionTitle: "宋体"),
|
||||
"字体应可切换到宋体"
|
||||
)
|
||||
XCTAssertEqual(fontChoiceControl.value as? String, "宋体", "切换字体后当前字体值应更新")
|
||||
app.buttons[IDs.settingsDone].tap()
|
||||
|
||||
@@ -174,7 +169,7 @@ final class SettingsEffectTests: XCTestCase {
|
||||
app.showReaderChromeIfNeeded()
|
||||
app.buttons[IDs.readerSettings].tap()
|
||||
|
||||
let reopenedFontChoiceControl = app.segmentedControls[IDs.settingsFontChoice]
|
||||
let reopenedFontChoiceControl = app.buttons[IDs.settingsFontChoice]
|
||||
XCTAssertTrue(reopenedFontChoiceControl.waitForExistence(timeout: 5))
|
||||
XCTAssertEqual(reopenedFontChoiceControl.value as? String, "宋体", "字体选择应在重启后保持")
|
||||
app.buttons[IDs.settingsDone].tap()
|
||||
|
||||
@@ -14,7 +14,7 @@ final class SettingsExtendedTests: XCTestCase {
|
||||
|
||||
XCTAssertTrue(app.buttons[IDs.readerSettings].waitForExistence(timeout: 5))
|
||||
app.buttons[IDs.readerSettings].tap()
|
||||
XCTAssertTrue(app.scrollViews[IDs.settingsScroll].waitForExistence(timeout: 5))
|
||||
XCTAssertTrue(app.waitForSettingsPanel())
|
||||
}
|
||||
|
||||
func testBrightnessSliderExists() {
|
||||
@@ -25,12 +25,13 @@ final class SettingsExtendedTests: XCTestCase {
|
||||
|
||||
func testLineHeightControl() {
|
||||
openSettings()
|
||||
let lineHeight = app.segmentedControls[IDs.settingsLineHeight]
|
||||
XCTAssertTrue(lineHeight.waitForExistence(timeout: 3), "行距控件未出现")
|
||||
|
||||
lineHeight.buttons["紧凑"].tap()
|
||||
lineHeight.buttons["标准"].tap()
|
||||
lineHeight.buttons["宽松"].tap()
|
||||
for title in ["紧凑", "标准", "宽松"] {
|
||||
XCTAssertTrue(
|
||||
app.selectSettingsPillOption(pillIdentifier: IDs.settingsLineHeight, optionTitle: title),
|
||||
"行距应可切换到\(title)"
|
||||
)
|
||||
}
|
||||
|
||||
app.buttons[IDs.settingsDone].tap()
|
||||
XCTAssertTrue(app.buttons[IDs.readerBack].waitForExistence(timeout: 5))
|
||||
@@ -38,10 +39,11 @@ final class SettingsExtendedTests: XCTestCase {
|
||||
|
||||
func testDisplayTypeFromSettings() {
|
||||
openSettings()
|
||||
let displayType = app.segmentedControls[IDs.settingsDisplayType]
|
||||
XCTAssertTrue(displayType.waitForExistence(timeout: 3), "翻页方式控件未出现")
|
||||
XCTAssertTrue(
|
||||
app.selectSettingsPillOption(pillIdentifier: IDs.settingsDisplayType, optionTitle: "横滑"),
|
||||
"翻页方式应可切换到横滑"
|
||||
)
|
||||
|
||||
displayType.buttons["横滑"].tap()
|
||||
app.buttons[IDs.settingsDone].tap()
|
||||
app.waitForDemoReaderState(timeout: 5, description: "display=horizontalScroll") { $0.display == "horizontalScroll" }
|
||||
}
|
||||
@@ -54,13 +56,13 @@ final class SettingsExtendedTests: XCTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
func testScrollSettingsToReachAllControls() {
|
||||
/// 面板是固定高度布局,所有控件一屏内可达,不需要滚动
|
||||
func testAllSettingsControlsReachableWithoutScrolling() {
|
||||
openSettings()
|
||||
let scroll = app.scrollViews[IDs.settingsScroll]
|
||||
XCTAssertTrue(scroll.waitForExistence(timeout: 3))
|
||||
|
||||
XCTAssertTrue(app.sliders[IDs.settingsFontSize].waitForExistence(timeout: 3))
|
||||
XCTAssertTrue(app.sliders[IDs.settingsBrightness].waitForExistence(timeout: 3))
|
||||
XCTAssertTrue(app.segmentedControls[IDs.settingsLineHeight].waitForExistence(timeout: 3))
|
||||
XCTAssertTrue(app.segmentedControls[IDs.settingsDisplayType].waitForExistence(timeout: 3))
|
||||
XCTAssertTrue(app.buttons[IDs.settingsLineHeight].waitForExistence(timeout: 3))
|
||||
XCTAssertTrue(app.buttons[IDs.settingsDisplayType].waitForExistence(timeout: 3))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,18 +15,19 @@ final class SettingsPanelTests: XCTestCase {
|
||||
XCTAssertTrue(app.buttons[IDs.readerSettings].waitForExistence(timeout: 5))
|
||||
app.buttons[IDs.readerSettings].tap()
|
||||
|
||||
XCTAssertTrue(app.scrollViews[IDs.settingsScroll].waitForExistence(timeout: 5))
|
||||
XCTAssertTrue(app.waitForSettingsPanel())
|
||||
let fontValue = app.staticTexts[IDs.settingsFontValue]
|
||||
XCTAssertTrue(fontValue.waitForExistence(timeout: 3))
|
||||
let before = fontValue.label
|
||||
|
||||
app.buttons[IDs.settingsFontIncrease].tap()
|
||||
XCTAssertTrue(app.increaseSettingsFontSize(), "字号滑块应存在")
|
||||
XCTAssertNotEqual(before, fontValue.label)
|
||||
|
||||
app.buttons[IDs.settingsFontDecrease].tap()
|
||||
let fontChoice = app.segmentedControls[IDs.settingsFontChoice]
|
||||
XCTAssertTrue(fontChoice.waitForExistence(timeout: 3))
|
||||
fontChoice.buttons["宋体"].tap()
|
||||
XCTAssertTrue(app.decreaseSettingsFontSize(), "字号滑块应存在")
|
||||
XCTAssertTrue(
|
||||
app.selectSettingsPillOption(pillIdentifier: IDs.settingsFontChoice, optionTitle: "宋体"),
|
||||
"字体应可切换到宋体"
|
||||
)
|
||||
|
||||
let darkTheme = app.buttons[IDs.settingsTheme(5)]
|
||||
XCTAssertTrue(darkTheme.waitForExistence(timeout: 3))
|
||||
|
||||
Reference in New Issue
Block a user