From 42f07831cfc2802ce608edb928a420694da179b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Menu?= Date: Mon, 3 Jan 2022 11:41:38 +0100 Subject: [PATCH] Add `NavigatorDelegate.navigator(_:didJumpTo:)` API --- CHANGELOG.md | 6 +++ .../Navigator/Audiobook/AudioNavigator.swift | 6 +++ .../CBZ/CBZNavigatorViewController.swift | 28 +++++------ .../EPUB/EPUBNavigatorViewController.swift | 1 + Sources/Navigator/Navigator.swift | 19 ++++++-- .../PDF/PDFNavigatorViewController.swift | 46 +++++++++++-------- .../Streamer/Parser/Audio/AudioParser.swift | 9 +--- 7 files changed, 68 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9736769d5..20bead059 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,12 @@ All notable changes to this project will be documented in this file. Take a look * Support for the [`conformsTo` RWPM metadata](https://github.com/readium/webpub-manifest/issues/65), to identify the profile of a `Publication`. * Support for right-to-left PDF documents by extracting the reading progression from the `ViewerPreferences/Direction` metadata. +#### Navigator + +* The new `NavigatorDelegate.navigator(_:didJumpTo:)` API is called every time the navigator jumps to an explicit location, which might break the linear reading progression. + * For example, it is called when clicking on internal links or programmatically calling `Navigator.go(to:)`, but not when turning pages. + * You can use this callback to implement a navigation history by differentiating between continuous and discontinuous moves. + ### Deprecated #### Shared diff --git a/Sources/Navigator/Audiobook/AudioNavigator.swift b/Sources/Navigator/Audiobook/AudioNavigator.swift index 33f63a170..b4a741be9 100644 --- a/Sources/Navigator/Audiobook/AudioNavigator.swift +++ b/Sources/Navigator/Audiobook/AudioNavigator.swift @@ -238,6 +238,12 @@ open class _AudioNavigator: _MediaNavigator, _AudioSessionUser, Loggable { play() + if let delegate = delegate, let location = currentLocation { + delegate.navigator(self, didJumpTo: location) + } + + DispatchQueue.main.async(execute: completion) + return true } catch { diff --git a/Sources/Navigator/CBZ/CBZNavigatorViewController.swift b/Sources/Navigator/CBZ/CBZNavigatorViewController.swift index e68e6ea27..979a0b476 100644 --- a/Sources/Navigator/CBZ/CBZNavigatorViewController.swift +++ b/Sources/Navigator/CBZ/CBZNavigatorViewController.swift @@ -1,12 +1,7 @@ // -// CBZNavigatorViewController.swift -// r2-navigator-swift -// -// Created by Alexandre Camilleri on 8/24/17. -// // Copyright 2018 Readium Foundation. All rights reserved. -// Use of this source code is governed by a BSD-style license which is detailed -// in the LICENSE file present in the project repository where this source code is maintained. +// Use of this source code is governed by the BSD-style license +// available in the top-level LICENSE file of the project. // import UIKit @@ -65,7 +60,7 @@ open class CBZNavigatorViewController: UIViewController, VisualNavigator, Loggab view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(didTap))) - goToResourceAtIndex(initialIndex) + goToResourceAtIndex(initialIndex, animated: false, isJump: false) } private var currentResourceIndex: Int { @@ -85,7 +80,7 @@ open class CBZNavigatorViewController: UIViewController, VisualNavigator, Loggab } @discardableResult - private func goToResourceAtIndex(_ index: Int, animated: Bool = false, completion: @escaping () -> Void = {}) -> Bool { + private func goToResourceAtIndex(_ index: Int, animated: Bool, isJump: Bool, completion: @escaping () -> Void = {}) -> Bool { guard let imageViewController = imageViewController(at: index) else { return false } @@ -105,6 +100,9 @@ open class CBZNavigatorViewController: UIViewController, VisualNavigator, Loggab return } self.delegate?.navigator(self, locationDidChange: position) + if isJump { + self.delegate?.navigator(self, didJumpTo: position) + } completion() } return true @@ -140,22 +138,22 @@ open class CBZNavigatorViewController: UIViewController, VisualNavigator, Loggab guard let index = publication.readingOrder.firstIndex(withHREF: locator.href) else { return false } - return goToResourceAtIndex(index, animated: animated, completion: completion) + return goToResourceAtIndex(index, animated: animated, isJump: true, completion: completion) } public func go(to link: Link, animated: Bool, completion: @escaping () -> Void) -> Bool { guard let index = publication.readingOrder.firstIndex(withHREF: link.href) else { return false } - return goToResourceAtIndex(index, animated: animated, completion: completion) + return goToResourceAtIndex(index, animated: animated, isJump: true, completion: completion) } public func goForward(animated: Bool, completion: @escaping () -> Void) -> Bool { - return goToResourceAtIndex(currentResourceIndex + 1, animated: animated, completion: completion) + return goToResourceAtIndex(currentResourceIndex + 1, animated: animated, isJump: false, completion: completion) } public func goBackward(animated: Bool, completion: @escaping () -> Void) -> Bool { - return goToResourceAtIndex(currentResourceIndex - 1, animated: animated, completion: completion) + return goToResourceAtIndex(currentResourceIndex - 1, animated: animated, isJump: false, completion: completion) } } @@ -228,9 +226,7 @@ extension CBZNavigatorViewController { } @available(*, unavailable, message: "Use `go(to:)` using the `readingOrder` instead") - public func load(at index: Int) { - goToResourceAtIndex(index, animated: true) - } + public func load(at index: Int) {} @available(*, unavailable, message: "Use init(publication:initialLocation:) instead") public convenience init(for publication: Publication, initialIndex: Int = 0) { diff --git a/Sources/Navigator/EPUB/EPUBNavigatorViewController.swift b/Sources/Navigator/EPUB/EPUBNavigatorViewController.swift index 677d1f9aa..d09c92ec8 100644 --- a/Sources/Navigator/EPUB/EPUBNavigatorViewController.swift +++ b/Sources/Navigator/EPUB/EPUBNavigatorViewController.swift @@ -532,6 +532,7 @@ open class EPUBNavigatorViewController: UIViewController, VisualNavigator, Selec return paginationView.goToIndex(spreadIndex, location: .locator(locator), animated: animated) { self.on(.jumped) + self.delegate?.navigator(self, didJumpTo: locator) completion() } } diff --git a/Sources/Navigator/Navigator.swift b/Sources/Navigator/Navigator.swift index dbae3c3d6..1ce3f9fe7 100644 --- a/Sources/Navigator/Navigator.swift +++ b/Sources/Navigator/Navigator.swift @@ -77,9 +77,19 @@ public extension Navigator { public protocol NavigatorDelegate: AnyObject { - /// Called when the current position in the publication changed. You should save the locator here to restore the last read page. + /// Called when the current position in the publication changed. You should save the locator here to restore the + /// last read page. func navigator(_ navigator: Navigator, locationDidChange locator: Locator) - + + /// Called when the navigator jumps to an explicit location, which might break the linear reading progression. + /// + /// For example, it is called when clicking on internal links or programmatically calling `go()`, but not when + /// turning pages. + /// + /// You can use this callback to implement a navigation history by differentiating between continuous and + /// discontinuous moves. + func navigator(_ navigator: Navigator, didJumpTo locator: Locator) + /// Called when an error must be reported to the user. func navigator(_ navigator: Navigator, presentError error: NavigatorError) @@ -97,7 +107,9 @@ public protocol NavigatorDelegate: AnyObject { public extension NavigatorDelegate { - + + func navigator(_ navigator: Navigator, didJumpTo locator: Locator) {} + func navigator(_ navigator: Navigator, presentExternalURL url: URL) { if UIApplication.shared.canOpenURL(url) { UIApplication.shared.open(url, options: [:], completionHandler: nil) @@ -107,7 +119,6 @@ public extension NavigatorDelegate { func navigator(_ navigator: Navigator, shouldNavigateToNoteAt link: Link, content: String, referrer: String?) -> Bool { return true } - } diff --git a/Sources/Navigator/PDF/PDFNavigatorViewController.swift b/Sources/Navigator/PDF/PDFNavigatorViewController.swift index 01915a606..00d1b7ec8 100644 --- a/Sources/Navigator/PDF/PDFNavigatorViewController.swift +++ b/Sources/Navigator/PDF/PDFNavigatorViewController.swift @@ -1,12 +1,7 @@ // -// PDFNavigatorViewController.swift -// r2-navigator-swift -// -// Created by Mickaël Menu on 05.03.19. -// // Copyright 2019 Readium Foundation. All rights reserved. -// Use of this source code is governed by a BSD-style license which is detailed -// in the LICENSE file present in the project repository where this source code is maintained. +// Use of this source code is governed by the BSD-style license +// available in the top-level LICENSE file of the project. // import Foundation @@ -93,9 +88,9 @@ open class PDFNavigatorViewController: UIViewController, VisualNavigator, Select editingActions.updateSharedMenuController() if let locator = initialLocation { - go(to: locator) + go(to: locator, isJump: false) } else if let link = publication.readingOrder.first { - go(to: link) + go(to: link, pageNumber: 0, isJump: false) } else { log(.error, "No initial location and empty reading order") } @@ -146,7 +141,22 @@ open class PDFNavigatorViewController: UIViewController, VisualNavigator, Select delegate?.navigator(self, locationDidChange: locator) } - private func go(to link: Link, pageNumber: Int? = nil, completion: @escaping () -> Void) -> Bool { + @discardableResult + private func go(to locator: Locator, isJump: Bool, completion: @escaping () -> Void = {}) -> Bool { + guard let index = publication.readingOrder.firstIndex(withHREF: locator.href) else { + return false + } + + return go( + to: publication.readingOrder[index], + pageNumber: pageNumber(for: locator), + isJump: isJump, + completion: completion + ) + } + + @discardableResult + private func go(to link: Link, pageNumber: Int?, isJump: Bool, completion: @escaping () -> Void = {}) -> Bool { guard let index = publication.readingOrder.firstIndex(of: link) else { return false } @@ -175,6 +185,10 @@ open class PDFNavigatorViewController: UIViewController, VisualNavigator, Select } pdfView.go(to: page) } + if isJump, let delegate = delegate, let location = currentPosition { + delegate.navigator(self, didJumpTo: location) + } + DispatchQueue.main.async(execute: completion) return true } @@ -284,19 +298,11 @@ open class PDFNavigatorViewController: UIViewController, VisualNavigator, Select } public func go(to locator: Locator, animated: Bool, completion: @escaping () -> Void) -> Bool { - guard let index = publication.readingOrder.firstIndex(withHREF: locator.href) else { - return false - } - - return go( - to: publication.readingOrder[index], - pageNumber: pageNumber(for: locator), - completion: completion - ) + return go(to: locator, isJump: true, completion: completion) } public func go(to link: Link, animated: Bool, completion: @escaping () -> Void) -> Bool { - return go(to: Locator(link: link), animated: animated, completion: completion) + return go(to: link, pageNumber: nil, isJump: true, completion: completion) } public func goForward(animated: Bool, completion: @escaping () -> Void) -> Bool { diff --git a/Sources/Streamer/Parser/Audio/AudioParser.swift b/Sources/Streamer/Parser/Audio/AudioParser.swift index 6e4112a7e..4b854b20c 100644 --- a/Sources/Streamer/Parser/Audio/AudioParser.swift +++ b/Sources/Streamer/Parser/Audio/AudioParser.swift @@ -1,12 +1,7 @@ // -// AudioParser.swift -// r2-streamer-swift -// -// Created by Mickaël Menu on 15/07/2020. -// // Copyright 2020 Readium Foundation. All rights reserved. -// Use of this source code is governed by a BSD-style license which is detailed -// in the LICENSE file present in the project repository where this source code is maintained. +// Use of this source code is governed by the BSD-style license +// available in the top-level LICENSE file of the project. // import Foundation