Как можно отключить выбор в PDFView?
Отображение PDFDocument
в PDFView
позволяет пользователю выбирать части документа и выполнять действия, например, "копировать" с выделением. Как можно отключить выбор в PDFView, сохраняя при этом возможность для пользователя увеличивать и уменьшать масштаб и прокручивать PDF?
PDFView
само по себе, кажется, не предлагает такую собственность, а PDFViewDelegate
,
6 ответов
Вы должны создать подкласс PDFView, как таковой:
class MyPDFView: PDFView {
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
return false
}
override func addGestureRecognizer(_ gestureRecognizer: UIGestureRecognizer) {
if gestureRecognizer is UILongPressGestureRecognizer {
gestureRecognizer.isEnabled = false
}
super.addGestureRecognizer(gestureRecognizer)
}
}
Просто нужно сделать, это автоматически очистит выделение, и пользователь больше не будет долго нажимать на текст PDF.
class MyPDFView: PDFView {
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
self.currentSelection = nil
self.clearSelection()
return false
}
override func addGestureRecognizer(_ gestureRecognizer: UIGestureRecognizer) {
if gestureRecognizer is UILongPressGestureRecognizer {
gestureRecognizer.isEnabled = false
}
super.addGestureRecognizer(gestureRecognizer)
}
}
Это ниже 2 строк необходимо добавить в canPerformAction()
self.currentSelection = nil
self.clearSelection()
Для iOS 13 вышеуказанное решение больше не работает. Похоже, они изменили внутреннюю реализациюPDFView
и, в частности, как настроены распознаватели жестов. Я думаю, что вообще не рекомендуется делать такие вещи, но это все еще можно сделать без использования какого-либо внутреннего API, вот как:
1) Рекурсивно собрать все подвиды PDFView
(для этого см. вспомогательную функцию ниже)
let allSubviews = pdfView.allSubViewsOf(type: UIView.self)
2) Перебирайте их и деактивируйте любые UILongPressGestureRecognizer
s:
for gestureRec in allSubviews.compactMap({ $0.gestureRecognizers }).flatMap({ $0 }) {
if gestureRec is UILongPressGestureRecognizer {
gestureRec.isEnabled = false
}
}
Вспомогательная функция для рекурсивного получения всех подвидов заданного типа:
func allSubViewsOf<T: UIView>(type: T.Type) -> [T] {
var all: [T] = []
func getSubview(view: UIView) {
if let aView = view as? T {
all.append(aView)
}
guard view.subviews.count > 0 else { return }
view.subviews.forEach{ getSubview(view: $0) }
}
getSubview(view: self)
return all
}
Я вызываю приведенный выше код из viewDidLoad
метод содержащего контроллера представления.
Я еще не нашел хорошего способа превратить это в подкласс PDFView
, который был бы предпочтительным способом повторного использования и мог бы быть просто дополнением к вышеуказанному NonSelectablePDFView
. То, что я пробовал до сих пор, отменяетdidAddSubview
и добавив приведенный выше код после вызова super
, но это не сработало, как ожидалось. Похоже, что распознаватели жестов добавляются только на более позднем этапе, поэтому следующим шагом будет выяснение того, когда это произойдет и есть ли способ для подкласса вызвать некоторый пользовательский код после того, как это произошло.
Вы можете решить свою проблему, переопределив addGestureRecognizer(_:)
метод и canPerformAction(_:withSender:)
метод в PDFView
подкласс.
import UIKit
import PDFKit
class NonSelectablePDFView: PDFView {
override func addGestureRecognizer(_ gestureRecognizer: UIGestureRecognizer) {
(gestureRecognizer as? UILongPressGestureRecognizer)?.isEnabled = false
super.addGestureRecognizer(gestureRecognizer)
}
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
return false
}
}
В качестве альтернативы предыдущей реализации вы можете просто переключить UILongPressGestureRecognizer
isEnabled
собственность на false
в инициализаторе.
import UIKit
import PDFKit
class NonSelectablePDFView: PDFView {
override init(frame: CGRect) {
super.init(frame: frame)
if let gestureRecognizers = gestureRecognizers {
for gestureRecognizer in gestureRecognizers where gestureRecognizer is UILongPressGestureRecognizer {
gestureRecognizer.isEnabled = false
}
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
return false
}
}
В моем тесте двойное нажатие и «нажмите, а затем перетащите» не активируют выбор на iOS16, но на iOS13 это будет, так что вот так:
- Расширьте, добавьте функцию отключения выделения текста. я добавляю это
UIViewController
расширение, поэтому при вызове мне не нужно звонитьpdfView. recursivelyDisableSelection(view: pdfView)
, толькоrecursivelyDisableSelection(view: pdfView)
казалось бы немного лучше.
import UIKit
import PDFKit
extension UIViewController {
func recursivelyDisableSelection(view: UIView) {
// Get all recognizers for the PDFView's subviews. Here we are ignoring the recognizers for the PDFView itself, since we know from testing that not the reason for the mess.
for rec in view.subviews.compactMap({$0.gestureRecognizers}).flatMap({$0}) {
// UITapAndAHalfRecognizer is for a gesture like "tap first, then tap again and drag", this gesture also enable's text selection
if rec is UILongPressGestureRecognizer || type(of: rec).description() == "UITapAndAHalfRecognizer" {
rec.isEnabled = false
}
}
// For all subviews, if they do have subview in itself, disable the above 2 gestures as well.
for view in view.subviews {
if !view.subviews.isEmpty {
recursivelyDisableSelection(view: view)
}
}
}
}
При вызове функции передайте экземпляр
PDFView
вы хотите отключить выделение текста. Примечание: его следует вызывать ПОСЛЕ установки документа PDFView, иначе он не будет работать.Предупреждение. Если вы включили usePageViewController() для вашего PDFView, кажется, что при прокрутке для изменения текущей страницы жесты будут добавлены снова. Разумной мыслью было бы добавить наблюдателя для
PDFViewPageChanged
сообщение, я пробовал, работает только иногда. Лучше всего прослушать уведомление, так оно всегда работает. Опять же, наблюдателя следует добавлять после установки документа PDFView, иначе он не будет работать.
Итак, если вы, как и я, используете pageViewController, вам следует вызывать его в двух местах: один после установки документа, это отключение выбора для первой PDFPage, другой вPDFViewVisiblePagesChanged
уведомление для последующей прокрутки страницы.
pdfView.document = `YOUR_PDF_DOCUMENT`
recursivelyDisableSelection(view: pdfView)
NotificationCenter.default.addObserver(self, selector: #selector(pageChanged), name: .PDFViewVisiblePagesChanged, object: nil)
Позже в контроллере представления определите функцию-оболочку объекта c:
@objc func pageChanged() {
recursivelyDisableSelection(view: pdfView)
}
Следует отметить, что этого недостаточно для отключения выделения текста, поскольку существует также UITapAndHalfRecognizer - очевидно, частный класс Apple, - который также создает выделения.
Он прикреплен к PDFDocumentView, который является еще одной частной деталью реализации PDFView, и которую вы не можете заменить своей собственной реализацией класса.