Skip to content

Commit 1f15efa

Browse files
committed
Add a Navigator guide
1 parent 68fe1ed commit 1f15efa

File tree

4 files changed

+200
-76
lines changed

4 files changed

+200
-76
lines changed

Documentation/Guides/Getting Started.md

+5-65
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ The toolkit is divided into separate packages that can be used independently.
1010

1111
* `R2Shared` contains shared `Publication` models and utilities.
1212
* `R2Streamer` parses publication files (e.g. an EPUB) into a `Publication` object.
13-
* `R2Navigator` renders the content of a publication.
13+
* [`R2Navigator` renders the content of a publication](Navigator/Navigator.md).
1414

1515
### Specialized packages
1616

1717
* `ReadiumOPDS` parses [OPDS catalog feeds](https://opds.io) (both OPDS 1 and 2).
18-
* `ReadiumLCP` downloads and decrypts [LCP-protected publications](https://www.edrlab.org/readium-lcp/).
18+
* [`ReadiumLCP` downloads and decrypts LCP-protected publications](Readium%20LCP.md).
1919

2020
### Adapters to third-party dependencies
2121

@@ -94,73 +94,13 @@ You can retrieve the publication cover using `publication.cover`. Avoid calling
9494

9595
You can use a Readium navigator to present the publication to the user. The `Navigator` renders resources on the screen and offers APIs and user interactions for navigating the contents.
9696

97-
:warning: The `Navigator` does not have a user interface other than the view that displays the publication. The application is responsible for providing a user interface with bookmark buttons, a progress bar, etc.
98-
99-
The Readium toolkit ships with one `Navigator` implementation per [publication profile](https://readium.org/webpub-manifest/profiles/). You can use `publication.conformsTo()` to determine the profile of a publication.
100-
101-
| Profile | Navigator | Formats |
102-
|-------------|-------------------------------|-----------------------------------------------------------------------|
103-
| `epub` | `EPUBNavigatorViewController` | EPUB (`.epub`), Readium Web Publication (`.webpub`) |
104-
| `pdf` | `PDFNavigatorViewController` | PDF (`.pdf`), LCP-protected PDF (`.lcpdf`) |
105-
| `audiobook` | `AudioNavigator` | Zipped Audio Book (`.zab`), Readium Audiobook (`.audiobook`, `.lcpa`) |
106-
| `divina` | `CBZNavigatorViewController` | Zipped Comic Book (`cbz`), Readium Divina (`.divina`) |
107-
10897
```swift
109-
if publication.conformsTo(.epub) {
110-
let navigator = try EPUBNavigatorViewController(
111-
publication: publication,
112-
initialLocation: lastReadLocation,
113-
httpServer: GCDHTTPServer.shared
114-
)
115-
116-
hostViewController.present(navigator, animated: true)
117-
}
118-
```
119-
120-
:point_up: The HTTP server is used to serve the publication resources to the navigator's web views. You may use your own implementation, or the recommended `GCDHTTPServer` which is part of the `ReadiumAdapterGCDWebServer` package.
121-
122-
## Navigating the contents of the publication (`R2Navigator`)
123-
124-
The `Navigator` offers various `go` APIs for navigating the publication. For instance:
125-
126-
* to the previous or next pages: `navigator.goForward()` or `navigator.goBackward()`
127-
* to a link from the `publication.tableOfContents` or `publication.readingOrder`: `navigator.go(to: link)`
128-
* to a locator from a search result: `navigator.go(to: locator)`
129-
130-
## Saving and restoring the last read location (`R2Navigator`)
131-
132-
You can observe the current position in the publication by implementing a `NavigatorDelegate`.
133-
134-
```swift
135-
navigator.delegate = MyNavigatorDelegate()
136-
137-
class MyNavigatorDelegate: NavigatorDelegate {
138-
139-
override func navigator(_ navigator: Navigator, locationDidChange locator: Locator) {
140-
if let position = locator.locations.position {
141-
print("At position \(position) on \(publication.positions.count)")
142-
}
143-
if let progression = locator.locations.progression {
144-
return "Progression in the current resource: \(progression)%"
145-
}
146-
if let totalProgression = locator.locations.totalProgression {
147-
return "Total progression in the publication: \(progression)%"
148-
}
149-
150-
// Save the position in your bookshelf database
151-
database.saveLastReadLocation(locator.jsonString)
152-
}
153-
}
154-
```
155-
156-
The `Locator` object may be serialized to JSON in your database, and deserialized to set the initial location when creating the navigator.
157-
158-
```swift
159-
let lastReadLocation = Locator(jsonString: dabase.lastReadLocation())
160-
16198
let navigator = try EPUBNavigatorViewController(
16299
publication: publication,
163100
initialLocation: lastReadLocation,
164101
httpServer: GCDHTTPServer.shared
165102
)
103+
104+
hostViewController.present(navigator, animated: true)
166105
```
106+
Please refer to the [Navigator guide](Navigator/Navigator.md) for more information.
+194
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
# Navigator
2+
3+
You can use a Readium Navigator to present the publication to the user. The `Navigator` renders resources on the screen and offers APIs and user interactions for navigating the contents.
4+
5+
:warning: Navigators do not have user interfaces besides the view that displays the publication. Applications are responsible for providing a user interface with bookmark buttons, a progress bar, etc.
6+
7+
## Default implementations
8+
9+
The Readium toolkit comes with several `Navigator` implementations for different publication profiles. Some are `UIViewController`s, designed to be added to your view hierarchy, while others are chromeless and can be used in the background.
10+
11+
| Navigator | Profile | Formats |
12+
|-------------------------------|-------------|-----------------------------------------------------------------------|
13+
| `EPUBNavigatorViewController` | `epub` | EPUB (`.epub`), Readium Web Publication (`.webpub`) |
14+
| `PDFNavigatorViewController` | `pdf` | PDF (`.pdf`), LCP-protected PDF (`.lcpdf`) |
15+
| `CBZNavigatorViewController` | `divina` | Zipped Comic Book (`cbz`), Readium Divina (`.divina`) |
16+
| `AudioNavigator` | `audiobook` | Zipped Audio Book (`.zab`), Readium Audiobook (`.audiobook`, `.lcpa`) |
17+
18+
To find out which Navigator is compatible with a publication, refer to its [profile](https://readium.org/webpub-manifest/profiles/). Use `publication.conformsTo()` to identify the publication's profile.
19+
20+
```swift
21+
if publication.conformsTo(.epub) {
22+
let navigator = try EPUBNavigatorViewController(
23+
publication: publication,
24+
initialLocation: lastReadLocation,
25+
httpServer: GCDHTTPServer.shared
26+
)
27+
28+
hostViewController.present(navigator, animated: true)
29+
}
30+
```
31+
32+
## Navigator APIs
33+
34+
Navigators implement a set of shared interfaces to help reuse the reading logic across publication profiles. For example, instead of using specific implementations like `EPUBNavigatorViewController`, use the `Navigator` interface to create a location history manager compatible with all Navigator types.
35+
36+
You can create custom Navigators and easily integrate them into your app with minimal modifications by implementing these interfaces.
37+
38+
### `Navigator` interface
39+
40+
All Navigators implement the `Navigator` interface, which provides the foundation for navigating resources in a `Publication`. You can use it to move through the publication's content or to find the current position.
41+
42+
Note that this interface does not specify how the content is presented to the user.
43+
44+
### `VisualNavigator` interface
45+
46+
Navigators rendering the content visually on the screen implement the `VisualNavigator` interface. This interface offers details about the presentation style (e.g., scrolled, right-to-left, etc.) and allows monitoring input events like taps or keyboard strokes.
47+
48+
### `SelectableNavigator` interface
49+
50+
Navigators enabling users to select parts of the content implement `SelectableNavigator`. You can use it to extract the `Locator` and content of the selected portion.
51+
52+
### `DecorableNavigator` interface
53+
54+
A Decorable Navigator is able to render decorations over a publication, such as highlights or margin icons.
55+
56+
[See the corresponding proposal for more information](https://readium.org/architecture/proposals/008-decorator-api.html).
57+
58+
## Instantiating a Navigator
59+
60+
### Visual Navigators
61+
62+
The Visual Navigators are implemented as `UIViewController` and must be added to your iOS view hierarchy to render the publication contents.
63+
64+
```swift
65+
let navigator = try EPUBNavigatorViewController(
66+
publication: publication,
67+
initialLocation: lastReadLocation,
68+
httpServer: GCDHTTPServer.shared
69+
)
70+
71+
hostViewController.present(navigator, animated: true)
72+
```
73+
74+
:point_up: The HTTP server is used to serve the publication resources to the Navigator. You may use your own implementation, or the recommended `GCDHTTPServer` which is part of the `ReadiumAdapterGCDWebServer` package.
75+
76+
### Audio Navigator
77+
78+
The `AudioNavigator` is chromeless and does not provide any user interface, allowing you to create your own custom UI.
79+
80+
```swift
81+
let navigator = AudioNavigator(
82+
publication: publication,
83+
initialLocation: lastReadLocation
84+
)
85+
86+
navigator.play()
87+
```
88+
89+
## Navigating the contents of the publication
90+
91+
The `Navigator` offers various `go` APIs for navigating the publication. For instance:
92+
93+
* to the previous or next pages: `navigator.goForward()` or `navigator.goBackward()`
94+
* to a link from the `publication.tableOfContents` or `publication.readingOrder`: `navigator.go(to: link)`
95+
* to a locator from a search result: `navigator.go(to: locator)`
96+
97+
## Reading progression
98+
99+
## Saving and restoring the last read location
100+
101+
Navigators don't store any data permanently. Therefore, it is your responsibility to save the last read location in your database and restore it when creating a new Navigator.
102+
103+
You can observe the current position in the publication by implementing a `NavigatorDelegate`.
104+
105+
```swift
106+
navigator.delegate = MyNavigatorDelegate()
107+
108+
class MyNavigatorDelegate: NavigatorDelegate {
109+
110+
override func navigator(_ navigator: Navigator, locationDidChange locator: Locator) {
111+
if let position = locator.locations.position {
112+
print("At position \(position) on \(publication.positions.count)")
113+
}
114+
if let progression = locator.locations.progression {
115+
return "Progression in the current resource: \(progression)%"
116+
}
117+
if let totalProgression = locator.locations.totalProgression {
118+
return "Total progression in the publication: \(progression)%"
119+
}
120+
121+
// Save the position in your bookshelf database
122+
database.saveLastReadLocation(locator.jsonString)
123+
}
124+
}
125+
```
126+
127+
The `Locator` object may be serialized to JSON in your database, and deserialized to set the initial location when creating the navigator.
128+
129+
```swift
130+
let lastReadLocation = Locator(jsonString: dabase.lastReadLocation())
131+
132+
let navigator = try EPUBNavigatorViewController(
133+
publication: publication,
134+
initialLocation: lastReadLocation,
135+
httpServer: GCDHTTPServer.shared
136+
)
137+
```
138+
139+
### Bookmarking the current location
140+
141+
Use a Navigator's `currentLocation` property to persists the current position, for instance as a bookmark.
142+
143+
After the user selects a bookmark from your user interface, navigate to it using `navigator.go(bookmark.locator)`.
144+
145+
### Displaying a progression slider
146+
147+
To display a percentage-based progression slider, use the `locations.totalProgression` property of the `currentLocation`. This property holds the total progression across an entire publication.
148+
149+
Given a progression from 0 to 1, you can obtain a `Locator` object from the `Publication`. This can be used to navigate to a specific percentage within the publication.
150+
151+
```swift
152+
if let locator = publication.locate(progression: 0.5) {
153+
navigator.go(to: locator)
154+
}
155+
```
156+
157+
### Displaying the number of positions
158+
159+
:warning: Readium does not have the concept of pages, as they are not useful when dealing with reflowable publications across different screen sizes. Instead, we use [**positions**](https://readium.org/architecture/models/locators/positions/) which remain stable even when the user changes the font size or device.
160+
161+
Not all Navigators provide positions, but most `VisualNavigator` implementations do. Verify if `publication.positions` is not empty to determine if it is supported.
162+
163+
To find the total positions in the publication, use `publication.positions.count`. You can get the current position with `navigator.currentLocation?.locations.position`.
164+
165+
## Navigating with edge taps and keyboard arrows
166+
167+
Readium provides a `DirectionalNavigationAdapter` helper to turn pages using arrow and space keys or screen taps.
168+
169+
You can use it from your `VisualNavigatorDelegate` implementation:
170+
171+
```swift
172+
extension MyReader: VisualNavigatorDelegate {
173+
174+
func navigator(_ navigator: VisualNavigator, didTapAt point: CGPoint) {
175+
// Turn pages when tapping the edge of the screen.
176+
guard !DirectionalNavigationAdapter(navigator: navigator).didTap(at: point) else {
177+
return
178+
}
179+
180+
toggleNavigationBar()
181+
}
182+
183+
func navigator(_ navigator: VisualNavigator, didPressKey event: KeyEvent) {
184+
// Turn pages when pressing the arrow keys.
185+
DirectionalNavigationAdapter(navigator: navigator).didPressKey(event: event)
186+
}
187+
}
188+
```
189+
190+
`DirectionalNavigationAdapter` offers a lot of customization options. Take a look at its API.
191+
192+
## User preferences
193+
194+
Readium Navigators support user preferences, such as font size or background color. Take a look at [the Preferences API guide](Preferences.md) for more information.

Documentation/Guides/Navigator/Preferences.md

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# Configuring the Navigator
22

3-
:warning: The Navigator Setting API is brand new and currently only available with `EPUBNavigatorViewController` and `PDFNavigatorViewController`.
4-
53
Take a look at the [migration guide](../Migration%20Guide.md) if you are already using the legacy EPUB settings.
64

75
## Overview

README.md

+1-9
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,7 @@
22

33
[Readium Mobile](https://github.com/readium/mobile) is a toolkit for ebooks, audiobooks and comics written in Swift & Kotlin.
44

5-
This toolkit is a modular project, which follows the [Readium Architecture](https://github.com/readium/architecture).
6-
7-
* [`R2Shared`](Sources/Shared) – Shared `Publication` models and utilities
8-
* [`R2Streamer`](Sources/Streamer) – Publication parsers and local HTTP server
9-
* [`R2Navigator`](Sources/Navigator) – Plain `UIViewController` classes rendering publications
10-
* [`ReadiumOPDS`](Sources/OPDS) – Parsers for OPDS catalog feeds
11-
* [`ReadiumLCP`](Sources/LCP) – Service and models for [Readium LCP](https://www.edrlab.org/readium-lcp/)
12-
13-
Take a look at the [guide to get started](Documentation/Guides/Getting%20Started.md). A [Test App](TestApp) demonstrates how to integrate the Readium Swift toolkit in your own reading app.
5+
:point_up: **Take a look at the [guide to get started](Documentation/Guides/Getting%20Started.md).** A [Test App](TestApp) demonstrates how to integrate the Readium Swift toolkit in your own reading app.
146

157
## Minimum Requirements
168

0 commit comments

Comments
 (0)