简体   繁体   中英

How can selection be disabled in PDFView?

Displaying a PDFDocument in a PDFView allows the user to select parts of the document and perform actions eg "copy" with the selection. How can selection be disabled in a PDFView while preserving the possibility for the user to zoom in and out and scroll in the PDF?

PDFView itself does not seem to offer such a property nor does the PDFViewDelegate .

You have to subclass PDFView, as such:

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)
    }

}

For iOS 13, the above solution no longer works. It looks like they've changed the internal implementation of PDFView and specifically how the gesture recognizers are set up. I think generally it's discouraged to do this kind of thing, but it can still be done without using any internal API, here's how:

1) Recursively gather all subviews of PDFView (see below for the helper function to do this)

let allSubviews = pdfView.allSubViewsOf(type: UIView.self)

2) Iterate over them and deactivate any UILongPressGestureRecognizer s:

for gestureRec in allSubviews.compactMap({ $0.gestureRecognizers }).flatMap({ $0 }) {
    if gestureRec is UILongPressGestureRecognizer {
        gestureRec.isEnabled = false
    }
}

Helper func to recursively get all subviews of a given type:

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
}

I'm calling the above code from the viewDidLoad method of the containing view controller.

I haven't yet found a good way to work this into a subclass of PDFView , which would be the preferred way for reusability and could just be an addition to the above NonSelectablePDFView . What I've tried so far is overriding didAddSubview and adding the above code after the call to super , but that didn't work as expected. It seems like the gesture recognizers are only being added at a later step, so figuring out when that is and if there's a way for the subclass to call some custom code after this happened would be a next step here.

Just need to do is it will auto clear the selection and User will no longer long-press on PDF text.

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)
    }

}

This below 2 lines need to add in canPerformAction()

self.currentSelection = nil
self.clearSelection()

With Swift 5 and iOS 12.3, you can solve your problem by overriding addGestureRecognizer(_:) method and canPerformAction(_:withSender:) method in a PDFView subclass.

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
    }

}

As an alternative to the previous implementation, you can simply toggle UILongPressGestureRecognizer isEnabled property to false in the initializer.

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
    }

}

You should note that this is not sufficient to disable text selecting, as there is a UITapAndHalfRecognizer as well – obviously a private Apple class - that also creates selections.

It is attached to the PDFDocumentView, which is another private implementation detail of PDFView, and which you can not replace with your own class implementation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM