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,142 @@
|
||||
|
||||
import UIKit
|
||||
import WebKit
|
||||
|
||||
extension RDEPUBWebView {
|
||||
private var normalSearchOverlayColor: UIColor {
|
||||
UIColor(red: 0.21, green: 0.48, blue: 0.95, alpha: 0.16)
|
||||
}
|
||||
|
||||
private var activeSearchOverlayColor: UIColor {
|
||||
UIColor(red: 0.14, green: 0.42, blue: 0.95, alpha: 0.34)
|
||||
}
|
||||
|
||||
func applySearchDecorationsIfNeeded(completion: (() -> Void)? = nil) {
|
||||
guard let webView else {
|
||||
completion?()
|
||||
return
|
||||
}
|
||||
|
||||
let presentation: RDEPUBSearchPresentation?
|
||||
switch currentRenderRequest {
|
||||
case .reflowable(let request):
|
||||
presentation = request.searchPresentation
|
||||
case .fixed(let request):
|
||||
presentation = request.searchPresentation
|
||||
case .none:
|
||||
presentation = nil
|
||||
}
|
||||
|
||||
let script = RDEPUBJavaScriptBridge.applySearchScript(for: presentation)
|
||||
webView.evaluateJavaScript(script) { [weak self] _, error in
|
||||
guard let self else {
|
||||
completion?()
|
||||
return
|
||||
}
|
||||
if let error {
|
||||
self.delegate?.epubWebView(self, didLogJavaScriptError: error.localizedDescription)
|
||||
}
|
||||
self.refreshNativeDecorationsIfNeeded(completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
func refreshNativeDecorationsIfNeeded(completion: (() -> Void)? = nil) {
|
||||
guard let webView else {
|
||||
onDecorationsResolved?([])
|
||||
completion?()
|
||||
return
|
||||
}
|
||||
|
||||
webView.evaluateJavaScript(RDEPUBJavaScriptBridge.resolveDecorationsScript()) { [weak self] result, error in
|
||||
guard let self else {
|
||||
completion?()
|
||||
return
|
||||
}
|
||||
if let error {
|
||||
self.delegate?.epubWebView(self, didLogJavaScriptError: error.localizedDescription)
|
||||
self.onDecorationsResolved?([])
|
||||
completion?()
|
||||
return
|
||||
}
|
||||
|
||||
let decorations = self.nativeDecorations(from: result)
|
||||
self.onDecorationsResolved?(decorations)
|
||||
completion?()
|
||||
}
|
||||
}
|
||||
|
||||
private func nativeDecorations(from result: Any?) -> [RDEPUBTextOverlayDecoration] {
|
||||
guard let payload = result as? [String: Any] else { return [] }
|
||||
|
||||
let highlightDecorations = decorationList(
|
||||
from: payload["highlights"] as? [[String: Any]],
|
||||
defaultColor: overlayColor(hex: "#F8E16C", alpha: 0.45) ?? UIColor(red: 248 / 255, green: 225 / 255, blue: 108 / 255, alpha: 0.45)
|
||||
)
|
||||
let searchDecorations = decorationList(
|
||||
from: payload["search"] as? [[String: Any]],
|
||||
defaultColor: normalSearchOverlayColor
|
||||
)
|
||||
return highlightDecorations + searchDecorations
|
||||
}
|
||||
|
||||
private func decorationList(
|
||||
from items: [[String: Any]]?,
|
||||
defaultColor: UIColor
|
||||
) -> [RDEPUBTextOverlayDecoration] {
|
||||
guard let items else { return [] }
|
||||
|
||||
return items.enumerated().compactMap { index, item -> RDEPUBTextOverlayDecoration? in
|
||||
guard let kindString = item["kind"] as? String,
|
||||
let kind = RDEPUBTextOverlayDecoration.Kind(rawValue: kindString),
|
||||
let rectPayloads = item["rects"] as? [[String: Any]] else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let rects = rectPayloads.compactMap { rectPayload -> CGRect? in
|
||||
guard let x = (rectPayload["x"] as? NSNumber)?.doubleValue,
|
||||
let y = (rectPayload["y"] as? NSNumber)?.doubleValue,
|
||||
let width = (rectPayload["width"] as? NSNumber)?.doubleValue,
|
||||
let height = (rectPayload["height"] as? NSNumber)?.doubleValue else {
|
||||
return nil
|
||||
}
|
||||
return CGRect(x: x, y: y, width: width, height: height)
|
||||
}
|
||||
guard !rects.isEmpty else { return nil }
|
||||
|
||||
let color: UIColor
|
||||
switch kind {
|
||||
case .underline:
|
||||
let hex = item["color"] as? String ?? "#F8E16C"
|
||||
color = overlayColor(hex: hex, alpha: 1) ?? defaultColor
|
||||
case .activeSearch:
|
||||
color = activeSearchOverlayColor
|
||||
case .search:
|
||||
color = normalSearchOverlayColor
|
||||
case .highlight:
|
||||
let hex = item["color"] as? String ?? "#F8E16C"
|
||||
color = overlayColor(hex: hex, alpha: 0.45) ?? defaultColor
|
||||
case .selection, .locate:
|
||||
color = defaultColor
|
||||
}
|
||||
|
||||
return RDEPUBTextOverlayDecoration(
|
||||
kind: kind,
|
||||
absoluteRange: NSRange(location: index, length: 1),
|
||||
rects: rects,
|
||||
color: color
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private func overlayColor(hex: String, alpha: CGFloat) -> UIColor? {
|
||||
var value = hex.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
value = value.replacingOccurrences(of: "#", with: "")
|
||||
guard value.count == 6, let hexValue = Int(value, radix: 16) else { return nil }
|
||||
return UIColor(
|
||||
red: CGFloat((hexValue >> 16) & 0xFF) / 255,
|
||||
green: CGFloat((hexValue >> 8) & 0xFF) / 255,
|
||||
blue: CGFloat(hexValue & 0xFF) / 255,
|
||||
alpha: alpha
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user