如何解决PDF View和SCrollView的手势冲突 [Swift 5, iOS 14]

How to resolve the conflict of gestures between PDFView and SCrollView [Swift5, iOS 14]

我正在为 iPad 创建一个 PDF 查看器,用户可以使用它通过水平滚动来阅读 PDF。

我创建了以下代码来实现通过手势更改页面的单页视图(同时咨询 How to create a single page vertical scrolling PDFView in Swift 和其他地方)。

尽管这种方法在大多数情况下都可以正常工作,但我发现在放大 PDF 文件时无法检测到(或调用)手势。因此,我无法通过滑动屏幕转到下一页。使用我创建的 extension PDFView {} 函数,我发现禁用 subview 中的用户交互可以让我检测到滑动手势。但是,现在我无法在 PDFView 内滚动页面。如果你能帮我弄清楚如何解决这个问题,我将不胜感激。

我想实现的是类似 ‎PDF Expert (https://apps.apple.com/us/app/pdf-expert-pdf-reader-editor/id743974925) 的东西,我可以在其中水平滚动到下一页。

非常感谢您的提前帮助!

import UIKit
import PDFKit

//PDF Zoom scale
var scaleOfPdf: CGFloat = 4

extension PDFView {
    func disableBouncing(){
        for subview in subviews{
            if let scrollView = subview as? UIScrollView{
                scrollView.bounces = false
                return
            }
}

class ViewController: UIViewController, UIGestureRecognizerDelegate,  UIDocumentPickerDelegate {

    @IBOutlet weak var pdfView: PDFView!
    
    override func viewDidLoad(){
        super.viewDidLoad()


            pdfView.autoresizesSubviews = true
            pdfView.autoresizingMask = [.flexibleWidth, .flexibleHeight, .flexibleTopMargin, .flexibleLeftMargin]
            
            pdfView.displayDirection = .horizontal
            pdfView.displayMode = .singlePage
            pdfView.autoScales = true
            // setting a color for background
            pdfView.backgroundColor = .black
            pdfView.document = pdfDocument
            //            pdfView.usePageViewController(true, withViewOptions: [UIPageViewController.OptionsKey.interPageSpacing: 20])
            pdfView.maxScaleFactor = 4.0
            pdfView.minScaleFactor = pdfView.scaleFactorForSizeToFit
            
            pdfView.disableBouncing()  


       //setting swipe gesture
        let leftSwipeGesture = UISwipeGestureRecognizer(target: self, action: #selector(respondLeftSwipeGesture(_:)))
        leftSwipeGesture.direction = [UISwipeGestureRecognizer.Direction.left]
        pdfView.addGestureRecognizer(leftSwipeGesture)
        
        let rightSwipeGesture = UISwipeGestureRecognizer(target: self, action: #selector(respondRightSwipeGesture(_:)))
        rightSwipeGesture.direction = [UISwipeGestureRecognizer.Direction.right]
        pdfView.addGestureRecognizer(rightSwipeGesture)

    }
    
        
    //setting swipe-gesture
    @objc func respondLeftSwipeGesture(_ sender: UISwipeGestureRecognizer) {
        print("left swipe was detected")
        if pdfView.document == nil { return }
        scaleOfPdf = pdfView.scaleFactor
        pdfView.goToNextPage(self)
        pdfView.scaleFactor = scaleOfPdf
    }
    
    @objc func respondRightSwipeGesture(_ sender: UISwipeGestureRecognizer) {
        print("right swipe was detected")
        if pdfView.document == nil { return }
        scaleOfPdf = pdfView.scaleFactor
        pdfView.goToPreviousPage(self)
        pdfView.scaleFactor = scaleOfPdf
    }
}

手势识别器作为处理触摸的链或管道工作 - 在一个 (G1) 失败后,第二个 (G2) 尝试识别其手势。这里你至少有 4 个识别器——你的 2 个(leftright),以及 2 个 scrollView 的(panpinch)。我将提供仅涵盖 scrollView 的 pan 识别器的简短解决方案,如果您也会看到 pinch 的问题 - 您需要遵循相同的方法。

假设 G1 是您的 left 识别器,G2 是 scrollView 的 pan 识别器。 为了让G2处理和G1一样的触摸,应该让他们同时识别。

此外,用户可能会在垂直滚动时稍微水平移动 his/her 手指,因此在这种情况下,您还希望仅在 G1 放弃滑动且无法识别后才开始滚动。

为了实现这一点,您应该将此代码添加到您的 VC。

override func viewDidLoad(){
    super.viewDidLoad()
    
    ...
    
    leftSwipeGesture.delegate = self
    leftSwipeGesture.cancelsTouchesInView = false
    rightSwipeGesture.delegate = self
    rightSwipeGesture.cancelsTouchesInView = false
}

optional func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, 
                                shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    return gestureRecognizer == leftSwipeGesture
        || gestureRecognizer == rightSwipeGesture
        || otherGestureRecognizer == leftSwipeGesture
        || otherGestureRecognizer == rightSwipeGesture
}

optional func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, 
                                shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    guard let _ = gestureRecognizer as? UIPanGestureRecognizer else { return false }
    return otherGestureRecognizer == leftSwipeGesture
        || otherGestureRecognizer == rightSwipeGesture
}

optional func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, 
                                shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    guard let _ = otherGestureRecognizer as? UIPanGestureRecognizer else { return false }
    return gestureRecognizer == leftSwipeGesture
        || gestureRecognizer == rightSwipeGesture
}

如果我添加的 UIGestureRecognizerDelegate 方法没有被调用,您将需要创建一个子类 PDFView,创建 left/rightSwipeGesture.delegate = pdfView 并在您的 PDFView 子类中覆盖它的 UIGestureRecognizerDelegate 方法具有这种逻辑。