web-dev-qa-db-ja.com

UIPageViewControllerがiOS 6でジェスチャーレコグナイザーを返さない

UIPageViewControllerのパンジェスチャ認識機能を無効にしようとしています。

IOS 5では、それらをループして無効にすることができます。

for (UIGestureRecognizer* recognizer in self.pageViewController.gestureRecognizers) {
    if ([recognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
        recognizer.enabled = NO;
    }
}

UIPageViewControllerTransitionStyleScrollを使用するiOS 6では、ページビューコントローラーから返されるジェスチャー認識機能はありません。

明確化

これは、次のように要約できます。

self.pageViewController.gestureRecognizers =0。UIPageViewControllerの遷移スタイルがスクロールに設定されているため、ジェスチャ認識機能にアクセスできません。

これを回避する方法はありますか?カールの移行がうまく機能するので、私は何も悪いことをしているとは思いません。

35
bjtitus

この動作には レーダーで提出されたバグ があります。したがって、Appleが修正されるまで、これを解決する機会はないでしょう)。

私の頭に浮かぶ1つの回避策は、透明なサブビューをUIPageViewControllerの上に配置し、UIPanGestureRecognizerを追加して、そのようなジェスチャーをインターセプトし、それ以上先に進めないようにすることです。ジェスチャーを無効にする必要がある場合は、このビュー/認識機能を有効にすることができます。

パンとタップのジェスチャー認識機能を組み合わせて試してみましたが、うまくいきました。

これは私のテストコードです:

- (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にバグがあるように見えますが(少なくとも、実際の動作はリファレンスの記述と明らかに異なります)、これ以上の解決策はありません。

26
sergio

これはUIPageViewController.hで見つかりました:

//遷移スタイルが 'UIPageViewControllerTransitionStylePageCurl'の場合にのみ入力されます。 @property(nonatomic、readonly)NSArray * gestureRecognizers;

したがって、バグではありません-設計により、スクロールスタイルが設定されている場合、pageViewControllerはジェスチャー認識機能を取得しません。

32
leanne

ページビューコントローラーのサブビューで、いつでもユーザー操作を無効にすることができます。

for (UIScrollView *view in self.pageViewController.view.subviews) {
    if ([view isKindOfClass:[UIScrollView class]]) {
        view.scrollEnabled = NO;
    }
}
28
dstulic

UIPageViewControllerヘッダーファイルによると、データソースがnilの場合、ジェスチャー駆動のナビゲーションは無効になります。

したがって、スワイプを無効にする場合はdatasourceをnilに設定し、スワイプを有効にする場合はデータソースをリセットします。

つまり.

// turns off paging
pageViewController.datasource = nil

// turns on paging
pageViewController.datasource = self;
18
3rdFunkyBot

UIPageViewControllerからUIScrollviewを介してUIPanGestureRecognizerにアクセスできます。

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:)];
    }
}
8
Berni

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 UIPageViewControllersのUIScrollViewの内部使用を変更しないようにする必要があるため、これは最善の将来の証明ソリューションではない可能性があります。しかし...

2
schmidiii

ジェスチャ認識機能を見つけて削除できると仮定すると、非常に壊れやすくなります。 AppleはインプリメントUIPageViewControllerを使用してアプリに機能を提供します。これが変更された場合(iOS 5とiOS 6の間など)、コードアプリは動作し始めます。これは、プライベートAPIを使用するようなものです-次のOSリリースで動作することを保証するものではありません。

1
Michael

KVC(Key Value Coding)メソッドを使用して認識機能にアクセスするとどうなりますか? (現時点では、これをテストできる場所ではありません。)

簡単なテストは、gestureRecognizersの数を取得することです。

[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"]];

違いはありませんでした。カールスタイルは両方の方法でうまく機能しました。スクロールスタイルでは、配列がnilではなく空であることが示されていました。しかし興味深いのは、ビューも認識機能をあきらめなかったことです。スクロール機能はあるので、少なくともパン認識機能が必要です...

1
leanne

私の場合、UIPageViewControllerの上にUIViewの「情報パネル」が表示されていましたが、ページビューコントローラーのジェスチャーレコグナイザーが情報パネルのナビゲーションを妨げていました。

私の解決策は、dataSourceをnilに設定することでしたが、情報パネルが起動しているときにページビューコントローラーがフォーカスを更新できないようにすることでもありました。

- (BOOL)shouldUpdateFocusInContext:(UIFocusUpdateContext *)context
{
    if (self.infoPanel) {
        return NO;
    } else {
        return YES;
    }
}
0
a-r-studios

歴史のためだけの別のソリューション。任意のビューをジェスチャ認識ブレーカにすることができ、そのビューの長方形で機能するはずです。デリゲートを持つ別のUIPanGestureRecognizerが必要です。 1つのメソッドを持つ任意のオブジェクトにすることができます。

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];
0
Igor Bulgakov

Pageviewcontrollerの内部には、quieingscrollviewと呼ばれるサブビューがあり、3つのジェスチャー認識機能があり、ページビューコントローラーを制御して使用します。

[[pageViewController.view.subviews objectAtIndex:0] gestureRecognizers]
0
user2568379