126 lines
4.5 KiB
Swift
126 lines
4.5 KiB
Swift
import UIKit
|
|
|
|
final class RDEPUBReaderViewportMonitor {
|
|
private unowned let context: RDEPUBReaderContext
|
|
|
|
private var lastAppliedViewportSignature: RDEPUBViewportSignature?
|
|
private var pendingViewportChangeReason: RDEPUBViewportChangeReason?
|
|
private var pendingPresentationRestoreLocation: RDEPUBLocation?
|
|
private var isWaitingForViewportTransitionCompletion = false
|
|
|
|
init(context: RDEPUBReaderContext) {
|
|
self.context = context
|
|
}
|
|
|
|
private var controller: RDEPUBReaderController? {
|
|
context.controller
|
|
}
|
|
|
|
func viewDidLayoutSubviews() {
|
|
guard let controller else { return }
|
|
guard let viewportSignature = currentViewportSignature() else { return }
|
|
|
|
if !controller.didStartInitialLoad {
|
|
lastAppliedViewportSignature = viewportSignature
|
|
controller.startInitialLoadIfNeeded()
|
|
return
|
|
}
|
|
|
|
guard controller.publication != nil || controller.isExternalTextBook else {
|
|
lastAppliedViewportSignature = viewportSignature
|
|
return
|
|
}
|
|
|
|
guard !isWaitingForViewportTransitionCompletion else {
|
|
return
|
|
}
|
|
|
|
handleViewportChangeIfNeeded(reason: .viewLayout, viewportSignature: viewportSignature)
|
|
}
|
|
|
|
func viewWillTransition(with coordinator: UIViewControllerTransitionCoordinator) {
|
|
guard let controller else { return }
|
|
guard controller.didStartInitialLoad else { return }
|
|
pendingPresentationRestoreLocation = controller.currentVisibleLocation() ?? controller.persistenceLocation()
|
|
isWaitingForViewportTransitionCompletion = true
|
|
|
|
coordinator.animate(alongsideTransition: nil) { [weak self] _ in
|
|
guard let self, let controller = self.controller else { return }
|
|
self.isWaitingForViewportTransitionCompletion = false
|
|
controller.view.layoutIfNeeded()
|
|
self.handleViewportChangeIfNeeded(reason: .orientationTransition)
|
|
}
|
|
}
|
|
|
|
func resetForReload() {
|
|
lastAppliedViewportSignature = currentViewportSignature()
|
|
pendingViewportChangeReason = nil
|
|
pendingPresentationRestoreLocation = nil
|
|
isWaitingForViewportTransitionCompletion = false
|
|
}
|
|
|
|
func consumePendingPresentationRestoreLocation() -> RDEPUBLocation? {
|
|
defer { pendingPresentationRestoreLocation = nil }
|
|
return pendingPresentationRestoreLocation
|
|
}
|
|
|
|
func capturePendingPresentationRestoreLocation() {
|
|
guard let controller else { return }
|
|
pendingPresentationRestoreLocation = controller.currentVisibleLocation() ?? controller.persistenceLocation()
|
|
}
|
|
|
|
func processPendingChangeAfterPagination() {
|
|
guard let pendingReason = pendingViewportChangeReason else { return }
|
|
pendingViewportChangeReason = nil
|
|
DispatchQueue.main.async { [weak self] in
|
|
self?.handleViewportChangeIfNeeded(reason: pendingReason)
|
|
}
|
|
}
|
|
|
|
func currentViewportSignature() -> RDEPUBViewportSignature? {
|
|
guard let controller else { return nil }
|
|
let containerSize = controller.readerView.bounds.size == .zero ? controller.view.bounds.size : controller.readerView.bounds.size
|
|
guard containerSize.width > 0, containerSize.height > 0 else { return nil }
|
|
let insets = controller.view.safeAreaInsets
|
|
return RDEPUBViewportSignature(
|
|
width: containerSize.width,
|
|
height: containerSize.height,
|
|
safeTop: insets.top,
|
|
safeLeft: insets.left,
|
|
safeBottom: insets.bottom,
|
|
safeRight: insets.right
|
|
)
|
|
}
|
|
|
|
func handleViewportChangeIfNeeded(
|
|
reason: RDEPUBViewportChangeReason,
|
|
viewportSignature: RDEPUBViewportSignature? = nil
|
|
) {
|
|
guard let controller else { return }
|
|
guard controller.didStartInitialLoad,
|
|
let signature = viewportSignature ?? currentViewportSignature() else {
|
|
return
|
|
}
|
|
|
|
if controller.isRepaginating {
|
|
pendingViewportChangeReason = reason
|
|
return
|
|
}
|
|
|
|
if let lastAppliedViewportSignature,
|
|
!signature.differsSignificantly(from: lastAppliedViewportSignature) {
|
|
return
|
|
}
|
|
|
|
lastAppliedViewportSignature = signature
|
|
|
|
if controller.isExternalTextBook {
|
|
controller.rebuildExternalTextBook()
|
|
return
|
|
}
|
|
|
|
guard controller.publication != nil else { return }
|
|
controller.repaginatePreservingCurrentLocation()
|
|
}
|
|
}
|