From 519ef61b385c5cc791c4a1c40aa5fceb92c8ae05 Mon Sep 17 00:00:00 2001 From: Miltiadis Vasilakis Date: Fri, 28 Feb 2025 20:52:39 +0200 Subject: [PATCH 1/5] Add back action when PDF reader scrolls to top by tapping status bar --- .../PDF/Views/PDFDocumentViewController.swift | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Zotero/Scenes/Detail/PDF/Views/PDFDocumentViewController.swift b/Zotero/Scenes/Detail/PDF/Views/PDFDocumentViewController.swift index ed071c388..0bc1532e3 100644 --- a/Zotero/Scenes/Detail/PDF/Views/PDFDocumentViewController.swift +++ b/Zotero/Scenes/Detail/PDF/Views/PDFDocumentViewController.swift @@ -663,6 +663,7 @@ final class PDFDocumentViewController: UIViewController { self.setup(scrubberBar: controller.userInterfaceView.scrubberBar) self.setup(interactions: controller.interactions) controller.shouldResetAppearanceModeWhenViewDisappears = false + controller.documentViewController?.delegate = self return controller } @@ -1171,3 +1172,18 @@ extension UIAction.Identifier { fileprivate static let pspdfkitAnnotationToolHighlight = UIAction.Identifier(rawValue: "com.pspdfkit.action.annotation-tool-Highlight") fileprivate static let pspdfkitAnnotationToolUnderline = UIAction.Identifier(rawValue: "com.pspdfkit.action.annotation-tool-Underline") } + +extension PDFDocumentViewController: PDFDocumentViewControllerDelegate { + func documentViewController(_ documentViewController: PSPDFKitUI.PDFDocumentViewController, configureScrollView scrollView: UIScrollView) { + scrollView.delegate = self + } +} + +extension PDFDocumentViewController: UIScrollViewDelegate { + func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool { + if let pdfController, pdfController.configuration.scrollDirection == .vertical, pdfController.pageIndex != 0 { + pdfController.backForwardList.register(PSPDFKit.GoToAction(pageIndex: pdfController.pageIndex)) + } + return true + } +} From 672f4ecd49dde44c3017f240cbbfd4037f5bc92a Mon Sep 17 00:00:00 2001 From: Miltiadis Vasilakis Date: Fri, 28 Feb 2025 20:56:47 +0200 Subject: [PATCH 2/5] Improve PDF reader back button placement when sidebar is visible --- .../Detail/PDF/Views/PDFReaderViewController.swift | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Zotero/Scenes/Detail/PDF/Views/PDFReaderViewController.swift b/Zotero/Scenes/Detail/PDF/Views/PDFReaderViewController.swift index bfe3eff20..a5a760221 100644 --- a/Zotero/Scenes/Detail/PDF/Views/PDFReaderViewController.swift +++ b/Zotero/Scenes/Detail/PDF/Views/PDFReaderViewController.swift @@ -38,7 +38,7 @@ class PDFReaderViewController: UIViewController { private weak var annotationToolbarController: AnnotationToolbarViewController! private var documentTop: NSLayoutConstraint! private var annotationToolbarHandler: AnnotationToolbarHandler! - private var intraDocumentNavigationHandler: IntraDocumentNavigationButtonsHandler! + private var intraDocumentNavigationHandler: IntraDocumentNavigationButtonsHandler? private var selectedText: String? private(set) var isCompactWidth: Bool @CodableUserDefault(key: "PDFReaderToolbarState", defaultValue: AnnotationToolbarHandler.State(position: .leading, visible: true), encoder: Defaults.jsonEncoder, decoder: Defaults.jsonDecoder) @@ -214,14 +214,13 @@ class PDFReaderViewController: UIViewController { ) viewModel.process(action: .changeIdleTimerDisabled(true)) view.backgroundColor = .systemGray6 - // Create intraDocumentNavigationHandler before setting up views, as it may be called by a child view controller, before view has finished loading. + setupViews() intraDocumentNavigationHandler = IntraDocumentNavigationButtonsHandler( - parent: self, + parent: documentController, back: { [weak self] in self?.documentController?.performBackAction() } ) - setupViews() setupObserving() if !viewModel.state.document.isLocked { @@ -230,7 +229,7 @@ class PDFReaderViewController: UIViewController { setupNavigationBar() updateInterface(to: viewModel.state.settings) - intraDocumentNavigationHandler.bringButtonToFront() + intraDocumentNavigationHandler?.bringButtonToFront() func setupViews() { let topSafeAreaSpacer = UIView() From 73a616ee906ee14b8ec9f19dcf24ca6064c8d150 Mon Sep 17 00:00:00 2001 From: Miltiadis Vasilakis Date: Sat, 1 Mar 2025 17:25:15 +0200 Subject: [PATCH 3/5] Add back action on PDF reader page change by annotation/outline/search --- .../Detail/PDF/Views/PDFDocumentViewController.swift | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Zotero/Scenes/Detail/PDF/Views/PDFDocumentViewController.swift b/Zotero/Scenes/Detail/PDF/Views/PDFDocumentViewController.swift index 0bc1532e3..406b666aa 100644 --- a/Zotero/Scenes/Detail/PDF/Views/PDFDocumentViewController.swift +++ b/Zotero/Scenes/Detail/PDF/Views/PDFDocumentViewController.swift @@ -432,20 +432,23 @@ final class PDFDocumentViewController: UIViewController { /// - parameter animated: `true` if scrolling is animated, `false` otherwise. /// - parameter completion: Completion block called after scroll. Block is also called when scroll was not needed. private func scrollIfNeeded(to pageIndex: PageIndex, animated: Bool, completion: @escaping () -> Void) { - guard self.pdfController?.pageIndex != pageIndex else { + guard let pdfController, pdfController.pageIndex != pageIndex else { completion() return } + let currentPageIndex = pdfController.pageIndex if !animated { - self.pdfController?.setPageIndex(pageIndex, animated: false) + pdfController.setPageIndex(pageIndex, animated: false) + pdfController.backForwardList.register(PSPDFKit.GoToAction(pageIndex: currentPageIndex)) completion() return } UIView.animate(withDuration: 0.25, animations: { - self.pdfController?.setPageIndex(pageIndex, animated: false) + pdfController.setPageIndex(pageIndex, animated: false) }, completion: { finished in + pdfController.backForwardList.register(PSPDFKit.GoToAction(pageIndex: currentPageIndex)) guard finished else { return } completion() }) From 0dcc00e60bd597b08f9e865bab8a359b866dd296 Mon Sep 17 00:00:00 2001 From: Miltiadis Vasilakis Date: Tue, 4 Mar 2025 10:45:19 +0200 Subject: [PATCH 4/5] Show forward action on PDF reader when visible --- ...ntraDocumentNavigationButtonsHandler.swift | 36 +++++++++++++++---- .../PDF/Views/PDFDocumentViewController.swift | 8 +++-- .../PDF/Views/PDFReaderViewController.swift | 20 ++++++++--- 3 files changed, 51 insertions(+), 13 deletions(-) diff --git a/Zotero/Scenes/Detail/PDF/Views/IntraDocumentNavigationButtonsHandler.swift b/Zotero/Scenes/Detail/PDF/Views/IntraDocumentNavigationButtonsHandler.swift index e9315b4d7..fa221c416 100644 --- a/Zotero/Scenes/Detail/PDF/Views/IntraDocumentNavigationButtonsHandler.swift +++ b/Zotero/Scenes/Detail/PDF/Views/IntraDocumentNavigationButtonsHandler.swift @@ -10,11 +10,15 @@ import UIKit final class IntraDocumentNavigationButtonsHandler { private weak var backButton: UIButton! + private weak var forwardButton: UIButton! var showsBackButton: Bool { backButton?.isHidden == false } + var showsForwardButton: Bool { + forwardButton?.isHidden == false + } - init(parent: UIViewController, back: @escaping () -> Void) { + init(parent: UIViewController, back: @escaping () -> Void, forward: @escaping () -> Void) { var backConfiguration = UIButton.Configuration.plain() backConfiguration.title = L10n.back backConfiguration.image = UIImage(systemName: "chevron.left", withConfiguration: UIImage.SymbolConfiguration(scale: .small)) @@ -31,17 +35,35 @@ final class IntraDocumentNavigationButtonsHandler { parent.view.addSubview(backButton) self.backButton = backButton + var forwardConfiguration = UIButton.Configuration.plain() + forwardConfiguration.title = L10n.forward + forwardConfiguration.image = UIImage(systemName: "chevron.right", withConfiguration: UIImage.SymbolConfiguration(scale: .small)) + forwardConfiguration.baseForegroundColor = Asset.Colors.zoteroBlueWithDarkMode.color + forwardConfiguration.background.backgroundColor = Asset.Colors.navbarBackground.color + forwardConfiguration.imagePadding = 8 + forwardConfiguration.imagePlacement = .trailing + let forwardButton = UIButton(configuration: forwardConfiguration) + forwardButton.translatesAutoresizingMaskIntoConstraints = false + forwardButton.isHidden = true + forwardButton.addAction( + UIAction(handler: { _ in forward() }), + for: .touchUpInside + ) + parent.view.addSubview(forwardButton) + self.forwardButton = forwardButton + NSLayoutConstraint.activate([ backButton.leadingAnchor.constraint(equalTo: parent.view.leadingAnchor, constant: 30), - parent.view.bottomAnchor.constraint(equalTo: backButton.bottomAnchor, constant: 80) + parent.view.bottomAnchor.constraint(equalTo: backButton.bottomAnchor, constant: 80), + forwardButton.trailingAnchor.constraint(equalTo: parent.view.trailingAnchor, constant: -30), + parent.view.bottomAnchor.constraint(equalTo: forwardButton.bottomAnchor, constant: 80) ]) } - func bringButtonToFront() { - backButton.superview?.bringSubviewToFront(backButton) - } - - func set(backButtonVisible: Bool) { + func set(backButtonVisible: Bool, forwardButtonVisible: Bool) { backButton.isHidden = !backButtonVisible + forwardButton.isHidden = !forwardButtonVisible + backButton.superview?.bringSubviewToFront(backButton) + forwardButton.superview?.bringSubviewToFront(forwardButton) } } diff --git a/Zotero/Scenes/Detail/PDF/Views/PDFDocumentViewController.swift b/Zotero/Scenes/Detail/PDF/Views/PDFDocumentViewController.swift index 406b666aa..4954c3730 100644 --- a/Zotero/Scenes/Detail/PDF/Views/PDFDocumentViewController.swift +++ b/Zotero/Scenes/Detail/PDF/Views/PDFDocumentViewController.swift @@ -25,7 +25,7 @@ protocol PDFDocumentDelegate: AnyObject { func didChange(undoState undoEnabled: Bool, redoState redoEnabled: Bool) func interfaceVisibilityDidChange(to isHidden: Bool) func showToolOptions() - func backNavigationButtonChanged(visible: Bool) + func navigationButtonsChanged(backVisible: Bool, forwardVisible: Bool) func didSelectText(_ text: String) } @@ -111,6 +111,10 @@ final class PDFDocumentViewController: UIViewController { pdfController?.backForwardList.requestBack(animated: true) } + func performForwardAction() { + pdfController?.backForwardList.requestForward(animated: true) + } + func focus(page: UInt) { self.scrollIfNeeded(to: page, animated: true, completion: {}) } @@ -916,7 +920,7 @@ extension PDFDocumentViewController: BackForwardActionListDelegate { func backForwardListDidUpdate(_ list: BackForwardActionList) { pdfController?.backForwardListDidUpdate(list) - parentDelegate?.backNavigationButtonChanged(visible: list.backAction != nil) + parentDelegate?.navigationButtonsChanged(backVisible: list.backAction != nil, forwardVisible: list.forwardAction != nil) } } diff --git a/Zotero/Scenes/Detail/PDF/Views/PDFReaderViewController.swift b/Zotero/Scenes/Detail/PDF/Views/PDFReaderViewController.swift index a5a760221..809bc061a 100644 --- a/Zotero/Scenes/Detail/PDF/Views/PDFReaderViewController.swift +++ b/Zotero/Scenes/Detail/PDF/Views/PDFReaderViewController.swift @@ -169,6 +169,12 @@ class PDFReaderViewController: UIViewController { .init(title: L10n.back, action: #selector(performBackAction), input: UIKeyCommand.inputLeftArrow, modifierFlags: [.command]) ] } + if intraDocumentNavigationHandler?.showsForwardButton == true { + keyCommands += [ + .init(title: L10n.back, action: #selector(performForwardAction), input: "]", modifierFlags: [.command]), + .init(title: L10n.back, action: #selector(performForwardAction), input: UIKeyCommand.inputRightArrow, modifierFlags: [.command]) + ] + } return keyCommands } @@ -178,7 +184,7 @@ class PDFReaderViewController: UIViewController { case #selector(UIResponderStandardEditActions.copy(_:)): return selectedText != nil - case #selector(search), #selector(performBackAction): + case #selector(search), #selector(performBackAction), #selector(performForwardAction): return true case #selector(undo(_:)): @@ -219,6 +225,9 @@ class PDFReaderViewController: UIViewController { parent: documentController, back: { [weak self] in self?.documentController?.performBackAction() + }, + forward: { [weak self] in + self?.documentController?.performForwardAction() } ) setupObserving() @@ -229,7 +238,6 @@ class PDFReaderViewController: UIViewController { setupNavigationBar() updateInterface(to: viewModel.state.settings) - intraDocumentNavigationHandler?.bringButtonToFront() func setupViews() { let topSafeAreaSpacer = UIView() @@ -673,6 +681,10 @@ class PDFReaderViewController: UIViewController { documentController.performBackAction() } + @objc private func performForwardAction() { + documentController.performForwardAction() + } + @objc private func undo(_ sender: Any?) { performUndo() } @@ -927,8 +939,8 @@ extension PDFReaderViewController: PDFDocumentDelegate { } } - func backNavigationButtonChanged(visible: Bool) { - intraDocumentNavigationHandler?.set(backButtonVisible: visible) + func navigationButtonsChanged(backVisible: Bool, forwardVisible: Bool) { + intraDocumentNavigationHandler?.set(backButtonVisible: backVisible, forwardButtonVisible: forwardVisible) } func didSelectText(_ text: String) { From 054f5aec3c2f8a56bffe60f152858ef0b7d9fe30 Mon Sep 17 00:00:00 2001 From: Miltiadis Vasilakis Date: Tue, 4 Mar 2025 15:06:44 +0200 Subject: [PATCH 5/5] Add back action on PDF reader page change by thumbnail list/scrubber --- .../Detail/PDF/Views/PDFDocumentViewController.swift | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Zotero/Scenes/Detail/PDF/Views/PDFDocumentViewController.swift b/Zotero/Scenes/Detail/PDF/Views/PDFDocumentViewController.swift index 4954c3730..376331dd2 100644 --- a/Zotero/Scenes/Detail/PDF/Views/PDFDocumentViewController.swift +++ b/Zotero/Scenes/Detail/PDF/Views/PDFDocumentViewController.swift @@ -260,7 +260,9 @@ final class PDFDocumentViewController: UIViewController { } if state.changes.contains(.visiblePageFromThumbnailList) { + let currentPageIndex = pdfController.pageIndex pdfController.setPageIndex(PageIndex(state.visiblePage), animated: false) + pdfController.backForwardList.register(PSPDFKit.GoToAction(pageIndex: currentPageIndex)) } if let tool = state.changedColorForTool, let color = state.toolColors[tool] { @@ -688,6 +690,7 @@ final class PDFDocumentViewController: UIViewController { scrubberBar.standardAppearance = appearance scrubberBar.compactAppearance = appearance + scrubberBar.delegate = self } private func setup(interactions: DocumentViewInteractions) { @@ -1194,3 +1197,12 @@ extension PDFDocumentViewController: UIScrollViewDelegate { return true } } + +extension PDFDocumentViewController: PSPDFKitUI.ScrubberBarDelegate { + func scrubberBar(_ scrubberBar: ScrubberBar, didSelectPageAt pageIndex: PageIndex) { + guard let pdfController, pdfController.pageIndex != pageIndex else { return } + let currentPageIndex = pdfController.pageIndex + pdfController.userInterfaceView.scrubberBar(scrubberBar, didSelectPageAt: pageIndex) + pdfController.backForwardList.register(PSPDFKit.GoToAction(pageIndex: currentPageIndex)) + } +}