Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ All notable changes to this project will be documented in this file. Take a look
* Added `DirectionalNavigationAdapter.onNavigation` callback to be notified when a navigation action is triggered.
* This callback is called before executing any navigation action.
* Useful for hiding UI elements when the user navigates, or implementing analytics.
* Added swipe gesture support for navigating in PDF paginated spread mode.

### Deprecated

#### Navigator

* `PDFNavigatorViewController.scalesDocumentToFit` is now deprecated and non-functional. The navigator always scales the document to fit the viewport.

### Changed

Expand All @@ -24,6 +31,7 @@ All notable changes to this project will be documented in this file. Take a look
#### Navigator

* Fixed EPUB fixed-layout spread settings not updating after device rotation when the app was in the background.
* Fixed zoom-to-fit scaling in PDF paginated spread mode when `offsetFirstPage` is enabled.

#### LCP

Expand Down
67 changes: 55 additions & 12 deletions Sources/Navigator/PDF/PDFNavigatorViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ open class PDFNavigatorViewController:
}

/// Whether the pages is always scaled to fit the screen, unless the user zoomed in.
public var scalesDocumentToFit = true
@available(*, unavailable, message: "This API is deprecated")
public var scalesDocumentToFit: Bool { true }

public weak var delegate: PDFNavigatorDelegate?
public private(set) var pdfView: PDFDocumentView?
Expand All @@ -76,6 +77,8 @@ open class PDFNavigatorViewController:
// Holds a reference to make sure they are not garbage-collected.
private var tapGestureController: PDFTapGestureController?
private var clickGestureController: PDFTapGestureController?
private var swipeLeftGestureRecognizer: UISwipeGestureRecognizer?
private var swipeRightGestureRecognizer: UISwipeGestureRecognizer?

private let server: HTTPServer?
private let publicationEndpoint: HTTPServerEndpoint?
Expand Down Expand Up @@ -184,7 +187,7 @@ open class PDFNavigatorViewController:
super.viewWillAppear(animated)

// Hack to layout properly the first page when opening the PDF.
if let pdfView = pdfView, scalesDocumentToFit {
if let pdfView = pdfView {
pdfView.scaleFactor = pdfView.minScaleFactor
if let page = pdfView.currentPage {
pdfView.go(to: page.bounds(for: pdfView.displayBox), on: page)
Expand All @@ -195,14 +198,12 @@ open class PDFNavigatorViewController:
override open func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)

if let pdfView = pdfView, scalesDocumentToFit {
// Makes sure that the PDF is always properly scaled down when rotating the screen, if the user didn't zoom in.
if let pdfView = pdfView {
// Makes sure that the PDF is always properly scaled down when
// rotating the screen, if the user didn't zoom in.
let isAtMinScaleFactor = (pdfView.scaleFactor == pdfView.minScaleFactor)
coordinator.animate(alongsideTransition: { _ in
self.updateScaleFactors()
if isAtMinScaleFactor {
pdfView.scaleFactor = pdfView.minScaleFactor
}
self.updateScaleFactors(zoomToFit: isAtMinScaleFactor)

// Reset the PDF view to update the spread if needed.
if self.settings.spread == .auto {
Expand Down Expand Up @@ -263,11 +264,14 @@ open class PDFNavigatorViewController:
target: self,
action: #selector(didClick)
)
swipeLeftGestureRecognizer = recognizeSwipe(in: pdfView, direction: .left)
swipeRightGestureRecognizer = recognizeSwipe(in: pdfView, direction: .right)

apply(settings: settings, to: pdfView)
delegate?.navigator(self, setupPDFView: pdfView)

NotificationCenter.default.addObserver(self, selector: #selector(pageDidChange), name: .PDFViewPageChanged, object: pdfView)
NotificationCenter.default.addObserver(self, selector: #selector(visiblePagesDidChange), name: .PDFViewVisiblePagesChanged, object: pdfView)
NotificationCenter.default.addObserver(self, selector: #selector(selectionDidChange), name: .PDFViewSelectionChanged, object: pdfView)

if let locator = locator {
Expand Down Expand Up @@ -328,7 +332,7 @@ open class PDFNavigatorViewController:

pdfView.displaysRTL = isRTL
pdfView.displaysPageBreaks = true
pdfView.autoScales = !scalesDocumentToFit
pdfView.autoScales = false

if let scrollView = pdfView.firstScrollView {
let showScrollbar = settings.visibleScrollbar
Expand All @@ -341,6 +345,10 @@ open class PDFNavigatorViewController:
}
pdfView.backgroundColor = settings.backgroundColor?.uiColor
?? pdfViewDefaultBackgroundColor

let enableSwipes = !settings.scroll && spread
swipeLeftGestureRecognizer?.isEnabled = enableSwipes
swipeRightGestureRecognizer?.isEnabled = enableSwipes
}

@objc private func didTap(_ gesture: UITapGestureRecognizer) {
Expand All @@ -367,13 +375,40 @@ open class PDFNavigatorViewController:
delegate?.navigator(self, didTapAt: location)
}

private func recognizeSwipe(in view: UIView, direction: UISwipeGestureRecognizer.Direction) -> UISwipeGestureRecognizer {
let recognizer = UISwipeGestureRecognizer(target: self, action: #selector(didSwipe))
recognizer.direction = direction
recognizer.numberOfTouchesRequired = 1
view.addGestureRecognizer(recognizer)
return recognizer
}

@objc private func didSwipe(_ gesture: UISwipeGestureRecognizer) {
switch gesture.direction {
case .left:
Task { await goRight(options: .animated) }
case .right:
Task { await goLeft(options: .animated) }
default:
break
}
}

@objc private func pageDidChange() {
guard let locator = currentPosition else {
return
}
delegate?.navigator(self, locationDidChange: locator)
}

@objc private func visiblePagesDidChange() {
// In paginated mode, we want to refresh the scale factors to properly
// fit the newly visible pages.
if !settings.scroll {
updateScaleFactors(zoomToFit: true)
}
}

@discardableResult
private func go(to locator: Locator, isJump: Bool) async -> Bool {
let locator = publication.normalizeLocator(locator)
Expand Down Expand Up @@ -426,7 +461,7 @@ open class PDFNavigatorViewController:
currentResourceIndex = index
documentHolder.set(document, at: href)
pdfView.document = document
updateScaleFactors()
updateScaleFactors(zoomToFit: true)
}

guard let document = pdfView.document else {
Expand All @@ -446,12 +481,20 @@ open class PDFNavigatorViewController:
return true
}

private func updateScaleFactors() {
guard let pdfView = pdfView, scalesDocumentToFit else {
/// Updates the scale factors to match the currently visible pages.
///
/// - Parameter zoomToFit: When true, the document will be zoomed to fit the
/// visible pages.
private func updateScaleFactors(zoomToFit: Bool) {
guard let pdfView = pdfView else {
return
}
pdfView.minScaleFactor = pdfView.scaleFactorForSizeToFit
pdfView.maxScaleFactor = 4.0

if zoomToFit {
pdfView.scaleFactor = pdfView.minScaleFactor
}
}

private func pageNumber(for locator: Locator) -> Int? {
Expand Down