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の遷移スタイルがスクロールに設定されているため、ジェスチャ認識機能にアクセスできません。
これを回避する方法はありますか?カールの移行がうまく機能するので、私は何も悪いことをしているとは思いません。
この動作には レーダーで提出されたバグ があります。したがって、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
にバグがあるように見えますが(少なくとも、実際の動作はリファレンスの記述と明らかに異なります)、これ以上の解決策はありません。
これはUIPageViewController.hで見つかりました:
//遷移スタイルが 'UIPageViewControllerTransitionStylePageCurl'の場合にのみ入力されます。 @property(nonatomic、readonly)NSArray * gestureRecognizers;
したがって、バグではありません-設計により、スクロールスタイルが設定されている場合、pageViewControllerはジェスチャー認識機能を取得しません。
ページビューコントローラーのサブビューで、いつでもユーザー操作を無効にすることができます。
for (UIScrollView *view in self.pageViewController.view.subviews) {
if ([view isKindOfClass:[UIScrollView class]]) {
view.scrollEnabled = NO;
}
}
UIPageViewControllerヘッダーファイルによると、データソースがnilの場合、ジェスチャー駆動のナビゲーションは無効になります。
したがって、スワイプを無効にする場合はdatasourceをnilに設定し、スワイプを有効にする場合はデータソースをリセットします。
つまり.
// turns off paging
pageViewController.datasource = nil
// turns on paging
pageViewController.datasource = self;
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:)];
}
}
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の内部使用を変更しないようにする必要があるため、これは最善の将来の証明ソリューションではない可能性があります。しかし...
ジェスチャ認識機能を見つけて削除できると仮定すると、非常に壊れやすくなります。 AppleはインプリメントUIPageViewControllerを使用してアプリに機能を提供します。これが変更された場合(iOS 5とiOS 6の間など)、コードアプリは動作し始めます。これは、プライベートAPIを使用するようなものです-次のOSリリースで動作することを保証するものではありません。
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ではなく空であることが示されていました。しかし興味深いのは、ビューも認識機能をあきらめなかったことです。スクロール機能はあるので、少なくともパン認識機能が必要です...
私の場合、UIPageViewControllerの上にUIViewの「情報パネル」が表示されていましたが、ページビューコントローラーのジェスチャーレコグナイザーが情報パネルのナビゲーションを妨げていました。
私の解決策は、dataSourceをnilに設定することでしたが、情報パネルが起動しているときにページビューコントローラーがフォーカスを更新できないようにすることでもありました。
- (BOOL)shouldUpdateFocusInContext:(UIFocusUpdateContext *)context
{
if (self.infoPanel) {
return NO;
} else {
return YES;
}
}
歴史のためだけの別のソリューション。任意のビューをジェスチャ認識ブレーカにすることができ、そのビューの長方形で機能するはずです。デリゲートを持つ別の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];
Pageviewcontrollerの内部には、quieingscrollviewと呼ばれるサブビューがあり、3つのジェスチャー認識機能があり、ページビューコントローラーを制御して使用します。
[[pageViewController.view.subviews objectAtIndex:0] gestureRecognizers]