Skip to content
This repository was archived by the owner on Aug 12, 2022. It is now read-only.

Commit 4503189

Browse files
Merge pull request #63 from readium/fixes/scrollmode
Fix EPUB chapter positionning when scroll mode is enabled
2 parents df76dcd + 524ff01 commit 4503189

File tree

5 files changed

+92
-54
lines changed

5 files changed

+92
-54
lines changed

r2-navigator-swift/EPUB/DocumentWebView.swift

Lines changed: 58 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ import R2Shared
1616
protocol DocumentWebViewDelegate: class {
1717
func willAnimatePageChange()
1818
func didEndPageAnimation()
19-
func displayRightDocument(animated: Bool, completion: @escaping () -> Void)
20-
func displayLeftDocument(animated: Bool, completion: @escaping () -> Void)
19+
@discardableResult
20+
func displayRightDocument(animated: Bool, completion: @escaping () -> Void) -> Bool
21+
@discardableResult
22+
func displayLeftDocument(animated: Bool, completion: @escaping () -> Void) -> Bool
2123
func webView(_ webView: DocumentWebView, didTapAt point: CGPoint)
2224
func handleTapOnLink(with url: URL)
2325
func handleTapOnInternalLink(with href: String)
@@ -218,51 +220,66 @@ class DocumentWebView: UIView, Loggable {
218220
/// - Parameter body: Unused.
219221
private func documentDidLoad(body: Any) {
220222
documentLoaded = true
221-
223+
224+
applyUserSettingsStyle()
225+
222226
// FIXME: We need to give the CSS and webview time to layout correctly. 0.2 seconds seems like a good value for it to work on an iPhone 5s. Look into solving this better
223227
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
224-
self.activityIndicatorView?.stopAnimating()
225-
UIView.animate(withDuration: self.animatedLoad ? 0.3 : 0, animations: {
226-
self.scrollView.alpha = 1
227-
})
228+
self.scrollToInitialPosition {
229+
self.activityIndicatorView?.stopAnimating()
230+
UIView.animate(withDuration: self.animatedLoad ? 0.3 : 0, animations: {
231+
self.scrollView.alpha = 1
232+
})
233+
}
228234
}
229-
230-
applyUserSettingsStyle()
231-
scrollToInitialPosition()
232235
}
233-
236+
234237
// Scroll at position 0-1 (0%-100%)
235-
func scrollAt(position: Double) {
236-
guard position >= 0 && position <= 1 else { return }
237-
238-
let dir = readingProgression.rawValue
239-
evaluateScriptInResource("readium.scrollToPosition(\'\(position)\', \'\(dir)\')")
238+
func scrollAt(position: Double, completion: @escaping () -> Void = {}) {
239+
guard position >= 0 && position <= 1 else {
240+
log(.warning, "Scrolling to invalid position \(position)")
241+
completion()
242+
return
243+
}
244+
245+
// Note: The JS layer does not take into account the scroll view's content inset. So it can't be used to reliably scroll to the top or the bottom of the page in scroll mode.
246+
if isScrollEnabled && [0, 1].contains(position) {
247+
var contentOffset = scrollView.contentOffset
248+
contentOffset.y = (position == 0)
249+
? -scrollView.contentInset.top
250+
: (scrollView.contentSize.height - scrollView.bounds.height + scrollView.contentInset.bottom)
251+
scrollView.contentOffset = contentOffset
252+
completion()
253+
} else {
254+
let dir = readingProgression.rawValue
255+
evaluateScriptInResource("readium.scrollToPosition(\'\(position)\', \'\(dir)\')") { _, _ in completion () }
256+
}
240257
}
241258

242259
// Scroll at the tag with id `tagId`.
243-
func scrollAt(tagId: String) {
244-
evaluateScriptInResource("readium.scrollToId(\'\(tagId)\');")
260+
func scrollAt(tagId: String, completion: @escaping () -> Void = {}) {
261+
evaluateScriptInResource("readium.scrollToId(\'\(tagId)\');") { _, _ in completion() }
245262
}
246263

247264
// Scroll to .beggining or .end.
248-
func scrollAt(location: BinaryLocation) {
265+
func scrollAt(location: BinaryLocation, completion: @escaping () -> Void = {}) {
249266
switch location {
250267
case .left:
251-
scrollAt(position: 0)
268+
scrollAt(position: 0, completion: completion)
252269
case .right:
253-
scrollAt(position: 1)
270+
scrollAt(position: 1, completion: completion)
254271
}
255272
}
256273

257274
/// Moves the webView to the initial location.
258-
func scrollToInitialPosition() {
275+
func scrollToInitialPosition(completion: @escaping () -> Void = {}) {
259276
/// If the savedProgression property has been set by the navigator.
260277
if let initialPosition = progression, initialPosition > 0.0 {
261-
scrollAt(position: initialPosition)
278+
scrollAt(position: initialPosition, completion: completion)
262279
} else if let initialId = initialId {
263-
scrollAt(tagId: initialId)
280+
scrollAt(tagId: initialId, completion: completion)
264281
} else {
265-
scrollAt(location: initialLocation)
282+
scrollAt(location: initialLocation, completion: completion)
266283
}
267284
}
268285

@@ -272,7 +289,18 @@ class DocumentWebView: UIView, Loggable {
272289
}
273290

274291
func scrollTo(_ direction: ScrollDirection, animated: Bool = false, completion: @escaping () -> Void = {}) -> Bool {
275-
let viewDelegate = self.viewDelegate
292+
if isScrollEnabled {
293+
guard let viewDelegate = viewDelegate else {
294+
return false
295+
}
296+
switch direction {
297+
case .left:
298+
return viewDelegate.displayLeftDocument(animated: animated, completion: completion)
299+
case .right:
300+
return viewDelegate.displayRightDocument(animated: animated, completion: completion)
301+
}
302+
}
303+
276304
if animated {
277305
switch direction {
278306
case .left:
@@ -295,17 +323,17 @@ class DocumentWebView: UIView, Loggable {
295323
let dir = readingProgression.rawValue
296324
switch direction {
297325
case .left:
298-
evaluateScriptInResource("readium.scrollLeft(\"\(dir)\");") { result, error in
326+
evaluateScriptInResource("readium.scrollLeft(\"\(dir)\");") { [weak self] result, error in
299327
if error == nil, let success = result as? Bool, !success {
300-
viewDelegate?.displayLeftDocument(animated: animated, completion: completion)
328+
self?.viewDelegate?.displayLeftDocument(animated: animated, completion: completion)
301329
} else {
302330
completion()
303331
}
304332
}
305333
case .right:
306-
evaluateScriptInResource("readium.scrollRight(\"\(dir)\");") { result, error in
334+
evaluateScriptInResource("readium.scrollRight(\"\(dir)\");") { [weak self] result, error in
307335
if error == nil, let success = result as? Bool, !success {
308-
viewDelegate?.displayRightDocument(animated: animated, completion: completion)
336+
self?.viewDelegate?.displayRightDocument(animated: animated, completion: completion)
309337
} else {
310338
completion()
311339
}

r2-navigator-swift/EPUB/EPUBNavigatorViewController.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -334,15 +334,17 @@ extension EPUBNavigatorViewController: DocumentWebViewDelegate {
334334
}
335335

336336
/// Display next document (readingOrder item).
337-
func displayRightDocument(animated: Bool, completion: @escaping () -> Void) {
337+
@discardableResult
338+
func displayRightDocument(animated: Bool, completion: @escaping () -> Void) -> Bool {
338339
let delta = triptychView.readingProgression == .rtl ? -1 : 1
339-
goToIndex(triptychView.index + delta, animated: animated, completion: completion)
340+
return goToIndex(triptychView.index + delta, animated: animated, completion: completion)
340341
}
341342

342343
/// Display previous document (readingOrder item).
343-
func displayLeftDocument(animated: Bool, completion: @escaping () -> Void) {
344+
@discardableResult
345+
func displayLeftDocument(animated: Bool, completion: @escaping () -> Void) -> Bool {
344346
let delta = triptychView.readingProgression == .rtl ? -1 : 1
345-
goToIndex(triptychView.index - delta, animated: animated, completion: completion)
347+
return goToIndex(triptychView.index - delta, animated: animated, completion: completion)
346348
}
347349

348350
}

r2-navigator-swift/EPUB/ReflowableDocumentWebView.swift

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ final class ReflowableDocumentWebView: DocumentWebView {
2424
override func setupWebView() {
2525
super.setupWebView()
2626
scrollView.bounces = false
27-
scrollView.isPagingEnabled = true
27+
scrollView.isPagingEnabled = !isScrollEnabled
2828

2929
webView.translatesAutoresizingMaskIntoConstraints = false
3030
topConstraint = webView.topAnchor.constraint(equalTo: topAnchor)
@@ -63,19 +63,20 @@ final class ReflowableDocumentWebView: DocumentWebView {
6363
override func applyUserSettingsStyle() {
6464
super.applyUserSettingsStyle()
6565

66-
if let userSettings = userSettings {
67-
for cssProperty in userSettings.userProperties.properties {
66+
if let properties = userSettings?.userProperties.properties {
67+
let propertiesScript = properties.reduce("") { script, property in
6868
let value: String = {
6969
// Scroll mode depends both on the user settings, and on the fact that VoiceOver is activated or not, so we need to generate the value dynamically.
7070
// FIXME: This would be handled in a better way by decoupling the user settings from the actual ReadiumCSS properties sent to the WebView, which should be private details of the EPUBNavigator implementation and not shared with the host app.
71-
if let switchable = cssProperty as? Switchable, cssProperty.name == ReadiumCSSName.scroll.rawValue {
71+
if let switchable = property as? Switchable, property.name == ReadiumCSSName.scroll.rawValue {
7272
return switchable.values[isScrollEnabled]!
7373
} else {
74-
return cssProperty.toString()
74+
return property.toString()
7575
}
7676
}()
77-
evaluateScriptInResource("readium.setProperty(\"\(cssProperty.name)\", \"\(value)\");")
77+
return script + "readium.setProperty(\"\(property.name)\", \"\(value)\");\n"
7878
}
79+
evaluateScriptInResource(propertiesScript)
7980
}
8081

8182
// Disables paginated mode if scroll is on.
@@ -85,20 +86,21 @@ final class ReflowableDocumentWebView: DocumentWebView {
8586
}
8687

8788
private func updateContentInset() {
88-
var insets = contentInset[traitCollection.verticalSizeClass]
89-
?? contentInset[.regular]
90-
?? contentInset[.unspecified]
91-
?? (top: 0, bottom: 0)
92-
93-
// Increases the insets by the notch area (eg. iPhone X) to make sure that the content is not overlapped by the screen notch.
94-
insets.top += notchAreaInsets.top
95-
insets.bottom += notchAreaInsets.bottom
96-
9789
if (isScrollEnabled) {
9890
topConstraint.constant = 0
9991
bottomConstraint.constant = 0
100-
scrollView.contentInset = UIEdgeInsets(top: insets.top, left: 0, bottom: insets.bottom, right: 0)
92+
scrollView.contentInset = UIEdgeInsets(top: notchAreaInsets.top, left: 0, bottom: notchAreaInsets.bottom, right: 0)
93+
10194
} else {
95+
var insets = contentInset[traitCollection.verticalSizeClass]
96+
?? contentInset[.regular]
97+
?? contentInset[.unspecified]
98+
?? (top: 0, bottom: 0)
99+
100+
// Increases the insets by the notch area (eg. iPhone X) to make sure that the content is not overlapped by the screen notch.
101+
insets.top += notchAreaInsets.top
102+
insets.bottom += notchAreaInsets.bottom
103+
102104
topConstraint.constant = insets.top
103105
bottomConstraint.constant = -insets.bottom
104106
scrollView.contentInset = .zero

r2-navigator-swift/EPUB/TriptychView.swift

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,14 @@ final class TriptychView: UIView {
9797
}
9898

9999
/// Index of the document currently being displayed.
100-
fileprivate(set) var index: Int
100+
fileprivate(set) var index: Int {
101+
willSet {
102+
guard let cw = currentView as? DocumentWebView else {
103+
return
104+
}
105+
cw.scrollAt(location: (index < newValue) ? trailing : leading)
106+
}
107+
}
101108

102109
fileprivate let scrollView: UIScrollView
103110

@@ -357,11 +364,9 @@ extension TriptychView {
357364
if index < nextIndex {
358365
currentRect.x += coefficient*currentFrameSize.width
359366
scrollView.scrollRectToVisible(CGRect(origin: currentRect, size: currentFrameSize), animated: false)
360-
cw.scrollAt(location: trailing)
361367
} else {
362368
currentRect.x -= coefficient*currentFrameSize.width
363369
scrollView.scrollRectToVisible(CGRect(origin: currentRect, size: currentFrameSize), animated: false)
364-
cw.scrollAt(location: leading)
365370
}
366371

367372
let previousIndex = index

r2-navigator-swift/EPUB/UserSettings.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ import R2Shared
1616

1717
public class UserSettings {
1818

19+
// WARNING: String values must not contain any single or double quotes characters, otherwise it breaks the streamer's injection.
1920
private let appearanceValues = ["readium-default-on", "readium-sepia-on","readium-night-on"]
20-
private let fontFamilyValues = ["Publisher's default", "Helvetica Neue", "Iowan Old Style", "Athelas", "Seravek", "OpenDyslexic"]
21+
private let fontFamilyValues = ["Original", "Helvetica Neue", "Iowan Old Style", "Athelas", "Seravek", "OpenDyslexic"]
2122
private let textAlignmentValues = ["justify", "start"]
2223
private let columnCountValues = ["auto", "1", "2"]
2324

0 commit comments

Comments
 (0)