Определить, что PDFView прокручивал

Предоставляет ли PDFKit на iOS доступ к базовому UIScrollView для PDFView или есть какой-либо другой способ непосредственно определить, что пользователь прокрутил PDFView?

Мой вариант использования - скрыть панель навигации, когда документ прокручивается, так что в качестве обходного пути я добавил свой собственный распознаватель жестов панорамирования в родительский элемент PDFView, и я скрываю в gestRecognizerShouldBegin и всегда возвращаю false, но я ожидаю, что есть что-то более похожее на UIScrollViewDelegate что я скучаю по документам.

5 ответов

Попробуй это,

NotificationCenter.default.addObserver(self, selector: #selector(handlePageChange(notification:)), name: Notification.Name.PDFViewPageChanged, object: nil)

@objc private func handlePageChange(notification: Notification)
{
    print("Page changed")
}

Предоставляет ли PDFKit на iOS базовый UIScrollView PDFView

Нет, но, надеюсь, Apple добавит это в будущем. Я помню, что у UIWebView его изначально не было, и он был добавлен позже.

или есть ли другой способ напрямую определить, что пользователь прокрутил PDFView

Нет, похоже, ни одно из уведомлений, предоставленных PDFViewDelegate обратиться к этому.

Я перехожу с UIWebView на PDFView и использую scrollViewDidScrollдля множества вещей, поэтому я не хотел полагаться только на добавление распознавателя жестов панорамирования. Основываясь на ответе @Matthijs, я нахожу UIScrollView внутри PDFView, делаю свой класс своим делегатом, а затем передаю любые события обратно в представление прокрутки (которое было его собственным делегатом до того, как мой класс стал делегатом), чтобы он мог отвечать на них, слишком. В UIWebView этот последний шаг не требовался, но с PDFView масштабирование и, возможно, другие функции не будут работать без него.

Я отменяю все документированные методы делегата, чтобы снизить вероятность поломки, если Apple изменит внутреннюю функцию PDFView. Однако мне пришлось проверитьrespondsToSelector в каждом методе, потому что исходный делегат представления прокрутки в настоящее время не реализует их все.

- (void)viewDidLoad {
    // create the PDFView and find its inner scrollView
    self.pdfView = [[PDFView alloc] init];
    for (UIView *subview in self.pdfView.subviews) {
        if ([subview isKindOfClass:[UIScrollView class]]) {
            self.scrollView = (UIScrollView *)subview;
        } else {
            for (UIView *subsubview in subview.subviews) {
                if ([subsubview isKindOfClass:[UIScrollView class]]) {
                    self.scrollView = (UIScrollView *)subsubview;
                }
            }
        }
    }
}

- (void)loadPDFDocument:(NSString *)URL {
    // load a document, then become the delegate for the scrollView (we have to do that after loading the document)
    PDFDocument *document = [[PDFDocument alloc] initWithURL:URL];
    self.pdfView.document = document;
    self.scrollView.delegate = self;
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    // *** respond to scroll events here ***

    UIScrollView <UIScrollViewDelegate> *scrollViewDelegate = (UIScrollView <UIScrollViewDelegate> *)self.scrollView;
    if ([scrollViewDelegate respondsToSelector:@selector(scrollViewDidScroll:)]) {
        [scrollViewDelegate scrollViewDidScroll:scrollView];
    }
}

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    UIScrollView <UIScrollViewDelegate> *scrollViewDelegate = (UIScrollView <UIScrollViewDelegate> *)self.scrollView;
    if ([scrollViewDelegate respondsToSelector:@selector(scrollViewWillBeginDragging:)]) {
        [scrollViewDelegate scrollViewWillBeginDragging:scrollView];
    }
}

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
    UIScrollView <UIScrollViewDelegate> *scrollViewDelegate = (UIScrollView <UIScrollViewDelegate> *)self.scrollView;
    if ([scrollViewDelegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)]) {
        [scrollViewDelegate scrollViewWillEndDragging:scrollView withVelocity:velocity targetContentOffset:targetContentOffset];
    }
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    UIScrollView <UIScrollViewDelegate> *scrollViewDelegate = (UIScrollView <UIScrollViewDelegate> *)self.scrollView;
    if ([scrollViewDelegate respondsToSelector:@selector(scrollViewDidEndDragging:willDecelerate:)]) {
        [scrollViewDelegate scrollViewDidEndDragging:scrollView willDecelerate:decelerate];
    }
}

- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView {
    UIScrollView <UIScrollViewDelegate> *scrollViewDelegate = (UIScrollView <UIScrollViewDelegate> *)self.scrollView;
    if ([scrollViewDelegate respondsToSelector:@selector(scrollViewShouldScrollToTop:)]) {
        return [scrollViewDelegate scrollViewShouldScrollToTop:scrollView];
    }
    return TRUE;
}

- (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView {
    UIScrollView <UIScrollViewDelegate> *scrollViewDelegate = (UIScrollView <UIScrollViewDelegate> *)self.scrollView;
    if ([scrollViewDelegate respondsToSelector:@selector(scrollViewDidScrollToTop:)]) {
        [scrollViewDelegate scrollViewDidScrollToTop:scrollView];
    }
}

- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView {
    UIScrollView <UIScrollViewDelegate> *scrollViewDelegate = (UIScrollView <UIScrollViewDelegate> *)self.scrollView;
    if ([scrollViewDelegate respondsToSelector:@selector(scrollViewWillBeginDecelerating:)]) {
        [scrollViewDelegate scrollViewWillBeginDecelerating:scrollView];
    }
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    UIScrollView <UIScrollViewDelegate> *scrollViewDelegate = (UIScrollView <UIScrollViewDelegate> *)self.scrollView;
    if ([scrollViewDelegate respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) {
        [scrollViewDelegate scrollViewDidEndDecelerating:scrollView];
    }
}

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
    UIScrollView <UIScrollViewDelegate> *scrollViewDelegate = (UIScrollView <UIScrollViewDelegate> *)self.scrollView;
    if ([scrollViewDelegate respondsToSelector:@selector(viewForZoomingInScrollView:)]) {
        return [scrollViewDelegate viewForZoomingInScrollView:scrollView];
    }
    return nil;
}

- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view {
    UIScrollView <UIScrollViewDelegate> *scrollViewDelegate = (UIScrollView <UIScrollViewDelegate> *)self.scrollView;
    if ([scrollViewDelegate respondsToSelector:@selector(scrollViewWillBeginZooming:withView:)]) {
        [scrollViewDelegate scrollViewWillBeginZooming:scrollView withView:view];
    }
}

- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale {
    UIScrollView <UIScrollViewDelegate> *scrollViewDelegate = (UIScrollView <UIScrollViewDelegate> *)self.scrollView;
    if ([scrollViewDelegate respondsToSelector:@selector(scrollViewDidEndZooming:withView:atScale:)]) {
        [scrollViewDelegate scrollViewDidEndZooming:scrollView withView:view atScale:scale];
    }
}

- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
    UIScrollView <UIScrollViewDelegate> *scrollViewDelegate = (UIScrollView <UIScrollViewDelegate> *)self.scrollView;
    if ([scrollViewDelegate respondsToSelector:@selector(scrollViewDidZoom:)]) {
        [scrollViewDelegate scrollViewDidZoom:scrollView];
    }
}

- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
    UIScrollView <UIScrollViewDelegate> *scrollViewDelegate = (UIScrollView <UIScrollViewDelegate> *)self.scrollView;
    if ([scrollViewDelegate respondsToSelector:@selector(scrollViewDidEndScrollingAnimation:)]) {
        [scrollViewDelegate scrollViewDidEndScrollingAnimation:scrollView];
    }
}

- (void)scrollViewDidChangeAdjustedContentInset:(UIScrollView *)scrollView {
    UIScrollView <UIScrollViewDelegate> *scrollViewDelegate = (UIScrollView <UIScrollViewDelegate> *)self.scrollView;
    if ([scrollViewDelegate respondsToSelector:@selector(scrollViewDidChangeAdjustedContentInset:)]) {
        [scrollViewDelegate scrollViewDidChangeAdjustedContentInset:scrollView];
    }
}

Я сделал это, чтобы обнаружить масштабирование и панорамирование в pdfView, чтобы скопировать эти жесты во второй pdfView, и здесь он отлично работает. Получил некоторую помощь в обнаружении вертикального и горизонтального панорамирования с помощью PanDirectionGestureRecognizer, который я нашел здесь: /questions/8461757/uipangesturerecognizer-tolko-vertikalnyij-ili-gorizontalnyij/49438038#49438038

class Document: UIViewController, UIScrollViewDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Subscribe to notifications.
        NotificationCenter.default.addObserver(self, selector: #selector(onPageZoomAndPan), name: .PDFViewScaleChanged, object: pdfView

        // get the scrollView in pdfView and attach gesture recognizers

        outerLoop: for subView in pdfView.subviews {
            for view in subView.subviews {
                if let scrollView = view as? UIScrollView {
                    let xScrollViewPanGesture = PanDirectionGestureRecognizer(direction: .horizontal, target: self, action: #selector(onPageZoomAndPan))
                    xScrollViewPanGesture.delegate = self
                    scrollView.addGestureRecognizer(xScrollViewPanGesture)

                    let yScrollViewPanGesture = PanDirectionGestureRecognizer(direction: .vertical, target: self, action: #selector(onPageZoomAndPan))
                    yScrollViewPanGesture.delegate = self
                    scrollView.addGestureRecognizer(yScrollViewPanGesture)

                    break outerLoop
                }
            }
        }
    }

    // MARK: - UIScrollViewDelegate

    @objc private func onPageZoomAndPan() {
        let rect = pdfView.convert(pdfView.bounds, to: pdfView.currentPage!)
        pdfViewSecondScreen.scaleFactor = pdfView.scaleFactor
        pdfViewSecondScreen.go(to: rect, on: pdfView.currentPage!)
    }
}



enum PanDirection {
    case vertical
    case horizontal
}


// UIKit.UIGestureRecognizerSubclass

import UIKit.UIGestureRecognizerSubclass

class PanDirectionGestureRecognizer: UIPanGestureRecognizer {

    let direction : PanDirection

    init(direction: PanDirection, target: AnyObject, action: Selector) {
        self.direction = direction
        super.init(target: target, action: action)
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
        super.touchesMoved(touches, with: event)

        if state == .began {
            let vel = velocity(in: self.view!)

            switch direction {
            case .horizontal where abs(vel.y) > abs(vel.x):
                state = .cancelled
            case .vertical where abs(vel.x) > abs(vel.y):
                state = .cancelled
            default:
                break
            }
        }
    }
} 

Решил решение, которое другие еще не сделали. Пошел с ключевым значением, наблюдая за contentOffsetсвойство лежащего в основе UIScrollView.

Вы можете использовать это расширение для запуска обратного вызова каждый раз, когда изменяется смещение прокрутки.

      var observation = pdfView.onScrollOffsetChanged { scroll in
    print("PDFView scrolled to \(scroll.contentOffset).")
}

Расширение

      extension PDFView {
    func onScrollOffsetChange(handler: @escaping (UIScrollView) -> Void) -> NSKeyValueObservation? {
        detectScrollView()?.observe(\.contentOffset) { scroll, _ in
            handler(scroll)
        }
    }
    
    private func detectScrollView() -> UIScrollView? {
        for view in subviews {
            if let scroll = view as? UIScrollView {
                return scroll
            } else {
                for subview in view.subviews {
                    if let scroll = subview as? UIScrollView {
                        return scroll
                    }
                }
            }
        }
        
        print("Unable to find a scrollView subview on a PDFView.")
        return nil
    }
}

попробуй это!

      (pdfView.subviews[0] as? UIScrollView)?.delegate = self

и наблюдайте за делегатом scrollview

      func scrollViewDidScroll(_ scrollView: UIScrollView) {
    if scrollView.contentOffset.y > 0 {
        /// ...
    } else {
        /// ...
    }
}
Другие вопросы по тегам