ReadViewSDK/Sources/RDReaderView/RDURLReaderController.swift

166 lines
6.3 KiB
Swift

import UIKit
public final class RDURLReaderController: UIViewController {
private let bookURL: URL
private let epubConfiguration: RDEPUBReaderConfiguration
private var embeddedController: UIViewController?
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()
}
public func applyDemoDisplayType(_ displayType: RDReaderView.DisplayType) {
readerController?.configuration.displayType = displayType
logDemoState(prefix: "display=\(displayType.demoArgumentValue)")
}
@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
}
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)
}
}
}
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)
}
private var readerController: RDEPUBReaderController? {
embeddedController as? RDEPUBReaderController
}
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
)
}
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 ""
}
}
private extension RDReaderView.DisplayType {
var demoArgumentValue: String {
switch self {
case .pageCurl:
return "pageCurl"
case .horizontalScroll:
return "horizontalScroll"
case .verticalScroll:
return "verticalScroll"
}
}
}