refactor: 添加中文注释 + 优化模块结构

- 给全部 78 个 Swift 源文件添加详细的中文注释(文件级、类级、方法级)
- 删除 LegacyRDReaderController/ 死代码目录(16 文件 4592 行)
- 根目录翻页容器文件移入 ReaderView/ 目录
- Resources/ 移入 EPUBCore/Resources/(与使用者归属一致)
- RDEPUBTextIndexTable.swift 移入 EPUBTextRendering/(消除反向依赖)
- RDURLReaderController.swift 移入 EPUBUI/(入口控制器归入 UI 层)
- 更新 podspec 资源路径
This commit is contained in:
shen
2026-05-25 10:19:14 +08:00
parent 5af90c1148
commit 54798ba578
88 changed files with 2492 additions and 4469 deletions
@@ -4,6 +4,11 @@ import UIKit
import DTCoreText
#endif
// MARK: -
///
/// DTCoreText
///
final class RDEPUBPageInteractionController {
var snapshot: RDEPUBPageLayoutSnapshot?
@@ -4,29 +4,56 @@ import UIKit
import DTCoreText
#endif
// MARK: -
///
/// DTCoreText 线
struct RDEPUBPageLine {
/// chapterContent
let stringRange: NSRange
///
let frame: CGRect
/// 线
let baselineOrigin: CGPoint
/// 线
let ascent: CGFloat
/// 线
let descent: CGFloat
///
let leading: CGFloat
}
/// Run
///
struct RDEPUBPageRun {
/// Run
let stringRange: NSRange
/// Run
let frame: CGRect
///
let isAttachment: Bool
}
///
///
struct RDEPUBPageAttachment {
///
let stringRange: NSRange
///
let frame: CGRect
///
let displaySize: CGSize
/// /
let placement: RDEPUBTextAttachmentPlacement?
/// /
let kind: RDEPUBTextAttachmentKind?
}
// MARK: -
///
/// DTCoreText
///
struct RDEPUBPageLayoutSnapshot {
let page: RDEPUBTextPage
let lines: [RDEPUBPageLine]
@@ -1,12 +1,27 @@
import UIKit
// MARK: -
///
///
/// configuration
final class RDEPUBReaderBottomToolView: RDEPUBReaderToolView {
// MARK:
///
var onShowTableOfContents: (() -> Void)?
///
var onShowBookmarks: (() -> Void)?
///
var onShowHighlights: (() -> Void)?
///
var onAddHighlight: (() -> Void)?
///
var onShowSettings: (() -> Void)?
// MARK: UI
///
private let stackView: UIStackView = {
let view = UIStackView()
view.axis = .horizontal
@@ -16,10 +31,15 @@ final class RDEPUBReaderBottomToolView: RDEPUBReaderToolView {
return view
}()
///
private let chapterButton = RDEPUBReaderTintButton(type: .system)
///
private let bookmarksButton = RDEPUBReaderTintButton(type: .system)
///
private let highlightsButton = RDEPUBReaderTintButton(type: .system)
///
private let addHighlightButton = RDEPUBReaderTintButton(type: .system)
///
private let settingsButton = RDEPUBReaderTintButton(type: .system)
override init(frame: CGRect) {
@@ -78,6 +98,9 @@ final class RDEPUBReaderBottomToolView: RDEPUBReaderToolView {
}
}
// MARK:
///
func setAddHighlightEnabled(_ isEnabled: Bool) {
addHighlightButton.isEnabled = isEnabled
addHighlightButton.alpha = isEnabled ? 1 : 0.45
@@ -1,6 +1,11 @@
import UIKit
// MARK: -
/// EPUB
///
final class RDEPUBReaderChapterListController: UITableViewController {
///
var onSelectItem: ((RDEPUBReaderTableOfContentsItem) -> Void)?
private let items: [RDEPUBReaderTableOfContentsItem]
@@ -1,24 +1,75 @@
import UIKit
// MARK: -
/// EPUB
///
public enum RDEPUBTextRenderingEngine: Equatable {
/// DTCoreText HTML/CSS NSAttributedString
case dtCoreText
}
// MARK: -
/// EPUB
/// EPUBUI
/// RDEPUBReaderController
public struct RDEPUBReaderConfiguration: Equatable {
// MARK:
/// pt 15
public var fontSize: CGFloat
/// 1.6 1.6
public var lineHeightMultiple: CGFloat
/// .pageCurl仿 .scroll
public var displayType: RDReaderView.DisplayType
///
public var landscapeDualPageEnabled: Bool
// MARK: UI
///
public var showsTableOfContents: Bool
///
public var allowsHighlights: Bool
///
public var showsSettingsPanel: Bool
// MARK:
/// reflowable 40pt 16pt
public var reflowableContentInsets: UIEdgeInsets
/// fixed layout
public var fixedContentInset: UIEdgeInsets
// MARK:
/// // .light
public var theme: RDEPUBReaderTheme
///
public var fixedLayoutFit: RDEPUBFixedLayoutFit
/// //
public var fixedLayoutSpreadMode: RDEPUBFixedLayoutSpreadMode
/// 使 DTCoreText
public var textRenderingEngine: RDEPUBTextRenderingEngine
// MARK:
///
/// - Parameters:
/// - fontSize: 15pt
/// - lineHeightMultiple: 1.6
/// - displayType: .pageCurl
/// - landscapeDualPageEnabled: true
/// - showsTableOfContents: true
/// - allowsHighlights: true
/// - showsSettingsPanel: true
/// - reflowableContentInsets:
/// - fixedContentInset:
/// - theme: .light
/// - fixedLayoutFit:
/// - fixedLayoutSpreadMode:
/// - textRenderingEngine:
public init(
fontSize: CGFloat = 15,
lineHeightMultiple: CGFloat = 1.6,
@@ -49,10 +100,15 @@ public struct RDEPUBReaderConfiguration: Equatable {
self.textRenderingEngine = textRenderingEngine
}
/// 使
public static let `default` = RDEPUBReaderConfiguration()
}
// MARK: -
extension RDEPUBReaderConfiguration {
/// RDEPUBPreferences
/// - Returns: Core 使
func makePreferences() -> RDEPUBPreferences {
RDEPUBPreferences(
fontSize: fontSize,
@@ -1,6 +1,29 @@
import UIKit
// MARK: -
/// EPUB
/// EPUBUI
/// EPUB URL TextBook
///
/// **** EPUBUI Core Parser Foundation
///
/// ****
/// - EPUB
/// -
/// -
/// - UI
/// -
///
/// **** init(epubURL:) viewDidLoad RDEPUBParser.parse paginatePublication readerView.reloadData restoreLocation
// MARK: -
public final class RDEPUBReaderController: UIViewController {
// MARK: -
///
///
private struct RDEPUBViewportSignature: Equatable {
let width: CGFloat
let height: CGFloat
@@ -19,6 +42,7 @@ public final class RDEPUBReaderController: UIViewController {
}
}
///
private enum RDEPUBViewportChangeReason {
case viewLayout
case orientationTransition
@@ -27,8 +51,12 @@ public final class RDEPUBReaderController: UIViewController {
private typealias NativeTextSnapshot = (pages: [EPUBPage], chapters: [EPUBChapterInfo])
// MARK: -
///
public weak var delegate: RDEPUBReaderDelegate?
///
public var configuration: RDEPUBReaderConfiguration {
didSet {
persistReaderSettingsIfNeeded()
@@ -54,38 +82,49 @@ public final class RDEPUBReaderController: UIViewController {
}
}
///
public var currentLocation: RDEPUBLocation? {
currentVisibleLocation()
}
/// 1 nil
public var currentPageNumber: Int? {
guard readerView.currentPage >= 0 else { return nil }
return readerView.currentPage + 1
}
/// /
public private(set) var currentSelection: RDEPUBSelection?
///
public var highlights: [RDEPUBHighlight] {
activeHighlights
}
///
public var bookmarks: [RDEPUBBookmark] {
activeBookmarks
}
///
public var tableOfContents: [EPUBTableOfContentsItem] {
publication?.tableOfContents ?? []
}
///
public var flattenedTableOfContents: [RDEPUBReaderTableOfContentsItem] {
guard let publication else { return [] }
return flattenedTableOfContentsItems(from: publication.tableOfContents)
}
///
public var currentTableOfContentsItem: RDEPUBReaderTableOfContentsItem? {
resolvedCurrentTableOfContentsItem()
}
// MARK: -
/// EPUB URL
private let epubURL: URL
fileprivate let persistence: RDEPUBReaderPersistence?
private let readerView = RDReaderView()
@@ -131,6 +170,14 @@ public final class RDEPUBReaderController: UIViewController {
/// TextBook URL TextBook
private var textFileURL: URL?
// MARK: -
/// 使 EPUB URL
///
/// - Parameters:
/// - epubURL: EPUB URL
/// - configuration: 使 .default
/// - persistence: 使 UserDefaults
public init(
epubURL: URL,
configuration: RDEPUBReaderConfiguration = .default,
@@ -149,7 +196,15 @@ public final class RDEPUBReaderController: UIViewController {
fatalError("init(coder:) has not been implemented")
}
/// 使 TextBook EPUB TXT
/// 使 TextBook EPUB
/// TXT TextBook
/// - Parameters:
/// - textBook: TextBook
/// - bookIdentifier:
/// - title:
/// - textFileURL: URL TextBook
/// - configuration:
/// - persistence:
public convenience init(
textBook: RDEPUBTextBook,
bookIdentifier: String,
@@ -244,6 +299,9 @@ public final class RDEPUBReaderController: UIViewController {
}
}
// MARK: - API
/// EPUB
public func reloadBook() {
didStartInitialLoad = false
parser = nil
@@ -263,11 +321,18 @@ public final class RDEPUBReaderController: UIViewController {
startInitialLoadIfNeeded()
}
///
/// - Parameter location:
public func go(to location: RDEPUBLocation) {
guard publication != nil else { return }
restoreReadingLocation(location)
}
///
/// - Parameters:
/// - pageNumber: 1
/// - animated:
/// - Returns:
@discardableResult
public func go(toPageNumber pageNumber: Int, animated: Bool = false) -> Bool {
guard pageNumber > 0 else { return false }
@@ -289,6 +354,7 @@ public final class RDEPUBReaderController: UIViewController {
return true
}
///
public func clearSelection() {
updateCurrentSelection(nil)
}
@@ -321,6 +387,12 @@ public final class RDEPUBReaderController: UIViewController {
return parts.joined(separator: " · ")
}
///
/// - Parameters:
/// - selection: 使 currentSelection
/// - color: CSS
/// - note:
/// - Returns: nil
@discardableResult
public func addHighlight(
from selection: RDEPUBSelection? = nil,
@@ -1285,6 +1357,8 @@ public final class RDEPUBReaderController: UIViewController {
}
}
// MARK: -
extension RDEPUBReaderController {
@discardableResult
public func addBookmark(note: String? = nil) -> RDEPUBBookmark? {
@@ -1456,6 +1530,10 @@ extension RDEPUBReaderController {
return trimmed.isEmpty ? nil : trimmed
}
// MARK: -
///
/// - Parameter keyword:
public func search(keyword: String) {
let normalizedKeyword = keyword.trimmingCharacters(in: .whitespacesAndNewlines)
guard !normalizedKeyword.isEmpty else {
@@ -1622,6 +1700,8 @@ extension RDEPUBReaderController {
}
}
// MARK: - RDReaderView
extension RDEPUBReaderController: RDReaderDataSource, RDReaderDelegate {
public func pageCountOfReaderView(readerView: RDReaderView) -> Int {
textBook?.pages.count ?? activePages.count
@@ -1724,6 +1804,8 @@ extension RDEPUBReaderController: RDReaderDataSource, RDReaderDelegate {
}
}
// MARK: - Web EPUB /Web
extension RDEPUBReaderController: RDEPUBWebContentViewDelegate {
func epubWebContentView(_ contentView: RDEPUBWebContentView, didUpdateLocation location: RDEPUBLocation, spineIndex: Int) {
guard readerView.currentPage >= 0,
@@ -1780,6 +1862,8 @@ extension RDEPUBReaderController: RDEPUBWebContentViewDelegate {
}
}
// MARK: - Native Text
extension RDEPUBReaderController: RDEPUBTextContentViewDelegate {
func textContentView(_ contentView: RDEPUBTextContentView, didChangeSelection selection: RDEPUBSelection?) {
guard let selection else {
@@ -1902,6 +1986,8 @@ extension RDEPUBReaderController: RDEPUBTextContentViewDelegate {
}
}
// MARK: - NavigationController
extension RDEPUBReaderController: UIGestureRecognizerDelegate {
public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
true
@@ -1,20 +1,86 @@
import UIKit
// MARK: -
/// EPUB
///
///
public protocol RDEPUBReaderDelegate: AnyObject {
///
/// - Parameters:
/// - reader:
/// - publication:
func epubReader(_ reader: UIViewController, didOpen publication: RDEPUBPublication)
///
/// - Parameters:
/// - reader:
/// - location:
func epubReader(_ reader: UIViewController, didUpdateLocation location: RDEPUBLocation)
///
/// - Parameter reader:
func epubReaderDidReachEnd(_ reader: UIViewController)
///
/// - Parameters:
/// - reader:
/// - selection: nil
func epubReader(_ reader: UIViewController, didChangeSelection selection: RDEPUBSelection?)
///
/// - Parameters:
/// - reader:
/// - highlights:
func epubReader(_ reader: UIViewController, didUpdateHighlights highlights: [RDEPUBHighlight])
///
/// - Parameters:
/// - reader:
/// - bookmarks:
func epubReader(_ reader: UIViewController, didUpdateBookmarks bookmarks: [RDEPUBBookmark])
///
/// - Parameters:
/// - reader:
/// - result: nil
func epubReader(_ reader: UIViewController, didUpdateSearchResult result: RDEPUBSearchResult?)
///
/// - Parameters:
/// - reader:
/// - match: nil
func epubReader(_ reader: UIViewController, didChangeCurrentSearchMatch match: RDEPUBSearchMatch?)
///
/// - Parameters:
/// - reader:
/// - item:
func epubReader(_ reader: UIViewController, didUpdateCurrentTableOfContentsItem item: RDEPUBReaderTableOfContentsItem?)
///
/// - Parameters:
/// - reader:
/// - url: URL
func epubReader(_ reader: UIViewController, didActivateExternalLink url: URL)
///
/// - Parameters:
/// - reader:
/// - error:
func epubReader(_ reader: UIViewController, didFailWithError error: Error)
///
/// viewDidLoad
/// - Parameters:
/// - reader:
/// - topToolView:
func epubReader(_ reader: UIViewController, configureTopToolView topToolView: RDEPUBReaderTopToolView)
}
// MARK: -
///
public extension RDEPUBReaderDelegate {
func epubReader(_ reader: UIViewController, didOpen publication: RDEPUBPublication) {}
func epubReader(_ reader: UIViewController, didUpdateLocation location: RDEPUBLocation) {}
@@ -1,13 +1,24 @@
import UIKit
// MARK: -
/// EPUB
/// //
final class RDEPUBReaderHighlightsViewController: UITableViewController {
///
var onSelectHighlight: ((RDEPUBHighlight) -> Void)?
///
var onUpdateHighlight: ((RDEPUBHighlight) -> Void)?
///
var onDeleteHighlight: ((RDEPUBHighlight) -> Void)?
///
private var highlights: [RDEPUBHighlight]
///
private let theme: RDEPUBReaderTheme
///
private let sectionTitleProvider: (RDEPUBHighlight) -> String?
///
private let filterControl = UISegmentedControl(items: ["全部", "批注", "高亮"])
private var filteredHighlights: [RDEPUBHighlight] {
@@ -205,8 +216,14 @@ final class RDEPUBReaderHighlightsViewController: UITableViewController {
}
}
// MARK: -
/// EPUB
///
final class RDEPUBReaderBookmarksViewController: UITableViewController {
///
var onSelectBookmark: ((RDEPUBBookmark) -> Void)?
///
var onDeleteBookmark: ((RDEPUBBookmark) -> Void)?
private var bookmarks: [RDEPUBBookmark]
@@ -1,16 +1,44 @@
import Foundation
// MARK: -
///
///
///
public protocol RDEPUBReaderPersistence: AnyObject {
///
/// - Parameter bookIdentifier:
/// - Returns: nil
func loadLocation(for bookIdentifier: String) -> RDEPUBLocation?
///
/// - Parameters:
/// - location:
/// - bookIdentifier:
func saveLocation(_ location: RDEPUBLocation, for bookIdentifier: String)
///
func loadBookmarks(for bookIdentifier: String) -> [RDEPUBBookmark]
///
func saveBookmarks(_ bookmarks: [RDEPUBBookmark], for bookIdentifier: String)
///
func loadHighlights(for bookIdentifier: String) -> [RDEPUBHighlight]
///
func saveHighlights(_ highlights: [RDEPUBHighlight], for bookIdentifier: String)
///
func loadReaderSettings() -> RDEPUBReaderSettings?
///
func saveReaderSettings(_ settings: RDEPUBReaderSettings)
}
// MARK: -
/// //
public extension RDEPUBReaderPersistence {
func loadBookmarks(for bookIdentifier: String) -> [RDEPUBBookmark] {
_ = bookIdentifier
@@ -31,13 +59,30 @@ public extension RDEPUBReaderPersistence {
}
}
// MARK: - UserDefaults
/// UserDefaults
/// JSON
/// SQLite
public final class RDEPUBUserDefaultsPersistence: RDEPUBReaderPersistence {
/// UserDefaults
private let defaults: UserDefaults
/// key
private let locationPrefix: String
/// key
private let bookmarksPrefix: String
/// key
private let highlightsPrefix: String
/// key
private let settingsKey: String
/// UserDefaults
/// - Parameters:
/// - defaults: UserDefaults .standard
/// - locationPrefix: key
/// - bookmarksPrefix: key
/// - highlightsPrefix: key
/// - settingsKey: key
public init(
defaults: UserDefaults = .standard,
locationPrefix: String = "ssreader.epub.location.",
@@ -1,11 +1,20 @@
import UIKit
// MARK: -
///
/// RDReaderView.DisplayType
public enum RDEPUBReaderDisplayMode: String, Codable, Equatable {
/// 仿
case pageCurl
///
case horizontalScroll
///
case verticalScroll
///
case horizontalCoverScroll
/// DisplayType UI DisplayMode
init(displayType: RDReaderView.DisplayType) {
switch displayType {
case .pageCurl:
@@ -17,6 +26,8 @@ public enum RDEPUBReaderDisplayMode: String, Codable, Equatable {
}
}
/// RDReaderView DisplayType
/// horizontalCoverScroll horizontalScroll
var displayType: RDReaderView.DisplayType {
switch self {
case .pageCurl:
@@ -29,14 +40,25 @@ public enum RDEPUBReaderDisplayMode: String, Codable, Equatable {
}
}
// MARK: -
///
/// RDEPUBReaderTheme
public enum RDEPUBReaderThemePreset: String, Codable, CaseIterable, Equatable {
///
case light
///
case yellow
/// 绿
case green
///
case pink
///
case blue
/// /
case dark
///
public var theme: RDEPUBReaderTheme {
switch self {
case .light:
@@ -54,6 +76,8 @@ public enum RDEPUBReaderThemePreset: String, Codable, CaseIterable, Equatable {
}
}
///
/// nil
public init?(theme: RDEPUBReaderTheme) {
switch theme {
case .light:
@@ -74,13 +98,26 @@ public enum RDEPUBReaderThemePreset: String, Codable, CaseIterable, Equatable {
}
}
// MARK: -
///
///
///
public struct RDEPUBReaderSettings: Codable, Equatable {
// MARK:
/// 0.0 ~ 1.0nil 使
public var brightness: CGFloat?
/// ptnil 使
public var fontSize: CGFloat?
/// nil 使
public var lineHeightMultiple: CGFloat?
/// nil 使
public var displayMode: RDEPUBReaderDisplayMode?
/// nil 使
public var themePreset: RDEPUBReaderThemePreset?
/// nil
public init(
brightness: CGFloat? = nil,
fontSize: CGFloat? = nil,
@@ -95,6 +132,10 @@ public struct RDEPUBReaderSettings: Codable, Equatable {
self.themePreset = themePreset
}
///
/// nil
/// - Parameter configuration: RDEPUBReaderConfiguration.default
/// - Returns:
public func applying(to configuration: RDEPUBReaderConfiguration) -> RDEPUBReaderConfiguration {
var resolvedConfiguration = configuration
@@ -114,6 +155,12 @@ public struct RDEPUBReaderSettings: Codable, Equatable {
return resolvedConfiguration
}
///
/// 0.0 ~ 1.0
/// - Parameters:
/// - configuration:
/// - brightness:
/// - Returns:
public static func capture(
configuration: RDEPUBReaderConfiguration,
brightness: CGFloat
@@ -1,12 +1,27 @@
import UIKit
// MARK: -
/// EPUB
///
/// iOS 13+ SF Symbols 退
final class RDEPUBReaderSettingsViewController: UIViewController {
// MARK:
/// 0.0 ~ 1.0
var onBrightnessChange: ((CGFloat) -> Void)?
/// pt
var onFontSizeChange: ((CGFloat) -> Void)?
///
var onLineHeightChange: ((CGFloat) -> Void)?
///
var onDisplayTypeChange: ((RDReaderView.DisplayType) -> Void)?
///
var onThemeChange: ((RDEPUBReaderTheme) -> Void)?
// MARK:
///
private enum ThemePreset: Int, CaseIterable {
case light
case yellow
@@ -60,7 +75,9 @@ final class RDEPUBReaderSettingsViewController: UIViewController {
}()
private var themeButtons: [UIButton] = []
/// 1.3 1.6 1.9
private let lineHeightValues: [CGFloat] = [1.3, 1.6, 1.9]
///
private var currentConfiguration: RDEPUBReaderConfiguration
init(configuration: RDEPUBReaderConfiguration, brightness: CGFloat) {
@@ -1,9 +1,17 @@
import Foundation
// MARK: -
///
/// depth
public struct RDEPUBReaderTableOfContentsItem: Equatable {
///
public var title: String
/// href EPUB
public var href: String
/// 0
public var depth: Int
/// 1 nil
public var pageNumber: Int?
public init(title: String, href: String, depth: Int, pageNumber: Int? = nil) {
@@ -1,13 +1,37 @@
import UIKit
// MARK: -
/// EPUB
///
/// 绿
public struct RDEPUBReaderTheme: Equatable {
// MARK:
///
public var contentBackgroundColor: UIColor
///
public var contentTextColor: UIColor
// MARK:
///
public var toolBackgroundColor: UIColor
/// /
public var toolControlTextColor: UIColor
///
public var toolControlBorderUnselectColor: UIColor
/// 线
public var toolLineColor: UIColor
///
/// - Parameters:
/// - contentBackgroundColor:
/// - contentTextColor:
/// - toolBackgroundColor:
/// - toolControlTextColor:
/// - toolControlBorderUnselectColor:
/// - toolLineColor: 线
public init(
contentBackgroundColor: UIColor,
contentTextColor: UIColor,
@@ -24,6 +48,9 @@ public struct RDEPUBReaderTheme: Equatable {
self.toolLineColor = toolLineColor
}
// MARK:
///
public static let light = RDEPUBReaderTheme(
contentBackgroundColor: .white,
contentTextColor: .black,
@@ -33,6 +60,7 @@ public struct RDEPUBReaderTheme: Equatable {
toolLineColor: UIColor.lightGray.withAlphaComponent(0.5)
)
///
public static let dark = RDEPUBReaderTheme(
contentBackgroundColor: .black,
contentTextColor: .white,
@@ -42,6 +70,7 @@ public struct RDEPUBReaderTheme: Equatable {
toolLineColor: UIColor.lightGray.withAlphaComponent(0.5)
)
///
public static let yellow = RDEPUBReaderTheme(
contentBackgroundColor: UIColor(red: 0.89, green: 0.87, blue: 0.79, alpha: 1),
contentTextColor: .black,
@@ -51,6 +80,7 @@ public struct RDEPUBReaderTheme: Equatable {
toolLineColor: UIColor.lightGray.withAlphaComponent(0.5)
)
/// 绿绿
public static let green = RDEPUBReaderTheme(
contentBackgroundColor: UIColor(red: 0.87, green: 0.91, blue: 0.82, alpha: 1),
contentTextColor: .black,
@@ -60,6 +90,7 @@ public struct RDEPUBReaderTheme: Equatable {
toolLineColor: UIColor.lightGray.withAlphaComponent(0.5)
)
///
public static let pink = RDEPUBReaderTheme(
contentBackgroundColor: UIColor(red: 1, green: 0.89, blue: 0.91, alpha: 1),
contentTextColor: .black,
@@ -69,6 +100,7 @@ public struct RDEPUBReaderTheme: Equatable {
toolLineColor: UIColor.lightGray.withAlphaComponent(0.5)
)
///
public static let blue = RDEPUBReaderTheme(
contentBackgroundColor: UIColor(red: 0.8, green: 0.84, blue: 0.89, alpha: 1),
contentTextColor: .black,
@@ -79,11 +111,15 @@ public struct RDEPUBReaderTheme: Equatable {
)
}
// MARK: - CSS
extension RDEPUBReaderTheme {
/// CSS EPUB HTML
var themeBackgroundColorCSS: String {
contentBackgroundColor.ss_cssString
}
/// CSS EPUB HTML
var themeTextColorCSS: String {
contentTextColor.ss_cssString
}
@@ -1,7 +1,13 @@
import UIKit
// MARK: -
///
/// 线
open class RDEPUBReaderToolView: UIView {
/// 线
private let lineView = UIView()
/// 线
private let lineHeight: CGFloat = 0.5
override init(frame: CGRect) {
@@ -19,15 +25,24 @@ open class RDEPUBReaderToolView: UIView {
lineView.frame = lineFrame(in: bounds)
}
///
/// - Parameter theme:
open func apply(theme: RDEPUBReaderTheme) {
backgroundColor = .white
lineView.backgroundColor = UIColor.lightGray.withAlphaComponent(0.5)
}
/// 线线
/// - Parameter bounds: bounds
/// - Returns: 线 frame
open func lineFrame(in bounds: CGRect) -> CGRect {
CGRect(x: 0, y: 0, width: bounds.width, height: lineHeight)
}
}
// MARK: -
///
/// UIButton
final class RDEPUBReaderTintButton: UIButton {
}
@@ -1,7 +1,14 @@
import UIKit
// MARK: -
///
///
/// iOS 13+ SF Symbols 退
public final class RDEPUBReaderTopToolView: RDEPUBReaderToolView {
///
var onBack: (() -> Void)?
///
var onToggleBookmark: (() -> Void)?
private let backButton = RDEPUBReaderTintButton(type: .system)
@@ -1,21 +1,41 @@
import UIKit
// MARK: -
///
///
struct RDEPUBTextOverlayDecoration {
///
enum Kind: String {
///
case selection
///
case highlight
/// 线
case underline
///
case search
///
case activeSearch
///
case locate
}
///
var kind: Kind
///
var absoluteRange: NSRange
///
var rects: [CGRect]
///
var color: UIColor
}
// MARK: -
///
///
/// 使 Core Graphics 线
final class RDEPUBSelectionOverlayView: UIView {
private(set) var page: RDEPUBTextPage?
private var snapshot: RDEPUBPageLayoutSnapshot?
@@ -5,12 +5,22 @@ import Foundation
import DTCoreText
#endif
// MARK: -
///
///
protocol RDEPUBTextContentViewDelegate: AnyObject {
///
func textContentView(_ contentView: RDEPUBTextContentView, didChangeSelection selection: RDEPUBSelection?)
/// //
func textContentView(_ contentView: RDEPUBTextContentView, didRequestSelectionAction action: RDEPUBAnnotationMenuAction)
}
// MARK: -
/// UITextView UIMenuItem
final class RDEPUBSelectableTextView: UITextView {
///
var onSelectionAction: ((RDEPUBAnnotationMenuAction) -> Void)?
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
@@ -37,6 +47,10 @@ final class RDEPUBSelectableTextView: UITextView {
}
}
// MARK: - DTCoreText
/// DTCoreText Core Text
/// DTCoreText UIView UITextView
#if canImport(DTCoreText)
final class RDEPUBDirectCoreTextPageView: UIView {
var layoutFrame: DTCoreTextLayoutFrame? {
@@ -73,6 +87,14 @@ final class RDEPUBDirectCoreTextPageView: UIView {
}
#endif
// MARK: -
/// EPUB
///
/// 1. DTCoreText CoreText
/// 2. 退 UITextView attributedText
///
///
final class RDEPUBTextContentView: UIView {
private var contentInsets: UIEdgeInsets = .zero
private var currentPage: RDEPUBTextPage?
@@ -1,14 +1,29 @@
import UIKit
// MARK: - Web
/// Web
/// WebView
protocol RDEPUBWebContentViewDelegate: AnyObject {
/// WebView
func epubWebContentView(_ contentView: RDEPUBWebContentView, didUpdateLocation location: RDEPUBLocation, spineIndex: Int)
/// WebView
func epubWebContentView(_ contentView: RDEPUBWebContentView, didChangeSelection selection: RDEPUBSelection?, spineIndex: Int)
///
func epubWebContentView(_ contentView: RDEPUBWebContentView, didRequestSelectionAction action: RDEPUBAnnotationMenuAction)
///
func epubWebContentView(_ contentView: RDEPUBWebContentView, didActivateInternalLink location: RDEPUBLocation, fromSpineIndex: Int)
///
func epubWebContentView(_ contentView: RDEPUBWebContentView, didActivateExternalLink url: URL)
/// WebView JavaScript
func epubWebContentView(_ contentView: RDEPUBWebContentView, didLogJavaScriptError message: String)
}
// MARK: - Web
/// EPUB Web
/// RDEPUBWebView/
/// EPUB Native Text
final class RDEPUBWebContentView: UIView {
weak var delegate: RDEPUBWebContentViewDelegate?
@@ -0,0 +1,221 @@
//
// RDURLReaderController.swift
// RDReaderDemo
//
// URL
// ReadViewSDK 使
// - .epub RDEPUBReaderController
// - RDPlainTextBookBuilder RDEPUBReaderController
// - 退 UITextView
//
// RDReaderView UI
//
import UIKit
/// URL
/// URL
/// - EPUB 使 RDEPUBReaderController
/// - RDPlainTextBookBuilder 使 RDEPUBReaderController
/// - 退 UITextView
///
/// 使
/// ```swift
/// let controller = RDURLReaderController(bookURL: fileURL)
/// navigationController?.pushViewController(controller, animated: true)
/// ```
public final class RDURLReaderController: UIViewController {
/// URL
private let bookURL: URL
/// EPUB
private let epubConfiguration: RDEPUBReaderConfiguration
///
private var embeddedController: UIViewController?
///
/// - Parameters:
/// - bookURL: URL
/// - epubConfiguration: EPUB 使
public init(
bookURL: URL,
epubConfiguration: RDEPUBReaderConfiguration = RDEPUBReaderConfiguration()
) {
self.bookURL = bookURL
self.epubConfiguration = epubConfiguration
super.init(nibName: nil, bundle: nil)
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
///
public override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
title = bookURL.deletingPathExtension().lastPathComponent
embedReaderController()
}
/// Demo
/// - Parameter displayType:
public func applyDemoDisplayType(_ displayType: RDReaderView.DisplayType) {
readerController?.configuration.displayType = displayType
logDemoState(prefix: "display=\(displayType.demoArgumentValue)")
}
/// Demo
/// - Parameters:
/// - pageNumber:
/// - animated:
/// - Returns:
@discardableResult
public func goToDemoPage(_ pageNumber: Int, animated: Bool = false) -> Bool {
let moved = readerController?.go(toPageNumber: pageNumber, animated: animated) ?? false
if moved {
logDemoState(prefix: "page=\(pageNumber)")
}
return moved
}
/// Demo
///
/// - Parameters:
/// - displayTypes:
/// - initialPageNumber:
/// - stepDelay:
public func runDemoDisplaySequence(
_ displayTypes: [RDReaderView.DisplayType],
initialPageNumber: Int? = nil,
stepDelay: TimeInterval = 1.0
) {
let normalizedDelay = max(stepDelay, 0.1)
if let initialPageNumber {
DispatchQueue.main.asyncAfter(deadline: .now() + normalizedDelay) { [weak self] in
_ = self?.goToDemoPage(initialPageNumber)
}
}
for (index, displayType) in displayTypes.enumerated() {
let delay = normalizedDelay * Double(index + 1 + (initialPageNumber == nil ? 0 : 1))
DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in
self?.applyDemoDisplayType(displayType)
}
}
}
///
/// Child View Controller
private func embedReaderController() {
let controller: UIViewController
if bookURL.pathExtension.lowercased() == "epub" {
controller = RDEPUBReaderController(
epubURL: bookURL,
configuration: epubConfiguration
)
} else {
let bookIdentifier = bookURL.lastPathComponent
let bookTitle = bookURL.deletingPathExtension().lastPathComponent
let pageSize = currentTextPageSize()
let renderStyle = currentTextRenderStyle()
let builder = RDPlainTextBookBuilder()
if let textBook = try? builder.build(textFileURL: bookURL, pageSize: pageSize, style: renderStyle) {
controller = RDEPUBReaderController(
textBook: textBook,
bookIdentifier: bookIdentifier,
title: bookTitle,
textFileURL: bookURL,
configuration: epubConfiguration
)
} else {
// 退
let fallback = UIViewController()
let textView = UITextView()
textView.isEditable = false
textView.text = rd_decodeTextFile(url: bookURL)
fallback.view = textView
controller = fallback
}
}
embeddedController = controller
addChild(controller)
controller.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(controller.view)
NSLayoutConstraint.activate([
controller.view.topAnchor.constraint(equalTo: view.topAnchor),
controller.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
controller.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
controller.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
controller.didMove(toParent: self)
}
/// EPUB
private var readerController: RDEPUBReaderController? {
embeddedController as? RDEPUBReaderController
}
/// Demo
private func logDemoState(prefix: String) {
guard let readerController else { return }
let location = readerController.currentLocation
let href = location?.href ?? "nil"
let progression = location.map { String(format: "%.4f", $0.navigationProgression) } ?? "nil"
let page = readerController.currentPageNumber.map(String.init) ?? "nil"
print("[ReadViewDemo] automation \(prefix) -> page \(page) href \(href) progression \(progression)")
}
///
///
private func currentTextPageSize() -> CGSize {
let viewportSize = UIScreen.main.bounds.size
let insets = epubConfiguration.reflowableContentInsets
return CGSize(
width: max(viewportSize.width - insets.left - insets.right, 1),
height: max(viewportSize.height - insets.top - insets.bottom, 1)
)
}
///
private func currentTextRenderStyle() -> RDEPUBTextRenderStyle {
let font = UIFont.systemFont(ofSize: epubConfiguration.fontSize)
let lineSpacing = max(font.lineHeight * (epubConfiguration.lineHeightMultiple - 1), 4)
return RDEPUBTextRenderStyle(
font: font,
lineSpacing: lineSpacing,
textColor: epubConfiguration.theme.contentTextColor,
backgroundColor: epubConfiguration.theme.contentBackgroundColor
)
}
///
/// UTF-8 GBK GB2312
private func rd_decodeTextFile(url: URL) -> String {
if let content = try? NSString(contentsOf: url, encoding: String.Encoding.utf8.rawValue) as String {
return content
}
if let content = try? NSString(contentsOf: url, encoding: 0x80000632) as String {
return content
}
if let content = try? NSString(contentsOf: url, encoding: 0x80000631) as String {
return content
}
return ""
}
}
/// Demo
private extension RDReaderView.DisplayType {
/// Demo
var demoArgumentValue: String {
switch self {
case .pageCurl:
return "pageCurl"
case .horizontalScroll:
return "horizontalScroll"
case .verticalScroll:
return "verticalScroll"
}
}
}