Skip to content

Commit fae59aa

Browse files
authored
PDF: Fix zoom in paginated spread mode and add swipe gestures (#678)
1 parent db12e1e commit fae59aa

File tree

2 files changed

+63
-12
lines changed

2 files changed

+63
-12
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ All notable changes to this project will be documented in this file. Take a look
1212
* Added `DirectionalNavigationAdapter.onNavigation` callback to be notified when a navigation action is triggered.
1313
* This callback is called before executing any navigation action.
1414
* Useful for hiding UI elements when the user navigates, or implementing analytics.
15+
* Added swipe gesture support for navigating in PDF paginated spread mode.
16+
17+
### Deprecated
18+
19+
#### Navigator
20+
21+
* `PDFNavigatorViewController.scalesDocumentToFit` is now deprecated and non-functional. The navigator always scales the document to fit the viewport.
1522

1623
### Changed
1724

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

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

2836
#### LCP
2937

Sources/Navigator/PDF/PDFNavigatorViewController.swift

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ open class PDFNavigatorViewController:
5757
}
5858

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

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

8083
private let server: HTTPServer?
8184
private let publicationEndpoint: HTTPServerEndpoint?
@@ -184,7 +187,7 @@ open class PDFNavigatorViewController:
184187
super.viewWillAppear(animated)
185188

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

198-
if let pdfView = pdfView, scalesDocumentToFit {
199-
// Makes sure that the PDF is always properly scaled down when rotating the screen, if the user didn't zoom in.
201+
if let pdfView = pdfView {
202+
// Makes sure that the PDF is always properly scaled down when
203+
// rotating the screen, if the user didn't zoom in.
200204
let isAtMinScaleFactor = (pdfView.scaleFactor == pdfView.minScaleFactor)
201205
coordinator.animate(alongsideTransition: { _ in
202-
self.updateScaleFactors()
203-
if isAtMinScaleFactor {
204-
pdfView.scaleFactor = pdfView.minScaleFactor
205-
}
206+
self.updateScaleFactors(zoomToFit: isAtMinScaleFactor)
206207

207208
// Reset the PDF view to update the spread if needed.
208209
if self.settings.spread == .auto {
@@ -263,11 +264,14 @@ open class PDFNavigatorViewController:
263264
target: self,
264265
action: #selector(didClick)
265266
)
267+
swipeLeftGestureRecognizer = recognizeSwipe(in: pdfView, direction: .left)
268+
swipeRightGestureRecognizer = recognizeSwipe(in: pdfView, direction: .right)
266269

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

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

273277
if let locator = locator {
@@ -328,7 +332,7 @@ open class PDFNavigatorViewController:
328332

329333
pdfView.displaysRTL = isRTL
330334
pdfView.displaysPageBreaks = true
331-
pdfView.autoScales = !scalesDocumentToFit
335+
pdfView.autoScales = false
332336

333337
if let scrollView = pdfView.firstScrollView {
334338
let showScrollbar = settings.visibleScrollbar
@@ -341,6 +345,10 @@ open class PDFNavigatorViewController:
341345
}
342346
pdfView.backgroundColor = settings.backgroundColor?.uiColor
343347
?? pdfViewDefaultBackgroundColor
348+
349+
let enableSwipes = !settings.scroll && spread
350+
swipeLeftGestureRecognizer?.isEnabled = enableSwipes
351+
swipeRightGestureRecognizer?.isEnabled = enableSwipes
344352
}
345353

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

378+
private func recognizeSwipe(in view: UIView, direction: UISwipeGestureRecognizer.Direction) -> UISwipeGestureRecognizer {
379+
let recognizer = UISwipeGestureRecognizer(target: self, action: #selector(didSwipe))
380+
recognizer.direction = direction
381+
recognizer.numberOfTouchesRequired = 1
382+
view.addGestureRecognizer(recognizer)
383+
return recognizer
384+
}
385+
386+
@objc private func didSwipe(_ gesture: UISwipeGestureRecognizer) {
387+
switch gesture.direction {
388+
case .left:
389+
Task { await goRight(options: .animated) }
390+
case .right:
391+
Task { await goLeft(options: .animated) }
392+
default:
393+
break
394+
}
395+
}
396+
370397
@objc private func pageDidChange() {
371398
guard let locator = currentPosition else {
372399
return
373400
}
374401
delegate?.navigator(self, locationDidChange: locator)
375402
}
376403

404+
@objc private func visiblePagesDidChange() {
405+
// In paginated mode, we want to refresh the scale factors to properly
406+
// fit the newly visible pages.
407+
if !settings.scroll {
408+
updateScaleFactors(zoomToFit: true)
409+
}
410+
}
411+
377412
@discardableResult
378413
private func go(to locator: Locator, isJump: Bool) async -> Bool {
379414
let locator = publication.normalizeLocator(locator)
@@ -426,7 +461,7 @@ open class PDFNavigatorViewController:
426461
currentResourceIndex = index
427462
documentHolder.set(document, at: href)
428463
pdfView.document = document
429-
updateScaleFactors()
464+
updateScaleFactors(zoomToFit: true)
430465
}
431466

432467
guard let document = pdfView.document else {
@@ -446,12 +481,20 @@ open class PDFNavigatorViewController:
446481
return true
447482
}
448483

449-
private func updateScaleFactors() {
450-
guard let pdfView = pdfView, scalesDocumentToFit else {
484+
/// Updates the scale factors to match the currently visible pages.
485+
///
486+
/// - Parameter zoomToFit: When true, the document will be zoomed to fit the
487+
/// visible pages.
488+
private func updateScaleFactors(zoomToFit: Bool) {
489+
guard let pdfView = pdfView else {
451490
return
452491
}
453492
pdfView.minScaleFactor = pdfView.scaleFactorForSizeToFit
454493
pdfView.maxScaleFactor = 4.0
494+
495+
if zoomToFit {
496+
pdfView.scaleFactor = pdfView.minScaleFactor
497+
}
455498
}
456499

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

0 commit comments

Comments
 (0)