Устройства распознавания жестов UIPageViewController
Я работаю над приложением некоторое время, но я не мог задать этот вопрос из-за NDA.
У меня есть загрузка UIPageViewController с моим Viewcontroller. Контроллеры представлений имеют кнопки, которые переопределяются распознавателями жестов PageViewControllers. Например, у меня есть кнопка с правой стороны диспетчера view, и когда вы нажимаете кнопку, PageViewController берет верх и меняет страницу.
Как заставить кнопку получить прикосновение и отменить распознаватель жестов в PageViewController?
Я думаю, что PageViewController делает мой ViewController подвид его представления.
Я знаю, что могу отключить все Жесты, но это не тот эффект, который я ищу.
Я бы предпочел не подклассифицировать PageViewController, поскольку apple говорит, что этот класс не должен быть подклассом.
Ответы
Ответ 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.
Просто раскомментируйте эту строку, и все работает как ожидалось.
Филлип
Ответ 4
Настройка делегата gestureRecognizers на viewController, как показано ниже, больше не работает на ios6
for (UIGestureRecognizer *gR in self.view.gestureRecognizers) {
gR.delegate = self;
}
В ios6 установка делегата gestureRecognizers вашего pageViewController в viewController вызывает сбой
Ответ 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)
}
}
}