Подтвердить что ты не робот

Устройства распознавания жестов UIPageViewController

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

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

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

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

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

Я бы предпочел не подклассифицировать PageViewController, поскольку apple говорит, что этот класс не должен быть подклассом.

4b9b3361

Ответ 1

Вы можете переопределить

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

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

Мое решение выглядит так в 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.

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

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
}

Ответ 2

Вот еще одно решение, которое можно добавить в шаблон 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];
}

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

Ответ 3

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

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

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

Просто раскомментируйте эту строку, и все работает как ожидалось.

Филлип

Ответ 5

Я использовал

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

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

Ответ 6

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

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

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

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

Я решил просто установить cancelsTouchesInView для каждого распознавателя на false, что позволяет также отображать сообщения в tview > .

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

Ответ 7

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

Ответ 8

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

// 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;
}

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

Ответ 9

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

in viewDidLoad:

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

to:

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

Ответ 10

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

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

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

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

Ответ 11

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

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

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

Ответ 12

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

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

Получает представление UIPageControl из подпрограмм UIPageController и отключает взаимодействие с пользователем.

Ответ 13

Я оказался здесь, ища простой, всеобъемлющий способ реагировать на краны на моих дочерних контроллерах представления в UIPageViewController. Ядро моего решения (Swift 4, Xcode 9) оказалось таким же простым, как это, в моей RootViewController.swift (той же структуре, что и шаблон 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")
}

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

Ответ 14

Я разработал рабочее решение. Добавьте еще один UIGestureRecognizer в UIPageViewController и реализуйте метод делегата, представленный ниже. В каждый момент, когда вам нужно решить, какой жест должен быть заблокирован или передан дальше, будет вызываться этот метод. Не забудьте предоставить ссылку на confictingView, который в моем случае был UITableView, который также распознает жест панорамирования. Это представление было размещено внутри UIPageViewController, поэтому жест панорамирования распознавался дважды или просто случайным образом. Теперь в этом методе я проверяю, находится ли жест панорамирования в обоих моих UITableView и UIPageViewController, и я решаю, что UIPanGestureRecognizer является основным.

Этот подход не переопределяет напрямую ни один из других распознавателей жестов, поэтому нам не нужно беспокоиться об упомянутом "NSInvalidArgumentException".

Имейте в виду, что шаблон на самом деле не утвержден Apple :)

 var conflictingView:UIView?
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        if otherGestureRecognizer.view === pageViewController?.view {

            if let view = conflictingView {
                var point = otherGestureRecognizer.location(in: self.view)
                if view.frame.contains(point) {
                    print("Touch in conflicting view")
                    return false
                }
            }
            print("Touch outside conficting view")
            return true
        }
        print("Another view passed out")
        return true
    }

Ответ 15

Расширение Swift 3 для удаления распознавателя отвода:

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