UIPageViewController не возвращает никаких распознавателей жестов в iOS 6
Я пытаюсь отключить распознаватель жестов для UIPageViewController.
В iOS 5 я могу пропустить их и отключить.
for (UIGestureRecognizer* recognizer in self.pageViewController.gestureRecognizers) {
if ([recognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
recognizer.enabled = NO;
}
}
В iOS 6 с использованием UIPageViewControllerTransitionStyleScroll нет распознавателей жестов, возвращаемых контроллером просмотра страниц.
Разъяснение
Это можно свернуть до:
self.pageViewController.gestureRecognizers = 0, когда стиль перехода UIPageViewController настроен на прокрутку, поэтому я не могу получить доступ к распознающим признакам.
Есть ли способ обойти это? Я не думаю, что я делаю что-то не так, потому что переход на завиток отлично работает.
Ответы
Ответ 1
Для такого поведения есть ошибка зарегистрированная в радаре. Итак, я уверен, что до тех пор, пока Apple не исправит это, не будет возможности решить эту проблему.
Один из решений, который приходит мне на ум, заключается в том, что мы создаем прозрачное подзаголовок поверх вашего UIPageViewController
и добавляем к нему UIPanGestureRecognizer
, чтобы перехватить этот вид жестов, а не дальше. Вы можете включить этот просмотр/распознаватель, когда требуется отключить жест.
Я попробовал это с комбинацией распознавателей жестов Pan и Tap, и он работает.
Это мой тестовый код:
- (void)viewDidLoad {
[super viewDidLoad];
UIPanGestureRecognizer* g1 = [[[UIPanGestureRecognizer alloc] initWithTarget:self
action:@selector(g1Pan:)] autorelease];
[self.view addGestureRecognizer:g1];
UITapGestureRecognizer* s1 = [[[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(g1Tap:)] autorelease];
[self.view addGestureRecognizer:s1];
UIView* anotherView = [[[UIView alloc]initWithFrame:self.view.bounds] autorelease];
[self.view addSubview:anotherView];
UIPanGestureRecognizer* g2 = [[[UIPanGestureRecognizer alloc] initWithTarget:self
action:@selector(g2Pan:)] autorelease];
[anotherView addGestureRecognizer:g2];
}
Если параметр g2
включен, это предотвратит распознавание g1
. С другой стороны, это не предотвратит распознавание s1.
Я понимаю, что это взломать, но перед лицом кажущейся ошибки в UIPageViewController
(по крайней мере, фактическое поведение явно отличается от состояния ссылки), я не вижу лучшего решения.
Ответ 2
Обнаружено это в UIPageViewController.h:
//Только заполненный, если стиль перехода - 'UIPageViewControllerTransitionStylePageCurl'.@property (nonatomic, readonly) NSArray * gestureRecognizers;
Итак, не ошибка - по дизайну pageViewController не получает распознавателей жестов, когда установлен стиль прокрутки.
Ответ 3
Вы всегда можете попытаться отключить взаимодействие с пользователем в представлении контроллера просмотра страницы:
for (UIScrollView *view in self.pageViewController.view.subviews) {
if ([view isKindOfClass:[UIScrollView class]]) {
view.scrollEnabled = NO;
}
}
Ответ 4
В соответствии с заголовочным файлом UIPageViewController, когда источник данных равен нулю, управление с помощью жестов отключается.
Итак, установите источник данных на нуль, если вы хотите отключить прокрутку, тогда, когда вы хотите включить прокрутку, reset источник данных.
то есть.
// turns off paging
pageViewController.datasource = nil
// turns on paging
pageViewController.datasource = self;
Ответ 5
Вы можете получить доступ к UIPanGestureRecognizer через UIScrollview из UIPageViewController.
for (UIView *view in self.pageController.view.subviews) {
if ([view isKindOfClass:[UIScrollView class]])
{
UIScrollView *scrollView = (UIScrollView *)view;
UIPanGestureRecognizer* panGestureRecognizer = scrollView.panGestureRecognizer;
[panGestureRecognizer addTarget:self action:@selector(move:)];
}
}
Ответ 6
Если UIPageViewControllers UIPanGestureRecognizer ест/проглатывает все события от другого PanGestureRecognizer (например, скользящее меню).
Вы можете легко расширить решение по Берни и сделать UIScrollViews PanGestureRecognizer, чтобы другой Recognizer потерпел неудачу.
Что-то вроде этого:
for (UIView *view in pageViewController.view.subviews) {
if ([view isKindOfClass:[UIScrollView class]]){
UIScrollView *scrollView = (UIScrollView *)view;
[scrollView.panGestureRecognizer requireGestureRecognizerToFail:otherRecognizer];
}
}
Таким образом, прокрутка PanGestureRecognizer запускается только в тех областях, в которых я намеревался.
Это может быть не лучшее решение для будущего будущего, так как мы требуем от Apple не изменять внутреннее использование UIPcViewViewControllers UIScrollView. но...
Ответ 7
Предполагая, что вы можете найти распознаватели жестов и удалить их, очень хрупкий. Вы используете предполагаемые знания о том, как Apple использует инструменты UIPageViewController для обеспечения функциональности вашего приложения. Если это изменится (например, между iOS 5 и iOS 6), то приложение с кодом начнет вести себя непредсказуемыми способами. Это почти похоже на использование частного API - у вас нет гарантии, что он будет работать со следующей версией ОС.
Ответ 8
Что произойдет, если вы используете методы KVC (Key Value Coding) для доступа к распознавателям? (Я не могу проверить это на данный момент.)
Быстрое тестирование может заключаться в том, чтобы получить количество жестоврежима:
[self.pageViewController countOfKey:@"gestureRecognizers"];
Если это сработает, вы можете продолжить поиск массива распознавателей:
NSArray *recognizers = [self.pageViewController
mutableArrayValueForKey:@"gestureRecognizers"];
Тонкий, но возможно...
Изменить: смог наконец проверить. Используется следующее:
NSArray *pvcGRsNoKVC = [[NSArray alloc]
initWithArray:self.pageViewController.gestureRecognizers];
NSArray *viewGRsNoKVC = [[NSArray alloc] initWithArray:self.view.gestureRecognizers];
NSArray *pvcGRsKVC = [[NSArray alloc]
initWithArray:[self.pageViewController valueForKey:@"gestureRecognizers"]];
NSArray *viewGRsKVC = [[NSArray alloc]
initWithArray:[self.view valueForKey:@"gestureRecognizers"]];
Это не имело никакого значения. Стиль завитки отлично работал в обоих направлениях; стиль прокрутки показал, что массивы не ноль, а пустой. Интересная вещь, однако, заключалась в том, что вид ТАКЖЕ не отказался от своих распознавателей - хотя функциональность прокрутки есть, поэтому у нее должен быть хотя бы распознаватель панорамы...
Ответ 9
Другое решение, только для истории.
Вы можете сделать любой вид каким-то разрывом распознавания жестов, и он должен работать в этом прямоугольнике вида. Должен быть другой UIPanGestureRecognizer с делегатом. Это может быть любой объект одним способом:
static UIPageViewController* getPageViewControllerFromView(UIView* v) {
UIResponder* r = v.nextResponder;
while (r) {
if ([r isKindOfClass:UIPageViewController.class])
return (UIPageViewController*)r;
r = r.nextResponder;
}
return nil;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
if (getPageViewControllerFromView(otherGestureRecognizer.view))
{
otherGestureRecognizer.enabled = NO;
otherGestureRecognizer.enabled = YES;
}
return NO;
}
Вы можете использовать следующий класс для разблокировки распознавателя:
@interface GestureRecognizerBreaker : NSObject <UIGestureRecognizerDelegate>
{
UIGestureRecognizer* breaker_;
BOOL(^needsBreak_)(UIGestureRecognizer*);
}
- (id) initWithBreakerClass:(Class)recognizerClass
checker:(BOOL(^)(UIGestureRecognizer* recognizer))needsBreak;
- (void) lockForView:(UIView*)view;
- (void) unlockForView:(UIView*)view;
@end
@implementation GestureRecognizerBreaker
- (void) dummy:(id)r {}
- (id) initWithBreakerClass:(Class)recognizerClass checker:(BOOL(^)(UIGestureRecognizer*))needsBreak {
self = [super init];
if (!self)
return nil;
NSParameterAssert([recognizerClass isSubclassOfClass:UIGestureRecognizer.class] && needsBreak);
needsBreak_ = needsBreak;
breaker_ = [[recognizerClass alloc] initWithTarget:self action:@selector(dummy:)];
breaker_.delegate = self;
return self;
}
- (BOOL) gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer {
if (needsBreak_(otherGestureRecognizer)) {
otherGestureRecognizer.enabled = NO;
otherGestureRecognizer.enabled = YES;
}
return NO;
}
- (void) lockForView:(UIView*)view {
[view addGestureRecognizer:breaker_];
}
- (void) unlockForView:(UIView*)view {
[view removeGestureRecognizer:breaker_];
}
@end
Это работает, например, как одиночный:
static GestureRecognizerBreaker* PageViewControllerLocker() {
static GestureRecognizerBreaker* i = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
i = [[GestureRecognizerBreaker alloc]
initWithBreakerClass:UIPanGestureRecognizer.class
checker:^BOOL(UIGestureRecognizer* recognizer) {
UIView* v = recognizer.view;
UIResponder* r = v.nextResponder;
while (r) {
if ([r isKindOfClass:UIPageViewController.class])
return YES;
r = r.nextResponder;
}
return NO;
}];
});
return i;
}
После вызова -lockForView: жест управления страничным страницей не работает при перетаскивании в рамку просмотра. Например, я хочу заблокировать все пространство моего контроллера представления. Поэтому в какой-то момент я вижу метод контроллера
[PageViewControllerLocker() lockForView:self.view];
И в другой момент
[PageViewControllerLocker() unlockForView:self.view];
Ответ 10
Внутри viewviewcontroller есть subview, называемый quieingscrollview, у него есть 3 распознавателя жестов, которые управляют контроллером просмотра страницы
чтобы использовать их
[[pageViewController.view.subviews objectAtIndex:0] gestureRecognizers]
Ответ 11
В моем случае у меня была панель "Информация" UIView, которая опустилась поверх моего UIPageViewController, но распознаватели жестов в режиме просмотра страницы препятствовали навигации через мою информационную панель.
Мое решение состояло в том, чтобы установить dataSource в nil, но также не позволять контроллеру просмотра страниц обновлять фокус, пока информационная панель вверх:
- (BOOL)shouldUpdateFocusInContext:(UIFocusUpdateContext *)context
{
if (self.infoPanel) {
return NO;
} else {
return YES;
}
}
Ответ 12
Вы можете использовать методы делегата UIGestureRecognizer
для улавливания и отключения любых жестов. Например, вы можете использовать этот обратный вызов делегата: gestureRecognizer:shouldReceiveTouch:
. Просто убедитесь, что делегат установлен для всех распознавателей.