refactor(reader): 重构高亮选区绘制架构,对齐 WXRead 实现方案
- 移除 RDEPUBSelectableTextView,改用原生 UITextView - 新增 NSAttributedString 自定义属性(com.rdreader.highlight/underline)注入高亮 - RDEPUBTextPageRenderView 统一绘制高亮背景、文字和选区 - RDEPUBTextSelectionController 精简,选区矩形传递给 RenderView 绘制 - 新增高亮选区复刻 WXRead 实现方案文档 - UI 测试适配新架构 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
948004eed1
commit
70133e4e6e
@@ -60,7 +60,6 @@
|
||||
21A7DDFC2A430E9105419DF3DC2D4AA5 /* RDEPUBTextBuildPipelineInterfaces.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2055F08A4A307EA51828460481FE8276 /* RDEPUBTextBuildPipelineInterfaces.swift */; };
|
||||
2203356EEA94A9172BC7902923E43CBE /* DTCoreTextFontCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B7771FDE9AB1688166584C0AA03F12A /* DTCoreTextFontCollection.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
22AEC3422EFA234293F90AB3738CE694 /* RDEPUBReaderTableOfContentsItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F09F20128209EC507BBD2E73457DCD /* RDEPUBReaderTableOfContentsItem.swift */; };
|
||||
22FD1F39B4B94E9E33FDDF0C6880AA53 /* RDEPUBSelectableTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A65FCB87389CF471A9A4BD659D58346 /* RDEPUBSelectableTextView.swift */; };
|
||||
24395BEB3C360D9DA5180BFB955B04A0 /* DTAttributedLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = 21BAA93E67D8CBC5297302D798FB4F7A /* DTAttributedLabel.m */; };
|
||||
2445709667CB3820E51CD4AA915E6943 /* DTHTMLParserTextNode.h in Headers */ = {isa = PBXBuildFile; fileRef = C30D143134EEC5D4BA99459104B76A2E /* DTHTMLParserTextNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
24C5B52FD2AA9C49731DC6859ABA7CDE /* NSDictionary+DTCoreText.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C50CAB4241C141EF6EC9ED22AB3A7E8 /* NSDictionary+DTCoreText.m */; };
|
||||
@@ -559,7 +558,6 @@
|
||||
09C2D4A3D4F602D125FF38B56C91CF84 /* ZIPFoundation.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = ZIPFoundation.modulemap; sourceTree = "<group>"; };
|
||||
09C957F3199C596781EFA1DEA1DDCEC8 /* NSFileWrapper+DTCopying.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSFileWrapper+DTCopying.h"; path = "Core/Source/NSFileWrapper+DTCopying.h"; sourceTree = "<group>"; };
|
||||
0A294C65CC6C154EF2122380953FEFE3 /* RDReaderView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDReaderView.swift; sourceTree = "<group>"; };
|
||||
0A65FCB87389CF471A9A4BD659D58346 /* RDEPUBSelectableTextView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBSelectableTextView.swift; sourceTree = "<group>"; };
|
||||
0B10D3EC1701BBA4D69926915F942558 /* SnapKit-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SnapKit-umbrella.h"; sourceTree = "<group>"; };
|
||||
0B7771FDE9AB1688166584C0AA03F12A /* DTCoreTextFontCollection.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DTCoreTextFontCollection.h; path = Core/Source/DTCoreTextFontCollection.h; sourceTree = "<group>"; };
|
||||
0B832537E4E508747319F7DF89BE256A /* RDEPUBChapterTailNormalizer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RDEPUBChapterTailNormalizer.swift; sourceTree = "<group>"; };
|
||||
@@ -1460,7 +1458,6 @@
|
||||
children = (
|
||||
CA31C1F2D2AF87B527C261FF3EF62C58 /* RDEPUBPageInteractionController.swift */,
|
||||
19F9801AD3675102B14F64AFAFB99D89 /* RDEPUBPageLayoutSnapshot.swift */,
|
||||
0A65FCB87389CF471A9A4BD659D58346 /* RDEPUBSelectableTextView.swift */,
|
||||
185CD1A4E30E72715AF3129924F063E6 /* RDEPUBSelectionOverlayView.swift */,
|
||||
2F910EB56A7325328DCD94F082E99648 /* RDEPUBTextAnnotationOverlay.swift */,
|
||||
4B789EEAB8791D76E537C6186EA1AC1D /* RDEPUBTextContentView.swift */,
|
||||
@@ -2521,7 +2518,6 @@
|
||||
7C026FA594F06339EAAEB774A776A030 /* RDEPUBResourceURLSchemeHandler.swift in Sources */,
|
||||
4D00F4DD1B8D720FFA715756C7D70D40 /* RDEPUBSearchEngine.swift in Sources */,
|
||||
53240F8A0E98A48B59C6B2FA88270310 /* RDEPUBSearchModels.swift in Sources */,
|
||||
22FD1F39B4B94E9E33FDDF0C6880AA53 /* RDEPUBSelectableTextView.swift in Sources */,
|
||||
44B918236776349CDE1D85C70EDD7328 /* RDEPUBSelectionOverlayView.swift in Sources */,
|
||||
F77B604D4BCCE6CBE8520E588343F0A1 /* RDEPUBSemanticMarkerInjector.swift in Sources */,
|
||||
0A37B6C1B26290A715DB47A6D750293D /* RDEPUBStyleSheetBuilder.swift in Sources */,
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1540"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DE070C1D2FBF0CC900ED065F"
|
||||
BuildableName = "ReadViewDemo.app"
|
||||
BlueprintName = "ReadViewDemo"
|
||||
ReferencedContainer = "container:ReadViewDemo.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
codeCoverageEnabled = "NO">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "7BE8E325ADE250D48D1A997A"
|
||||
BuildableName = "ReadViewDemoUITests.xctest"
|
||||
BlueprintName = "ReadViewDemoUITests"
|
||||
ReferencedContainer = "container:ReadViewDemo.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DE070C1D2FBF0CC900ED065F"
|
||||
BuildableName = "ReadViewDemo.app"
|
||||
BlueprintName = "ReadViewDemo"
|
||||
ReferencedContainer = "container:ReadViewDemo.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DE070C1D2FBF0CC900ED065F"
|
||||
BuildableName = "ReadViewDemo.app"
|
||||
BlueprintName = "ReadViewDemo"
|
||||
ReferencedContainer = "container:ReadViewDemo.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -21,17 +21,30 @@ final class ReaderAnnotationTests: XCTestCase {
|
||||
app.waitForReaderState(containing: "selection=1", timeout: 5)
|
||||
|
||||
let highlightMenuItem = app.menuItems["高亮"]
|
||||
let highlightButton = app.buttons["高亮"]
|
||||
let highlightActionButton = app.buttons[IDs.readerSelectionHighlight]
|
||||
let highlightButton = app.buttons[IDs.readerSelectionHighlight]
|
||||
if highlightMenuItem.waitForExistence(timeout: 3) {
|
||||
highlightMenuItem.tap()
|
||||
} else if highlightActionButton.waitForExistence(timeout: 3) {
|
||||
highlightActionButton.tap()
|
||||
} else {
|
||||
XCTAssertTrue(highlightButton.waitForExistence(timeout: 3), "选中文本后未出现高亮菜单")
|
||||
} else if highlightButton.waitForExistence(timeout: 3) {
|
||||
highlightButton.tap()
|
||||
} else {
|
||||
XCTFail("选中文本后未出现高亮菜单")
|
||||
}
|
||||
|
||||
app.waitForReaderState(containing: "highlights=1", timeout: 8)
|
||||
}
|
||||
|
||||
func testLongPressSelectionDoesNotShowToolbars() {
|
||||
app.launchAndOpenSampleBook(pageNumber: 2)
|
||||
app.waitForReader()
|
||||
|
||||
let content = app.textViews[IDs.readerSelectionText].firstMatch
|
||||
XCTAssertTrue(content.waitForExistence(timeout: 5), "阅读内容区域未出现")
|
||||
|
||||
let start = content.coordinate(withNormalizedOffset: CGVector(dx: 0.35, dy: 0.42))
|
||||
let end = content.coordinate(withNormalizedOffset: CGVector(dx: 0.68, dy: 0.42))
|
||||
start.press(forDuration: 0.6, thenDragTo: end)
|
||||
|
||||
app.waitForReaderState(containing: "selection=1", timeout: 5)
|
||||
app.waitForReaderState(containing: "toolbar=hidden", timeout: 5)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user