Распознаватели жестов UIPageViewController
Я уже некоторое время работаю над приложением, но я не мог задать этот вопрос из-за NDA.
У меня есть uipageviewcontroller нагрузки с моим Viewcontroller. Контроллеры представления имеют кнопки, которые переопределяются распознавателями жестов PageViewControllers. Например, у меня есть кнопка на правой стороне viewcontroller и при нажатии на кнопку, PageViewController берет на себя и изменяет страницу.
Как я могу сделать кнопку получить прикосновение и отменить распознаватель жестов в PageViewController?
Я думаю, что PageViewController делает мой ViewController подвидом своего представления.
Я знаю, что могу отключить все жесты, но это не тот эффект, которого я ищу.
Я предпочитаю не подкласс PageViewController как apple говорит, что этот класс не предназначен для подкласса.
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; }
Я
for (UIScrollView *view in _pageViewController.view.subviews) { if ([view isKindOfClass:[UIScrollView class]]) { view.delaysContentTouches = NO; } }
чтобы разрешить клики для перехода к кнопкам внутри UIPageViewController
в более новых версиях (я нахожусь в Xcode 7.3, нацеленном на iOS 8.1+), ни одно из этих решений, похоже, не работает.
принятый ответ рухнет с ошибкой:
встроенный распознаватель жестов панорамирования UIScrollView должен иметь свой вид прокрутки в качестве своего делегата.
в настоящее время самый высокий рейтинг ответ (от ПЭТ McG) больше не работает, потому что
UIPageViewController
scrollview, похоже, использует нечетные подклассы распознавания жестов, которые вы не можете проверить. Таким образом, заявление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) } } }