@@ -11,13 +11,18 @@ import SwiftSoup
11
11
import UIKit
12
12
import WebKit
13
13
14
- public protocol EPUBNavigatorDelegate : VisualNavigatorDelegate , SelectableNavigatorDelegate {
14
+ @MainActor public protocol EPUBNavigatorDelegate : VisualNavigatorDelegate , SelectableNavigatorDelegate {
15
+ /// Called when the viewport is updated.
16
+ func navigator( _ navigator: EPUBNavigatorViewController , viewportDidChange viewport: EPUBNavigatorViewController . Viewport ? )
17
+
15
18
// MARK: - WebView Customization
16
19
17
20
func navigator( _ navigator: EPUBNavigatorViewController , setupUserScripts userContentController: WKUserContentController )
18
21
}
19
22
20
23
public extension EPUBNavigatorDelegate {
24
+ func navigator( _ navigator: EPUBNavigatorViewController , viewportDidChange viewport: EPUBNavigatorViewController . Viewport ? ) { }
25
+
21
26
func navigator( _ navigator: EPUBNavigatorViewController , setupUserScripts userContentController: WKUserContentController ) { }
22
27
}
23
28
@@ -117,6 +122,24 @@ open class EPUBNavigatorViewController: InputObservableViewController,
117
122
118
123
public weak var delegate : EPUBNavigatorDelegate ?
119
124
125
+ /// Information about the visible portion of the publication, when rendered.
126
+ public private( set) var viewport : Viewport ? {
127
+ didSet {
128
+ if oldValue != viewport {
129
+ delegate? . navigator ( self , viewportDidChange: viewport)
130
+ }
131
+ }
132
+ }
133
+
134
+ /// Information about the visible portion of the publication.
135
+ public struct Viewport : Equatable {
136
+ /// Indices of the visible reading order resources.
137
+ public var readingOrderIndices : ClosedRange < [ Link ] . Index>
138
+
139
+ /// Range of visible positions.
140
+ public var positions : ClosedRange < Int > ?
141
+ }
142
+
120
143
/// Navigation state.
121
144
private enum State : Equatable {
122
145
/// Initializing the navigator.
@@ -488,15 +511,6 @@ open class EPUBNavigatorViewController: InputObservableViewController,
488
511
paginationView? . currentIndex ?? 0
489
512
}
490
513
491
- // Reading order index of the left-most resource in the visible spread.
492
- private var currentResourceIndex : Int ? {
493
- guard spreads. indices. contains ( currentSpreadIndex) else {
494
- return nil
495
- }
496
-
497
- return readingOrder. firstIndexWithHREF ( spreads [ currentSpreadIndex] . left. url ( ) )
498
- }
499
-
500
514
private var reloadSpreadsContinuations = [ CheckedContinuation < Void , Never > ] ( )
501
515
private var needsReloadSpreads = false
502
516
@@ -542,8 +556,12 @@ open class EPUBNavigatorViewController: InputObservableViewController,
542
556
spread: viewModel. spreadEnabled
543
557
)
544
558
545
- let initialIndex : Int = {
546
- if let href = locator? . href, let foundIndex = self . spreads. firstIndexWithHREF ( href) {
559
+ let initialIndex : ReadingOrder . Index = {
560
+ if
561
+ let href = locator? . href,
562
+ let index = readingOrder. firstIndexWithHREF ( href) ,
563
+ let foundIndex = self . spreads. firstIndexWithReadingOrderIndex ( index)
564
+ {
547
565
return foundIndex
548
566
} else {
549
567
return 0
@@ -561,9 +579,16 @@ open class EPUBNavigatorViewController: InputObservableViewController,
561
579
}
562
580
563
581
private func loadedSpreadViewForHREF< T: URLConvertible > ( _ href: T ) -> EPUBSpreadView ? {
564
- paginationView? . loadedViews
582
+ guard
583
+ let loadedViews = paginationView? . loadedViews,
584
+ let index = readingOrder. firstIndexWithHREF ( href)
585
+ else {
586
+ return nil
587
+ }
588
+
589
+ return loadedViews
565
590
. compactMap { _, view in view as? EPUBSpreadView }
566
- . first { $0. spread. links . firstWithHREF ( href ) != nil }
591
+ . first { $0. spread. contains ( index : index ) }
567
592
}
568
593
569
594
// MARK: - Navigator
@@ -582,45 +607,64 @@ open class EPUBNavigatorViewController: InputObservableViewController,
582
607
)
583
608
}
584
609
585
- private func computeCurrentLocation ( ) async -> Locator ? {
610
+ private func computeCurrentLocationAndViewport ( ) async -> ( Locator ? , Viewport ? ) {
586
611
if case . initializing = state {
587
612
assertionFailure ( " Cannot update current location when initializing the navigator " )
588
- return nil
613
+ return ( nil , nil )
589
614
}
590
615
591
616
// Returns any pending locator to prevent returning invalid locations
592
617
// while loading it.
593
618
if let pendingLocator = state. pendingLocator {
594
- return pendingLocator
619
+ return ( pendingLocator, nil )
595
620
}
596
621
597
622
guard let spreadView = paginationView? . currentView as? EPUBSpreadView else {
598
- return nil
623
+ return ( nil , nil )
599
624
}
600
625
601
- let link = spreadView. focusedResource ?? spreadView. spread. leading
626
+ let index = spreadView. focusedResource ?? spreadView. spread. leading
627
+ let link = readingOrder [ index]
602
628
let href = link. url ( )
603
- let progression = min ( max ( spreadView. progression ( in: href) , 0.0 ) , 1.0 )
629
+ let progressionRange = spreadView. progression ( in: index)
630
+ let firstProgression = min ( max ( progressionRange. lowerBound, 0.0 ) , 1.0 )
631
+ let lastProgression = min ( max ( progressionRange. upperBound, 0.0 ) , 1.0 )
632
+
633
+ let location : Locator ?
634
+ var viewport = Viewport (
635
+ readingOrderIndices: spreadView. spread. readingOrderIndices,
636
+ positions: nil
637
+ )
604
638
605
639
if
606
640
// The positions are not always available, for example a Readium
607
641
// WebPub doesn't have any unless a Publication Positions Web
608
642
// Service is provided
609
643
let index = readingOrder. firstIndexWithHREF ( href) ,
610
644
let positionList = positionsByReadingOrder. getOrNil ( index) ,
611
- positionList. count > 0
645
+ positionList. count > 0 ,
646
+ let positionOffset = positionList [ 0 ] . locations. position
612
647
{
613
648
// Gets the current locator from the positionList, and fill its missing data.
614
- let positionIndex = Int ( ceil ( progression * Double( positionList. count - 1 ) ) )
615
- return await positionList [ positionIndex] . copy (
649
+ let firstPositionIndex = Int ( ceil ( firstProgression * Double( positionList. count - 1 ) ) )
650
+ let lastPositionIndex = ( lastProgression == 1.0 )
651
+ ? positionList. count - 1
652
+ : max ( firstPositionIndex, Int ( ceil ( lastProgression * Double( positionList. count - 1 ) ) ) - 1 )
653
+
654
+ location = await positionList [ firstPositionIndex] . copy (
616
655
title: tableOfContentsTitleByHref [ equivalent: href] ,
617
- locations: { $0. progression = progression }
656
+ locations: { $0. progression = firstProgression }
618
657
)
658
+
659
+ viewport. positions = ( positionOffset + firstPositionIndex) ... ( positionOffset + lastPositionIndex)
660
+
619
661
} else {
620
- return await publication. locate ( link) ? . copy (
621
- locations: { $0. progression = progression }
662
+ location = await publication. locate ( link) ? . copy (
663
+ locations: { $0. progression = firstProgression }
622
664
)
623
665
}
666
+
667
+ return ( location, viewport)
624
668
}
625
669
626
670
public func firstVisibleElementLocator( ) async -> Locator ? {
@@ -643,7 +687,7 @@ open class EPUBNavigatorViewController: InputObservableViewController,
643
687
return
644
688
}
645
689
646
- currentLocation = await computeCurrentLocation ( )
690
+ ( currentLocation, viewport ) = await computeCurrentLocationAndViewport ( )
647
691
648
692
if
649
693
let delegate = delegate,
@@ -660,7 +704,8 @@ open class EPUBNavigatorViewController: InputObservableViewController,
660
704
661
705
guard
662
706
let paginationView = paginationView,
663
- let spreadIndex = spreads. firstIndexWithHREF ( locator. href) ,
707
+ let index = readingOrder. firstIndexWithHREF ( locator. href) ,
708
+ let spreadIndex = spreads. firstIndexWithReadingOrderIndex ( index) ,
664
709
on ( . jump( locator) )
665
710
else {
666
711
return false
@@ -900,7 +945,8 @@ extension EPUBNavigatorViewController: EPUBNavigatorViewModelDelegate {
900
945
for (_, view) in paginationView. loadedViews {
901
946
guard
902
947
let view = view as? EPUBSpreadView ,
903
- view. spread. links. firstWithHREF ( href) != nil
948
+ let index = readingOrder. firstIndexWithHREF ( href) ,
949
+ view. spread. contains ( index: index)
904
950
else {
905
951
continue
906
952
}
@@ -943,7 +989,10 @@ extension EPUBNavigatorViewController: EPUBSpreadViewDelegate {
943
989
}
944
990
. joined ( separator: " \n " )
945
991
946
- for link in spreadView. spread. links {
992
+ let links = spreadView. spread. readingOrderIndices
993
+ . compactMap { readingOrder. getOrNil ( $0) }
994
+
995
+ for link in links {
947
996
let href = link. url ( )
948
997
for (group, decorations) in decorations {
949
998
let decorations = decorations
0 commit comments