refactor: rename RDReaderView -> RDEpubReaderView, update pod config and docs
- Rename source module from RDReaderView to RDEpubReaderView - Move all source files from Sources/RDReaderView/ to Sources/RDEpubReaderView/ - Update podspec: RDReaderView.podspec -> RDEpubReaderView.podspec - Update Podfile, demo project, and CocoaPods config for new pod name - Delete old RDReaderView pod support files from ReadViewDemo/Pods - Add new RDEpubReaderView pod support files - Update documentation (API ref, architecture, UML, conventions, etc.) - Add FixedLayoutRotationTests - Update .gitignore: exclude .DS_Store, manual unpack backups, _ssoft-output
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
import UIKit
|
||||
|
||||
#if canImport(DTCoreText)
|
||||
import DTCoreText
|
||||
#endif
|
||||
|
||||
/// Chapter-level shared display content and layouter
|
||||
/// (LONG_CHAPTER_MEMORY_OPTIMIZATION_PLAN.md P1-1 / P1-3).
|
||||
///
|
||||
/// One entry holds the chapter-length display string (theme text color and
|
||||
/// dark-image adjustment injected once over the full chapter at build time)
|
||||
/// plus the DTCoreTextLayouter wrapping its framesetter. Page views reference
|
||||
/// the shared string and request per-page layout frames; they must never
|
||||
/// mutate the shared string — the framesetter and any live layout frame hold
|
||||
/// it. Page-level decoration (highlights, search, selection) is drawn by the
|
||||
/// overlay views instead.
|
||||
///
|
||||
/// Main-thread only.
|
||||
final class RDEPUBChapterDisplayContentCache {
|
||||
|
||||
#if canImport(DTCoreText)
|
||||
|
||||
struct Entry {
|
||||
let content: NSAttributedString
|
||||
let layouter: DTCoreTextLayouter?
|
||||
fileprivate let signature: Signature
|
||||
}
|
||||
|
||||
fileprivate struct Signature: Equatable {
|
||||
let chapterContentID: ObjectIdentifier
|
||||
let contentLength: Int
|
||||
let textColor: UIColor
|
||||
let backgroundColor: UIColor
|
||||
let darkImageAdjustmentEnabled: Bool
|
||||
let darkImageBlendRatio: CGFloat
|
||||
|
||||
init(page: RDEPUBTextPage, configuration: RDEPUBReaderConfiguration) {
|
||||
chapterContentID = ObjectIdentifier(page.chapterContent)
|
||||
contentLength = page.chapterContent.length
|
||||
textColor = configuration.theme.contentTextColor
|
||||
backgroundColor = configuration.theme.contentBackgroundColor
|
||||
darkImageAdjustmentEnabled = configuration.darkImageAdjustmentEnabled
|
||||
darkImageBlendRatio = configuration.darkImageBlendRatio
|
||||
}
|
||||
}
|
||||
|
||||
/// Current chapter plus one adjacent chapter during page-curl/scroll
|
||||
/// transitions across a chapter boundary.
|
||||
private static let capacity = 2
|
||||
|
||||
private var entries: [String: Entry] = [:]
|
||||
|
||||
private var accessOrder: [String] = []
|
||||
|
||||
func entry(for page: RDEPUBTextPage, configuration: RDEPUBReaderConfiguration) -> Entry {
|
||||
dispatchPrecondition(condition: .onQueue(.main))
|
||||
let signature = Signature(page: page, configuration: configuration)
|
||||
if let cached = entries[page.href], cached.signature == signature {
|
||||
touch(page.href)
|
||||
return cached
|
||||
}
|
||||
let entry = Self.buildEntry(page: page, configuration: configuration, signature: signature)
|
||||
entries[page.href] = entry
|
||||
touch(page.href)
|
||||
evictIfNeeded()
|
||||
RDEPUBMemoryProbe.log("displayContentBuilt href=\(page.href) length=\(entry.content.length)")
|
||||
return entry
|
||||
}
|
||||
|
||||
func removeAll() {
|
||||
entries.removeAll()
|
||||
accessOrder.removeAll()
|
||||
}
|
||||
|
||||
private func touch(_ href: String) {
|
||||
accessOrder.removeAll { $0 == href }
|
||||
accessOrder.append(href)
|
||||
}
|
||||
|
||||
private func evictIfNeeded() {
|
||||
while accessOrder.count > Self.capacity {
|
||||
let evicted = accessOrder.removeFirst()
|
||||
entries.removeValue(forKey: evicted)
|
||||
}
|
||||
}
|
||||
|
||||
private static func buildEntry(
|
||||
page: RDEPUBTextPage,
|
||||
configuration: RDEPUBReaderConfiguration,
|
||||
signature: Signature
|
||||
) -> Entry {
|
||||
let content = NSMutableAttributedString(attributedString: page.chapterContent)
|
||||
let fullRange = NSRange(location: 0, length: content.length)
|
||||
_ = RDEPUBDarkImageAdjuster.adjustIfNeeded(
|
||||
content,
|
||||
in: fullRange,
|
||||
configuration: configuration
|
||||
)
|
||||
content.addAttribute(
|
||||
.foregroundColor,
|
||||
value: configuration.theme.contentTextColor,
|
||||
range: fullRange
|
||||
)
|
||||
let layouter = DTCoreTextLayouter(attributedString: content)
|
||||
layouter?.shouldCacheLayoutFrames = false
|
||||
return Entry(content: content, layouter: layouter, signature: signature)
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
func removeAll() {}
|
||||
|
||||
#endif
|
||||
}
|
||||
Reference in New Issue
Block a user