Распознаватели жестов UIPageViewController


Я уже некоторое время работаю над приложением, но я не мог задать этот вопрос из-за NDA.

У меня есть uipageviewcontroller нагрузки с моим Viewcontroller. Контроллеры представления имеют кнопки, которые переопределяются распознавателями жестов PageViewControllers. Например, у меня есть кнопка на правой стороне viewcontroller и при нажатии на кнопку, PageViewController берет на себя и изменяет страницу.

Как я могу сделать кнопку получить прикосновение и отменить распознаватель жестов в PageViewController?

Я думаю, что PageViewController делает мой ViewController подвидом своего представления.

Я знаю, что могу отключить все жесты, но это не тот эффект, которого я ищу.

Я предпочитаю не подкласс PageViewController как apple говорит, что этот класс не предназначен для подкласса.

14 54

14 ответов:

можно переопределить

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
 shouldReceiveTouch:(UITouch *)touch

чтобы лучше контролировать, когда PageViewController должен получить прикосновение и нет. Посмотрите на "предотвращение распознавания жестов от анализа прикосновений" в Dev API распознаватели жестов

мое решение выглядит так в RootViewController для UIPageViewController:

в viewDidLoad:

//EDITED Need to take care of all gestureRecogizers. Got a bug when only setting the delegate for Tap
for (UIGestureRecognizer *gR in self.view.gestureRecognizers) {
    gR.delegate = self;
}

переопределение:

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    //Touch gestures below top bar should not make the page turn.
    //EDITED Check for only Tap here instead.
    if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
        CGPoint touchPoint = [touch locationInView:self.view];
        if (touchPoint.y > 40) {
            return NO;
        }
        else if (touchPoint.x > 50 && touchPoint.x < 430) {//Let the buttons in the middle of the top bar receive the touch
            return NO;
        }
    }
    return YES;
}

и не забудьте установить RootViewController как UIGestureRecognizerDelegate.

(к вашему сведению, я только в ландшафтном режиме.)

EDIT-приведенный выше код переведен на Swift 2:

в viewDidLoad:

for gr in self.view.gestureRecognizers! {
    gr.delegate = self
}

сделать контроллер вида страницы наследовать UIGestureRecognizerDelegate потом добавил:

func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
    if let _ = gestureRecognizer as? UITapGestureRecognizer {
        let touchPoint = touch .locationInView(self.view)
        if (touchPoint.y > 40 ){
            return false
        }else{
            return true
        }
    }
    return true
}

вот еще одно решение, которое можно добавить в viewDidLoad шаблон сразу после self.view.gestureRecognizers = self.pageViewController.gestureRecognizers часть из шаблона Xcode. Он избегает возиться с кишками распознавателей жестов или иметь дело с его делегатами. Он просто удаляет распознаватель жестов крана из представлений, оставляя только распознаватель салфетки.

self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;

// Find the tap gesture recognizer so we can remove it!
UIGestureRecognizer* tapRecognizer = nil;    
for (UIGestureRecognizer* recognizer in self.pageViewController.gestureRecognizers) {
    if ( [recognizer isKindOfClass:[UITapGestureRecognizer class]] ) {
        tapRecognizer = recognizer;
        break;
    }
}

if ( tapRecognizer ) {
    [self.view removeGestureRecognizer:tapRecognizer];
    [self.pageViewController.view removeGestureRecognizer:tapRecognizer];
}

Теперь для переключения между страницами, вы должны провести. Краны теперь работают только на ваших элементах управления в верхней части представления страницы (что я и делал).

У меня была та же проблема. Пример и документация делают это в loadView или viewDidLoad:

self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;

это заменяет распознаватели жестов из представлений UIViewControllers на распознаватели жестов UIPageViewController. Теперь, когда происходит касание, они сначала отправляются через распознаватели жестов pageViewControllers - если они не совпадают, они отправляются в подвиды.

просто раскомментируйте эту строку, и все работает ожидаемый.

Филлип

установка делегата gestureRecognizers для viewController, как показано ниже, больше не работает на ios6

for (UIGestureRecognizer *gR in self.view.gestureRecognizers) {
    gR.delegate = self;
}

в ios6 установка делегата gestureRecognizers вашего pageViewController для viewController вызывает сбой

Я

for (UIScrollView *view in _pageViewController.view.subviews) {
    if ([view isKindOfClass:[UIScrollView class]]) {
        view.delaysContentTouches = NO;
    }
}

чтобы разрешить клики для перехода к кнопкам внутри UIPageViewController

в более новых версиях (я нахожусь в Xcode 7.3, нацеленном на iOS 8.1+), ни одно из этих решений, похоже, не работает.

принятый ответ рухнет с ошибкой:

встроенный распознаватель жестов панорамирования UIScrollView должен иметь свой вид прокрутки в качестве своего делегата.

в настоящее время самый высокий рейтинг ответ (от ПЭТ McG) больше не работает, потому что UIPageViewControllerscrollview, похоже, использует нечетные подклассы распознавания жестов, которые вы не можете проверить. Таким образом, заявление if ( [recognizer isKindOfClass:[UITapGestureRecognizer class]] ) не выполняет.

я решил просто установить cancelsTouchesInView на каждом распознавателе в false, что позволяет просматривать подвиды UIPageViewController для получения прикосновений, а также.

на viewDidLoad:

guard let recognizers = self.pageViewController.view.subviews[0].gestureRecognizers else {
    print("No gesture recognizers on scrollview.")
    return
}

for recognizer in recognizers {
    recognizer.cancelsTouchesInView = false
}

Если вы используете кнопку, которую вы подклассифицировали, вы можете переопределить touchesBegan, touchesMoved и touchesEnded, вызывая свой собственный программный поворот страницы по мере необходимости, но не вызывая super и передавая касания вверх по цепочке уведомлений.

также можно использовать это (спасибо за помощь, С сказать о делегате):

// add UIGestureRecognizerDelegate

NSPredicate *tp = [NSPredicate predicateWithFormat:@"self isKindOfClass: %@", [UITapGestureRecognizer class]];
UITapGestureRecognizer *tgr = (UITapGestureRecognizer *)[self.pageViewController.view.gestureRecognizers filteredArrayUsingPredicate:tp][0];
tgr.delegate = self; // tap delegating

NSPredicate *pp = [NSPredicate predicateWithFormat:@"self isKindOfClass: %@", [UIPanGestureRecognizer class]];
UIPanGestureRecognizer *pgr = (UIPanGestureRecognizer *)[self.pageViewController.view.gestureRecognizers filteredArrayUsingPredicate:pp][0];
pgr.delegate = self; // pan delegating

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch 
{
    CGPoint touchPoint = [touch locationInView:self.view];

    if (UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation]) && touchPoint.y > 915 ) {
        return NO; // if y > 915 px in portrait mode
    }

    if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation]) && touchPoint.y > 680 ) {
        return NO; // if y > 680 px in landscape mode
    }

    return YES;
}

отлично работает для меня :)

Это решение, которое лучше всего работало для меня я попробовал JRAMER ответ был в порядке, за исключением того, что я получу ошибку при подкачке за пределы (страница -1 или страница 23 для меня)

решение PatMCG не дало мне достаточной гибкости, так как оно отменило все taprecognizers, я все еще хотел кран, но не в моей метке

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

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
 if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
     return NO;
 } else {
     return YES;
 }
}

Я регулярно создаю pageviewcontrollers, когда мой пользователь прыгает, завивается и скользит по разным видам страниц. В процедуре, которая создает новый pageviewcontroller, я использую немного более простую версию превосходного кода, показанного выше:

UIPageViewController *npVC = [[UIPageViewController alloc]
                       initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl
                       navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal
                       options: options];
...

// Find the pageView tap gesture recognizer so we can remove it!
for (UIGestureRecognizer* recognizer in npVC.gestureRecognizers) {
    if ( [recognizer isKindOfClass:[UITapGestureRecognizer class]] ) {
        UIGestureRecognizer* tapRecognizer = recognizer;
        [npVC.view removeGestureRecognizer:tapRecognizer];
       break;
    }
}

теперь краны работают так, как я хочу (с левым и правым кранами, прыгающими на страницу, и завитки работают нормально.

просто создайте подвид (связанный с новым IBOutlet gesturesView) в вашем RootViewController и назначьте жесты этому новому представлению. Это представление охватывает ту часть экрана, которую вы хотите включить жестом.

в изменении viewDidLoad:

self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;

to:

self.gesturesView.gestureRecognizers = self.pageViewController.gestureRecognizers;

в моем случае я хотел отключить нажатие на UIPageControl и пусть нажатие принимается другой кнопкой на экране. Свайп все еще работает. Я пробовал много способов, и я считаю, что это было самое простое рабочее решение:

for (UIPageControl *view in _pageController.view.subviews) {
    if ([view isKindOfClass:[UIPageControl class]]) {
        view.enabled = NO;
    }
}

это получение представления UIPageControl из вложенных представлений UIPageController и отключение взаимодействия с пользователем.

я оказался здесь, ища простой, универсальный способ реагировать на нажатия на мои дочерние контроллеры представления в UIPageViewController. Ядро моего решения (Swift 4, Xcode 9) оказалось таким же простым, как это, в моем RootViewController.swift (та же структура, что и шаблон "Page-Based App" Xcode):

override func viewDidLoad() {
    super.viewDidLoad()

    ...

    let tapGesture = UITapGestureRecognizer(target: self, action: #selector(pageTapped(sender:)))
    self.pageViewController?.view.subviews[0].addGestureRecognizer(tapGesture)
}

...

@objc func pageTapped(sender: UITapGestureRecognizer) {
    print("pageTapped")
}

(Я также использовал ответ чтобы я мог отслеживать, какая страница была фактически прослушана, т. е. нынешняя.)

Swift 3 расширение для удаления tap recognizer:

    import UIKit

extension UIPageViewController {
    func removeTapRecognizer() {
        let gestureRecognizers = self.gestureRecognizers

        var tapGesture: UIGestureRecognizer?
        gestureRecognizers.forEach { recognizer in
            if recognizer.isKind(of: UITapGestureRecognizer.self) {
                tapGesture = recognizer
            }
        }
        if let tapGesture = tapGesture {
            self.view.removeGestureRecognizer(tapGesture)
        }
    }
}