私は今しばらくの間アプリケーションに取り組んでいますが、NDAのためにこの質問をすることができませんでした。
ViewControllerでUIPageViewControllerをロードしています。 View Controllerにはボタンがあり、PageViewControllersジェスチャ認識エンジンによってオーバーライドされます。たとえば、ViewControllerの右側にボタンがあり、ボタンを押すと、PageViewControllerがページを引き継いで変更します。
PageViewControllerでボタンがタッチを受信し、ジェスチャー認識機能をキャンセルするにはどうすればよいですか?
PageViewControllerは、ViewControllerをそのビューのサブビューにすると思います。
すべてのジェスチャーをオフにできることは知っていますが、これは私が探している効果ではありません。
prefer PageViewControllerをサブクラス化しないAppleは、このクラスはサブクラス化することを意図していないと言います。
オーバーライドできます
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldReceiveTouch:(UITouch *)touch
pageViewControllerがタッチを受信するタイミングを制御する必要があります。 Dev API Gesture Recognizers の「タッチの分析からのジェスチャ認識の防止」を参照してください。
UIPageViewControllerのRootViewControllerでは、私のソリューションは次のようになります。
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、私は横向きモードのみです。)
編集-上記のコードをSwift 2:
ViewDidLoadの場合:
for gr in self.view.gestureRecognizers! {
gr.delegate = self
}
Page View Controllerに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
}
Xcodeテンプレートのself.view.gestureRecognizers = self.pageViewController.gestureRecognizers
部分の直後のviewDidLoad
テンプレートに追加できる別のソリューションがあります。ジェスチャレコグナイザーの内臓をいじったり、デリゲートを処理したりすることを避けます。ビューからタップジェスチャレコグナイザーを削除し、スワイプレコグナイザーのみを残します。
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のGestureRecognizersに置き換えられます。タッチが発生すると、まずpageViewControllersジェスチャレコグナイザーを介して送信されます。一致しない場合、サブビューに送信されます。
その行のコメントを外すだけで、すべてが期待どおりに機能します。
フィリップ
以下のようにviewRecognizersデリゲートをviewControllerに設定すると、ios6で機能しなくなります
for (UIGestureRecognizer *gR in self.view.gestureRecognizers) {
gR.delegate = self;
}
ios6では、pageViewControllerのgestureRecognizersデリゲートをviewControllerに設定するとクラッシュします
新しいバージョンでは(iOS 8.1以降を対象とするXcode 7.3を使用しています)、これらのソリューションはどれも機能していないようです。
受け入れられた答えはエラーでクラッシュします:
UIScrollViewの組み込みパンジェスチャレコグナイザーには、デリゲートとしてスクロールビューが必要です。
UIPageViewController
のscrollviewがチェックできない奇妙なジェスチャ認識サブクラスを使用しているように見えるため、現在最高ランクの回答(Pat McGから)も機能しません。したがって、ステートメント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
}
私は使った
for (UIScrollView *view in _pageViewController.view.subviews) {
if ([view isKindOfClass:[UIScrollView class]]) {
view.delaysContentTouches = NO;
}
}
クリックがUIPageViewController内のボタンに移動できるようにする
私の場合、UIPageControlでのタップを無効にし、画面上の別のボタンでタップが受信されるようにしました。スワイプは引き続き機能します。私は多くの方法を試しましたが、それが最も簡単な作業ソリューションだったと思います。
for (UIPageControl *view in _pageController.view.subviews) {
if ([view isKindOfClass:[UIPageControl class]]) {
view.enabled = NO;
}
}
これは、UIPageControllerサブビューからUIPageControlビューを取得し、ユーザーの操作を無効にします。
サブクラス化したボタンを使用している場合、touchesBegan、touchesMoved、touchesEndedをオーバーライドし、必要に応じて独自のプログラムページターンを呼び出しますが、superを呼び出してタッチを通知チェーンに渡すことはできません。
ユーザーがさまざまなページビューにジャンプ、カール、スライドするときに、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;
}
}
これで、タップは希望どおりに機能します(左右のタップでページがジャンプし、カールが正常に機能します)。
RootViewControllerにサブビュー(新しいIBOutletジェスチャビューにリンク)を作成し、この新しいビューにジェスチャを割り当てます。このビューは、ジェスチャを有効にする画面の部分をカバーしています。
viewDidLoadの変更:
self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;
に:
self.gesturesView.gestureRecognizers = self.pageViewController.gestureRecognizers;
タップ認識機能を削除するための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)
}
}
}
これは私にとって最もうまくいった解決策です。境界を超えてページングするときにエラーが発生することを除いて、JRAMERの答えを試しました
PatMCGソリューションでは、すべてのタップ認識機能がキャンセルされたため、十分な柔軟性が得られませんでした。まだラベルが必要ではなく、タップが必要でした
私のUILabelでは、次のように単純にオーバーライドしました。これは、ラベルのみのタップをキャンセルしました
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
return NO;
} else {
return YES;
}
}
私は実用的な解決策を考え出しました。別のUIGestureRecognizerをUIPageViewControllerに追加し、以下に示すデリゲートメソッドを実装します。どのジェスチャーをロックするか、さらに渡す必要があるかを解決する必要があるすべての瞬間に、このメソッドが呼び出されます。 confictingView
への参照を忘れずに提供してください。これは、私の場合はUITableViewであり、パンジェスチャも認識します。このビューはUIPageViewController内に配置されたため、パンジェスチャは2回またはランダムに認識されました。このメソッドでは、パンジェスチャが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
}
UIPageViewController内の子View Controllerのタップに応答するためのシンプルでキャッチオールな方法を探している間に、私はここで終わりました。私のソリューションの中核(Swift 4、Xcode 9)は、RootViewController.Swift
(Xcodeの "Page-Based App"テンプレートと同じ構造)で、これと同じくらい簡単になりました:
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")
}
(また、 this answer を使用して、実際にタップされたページ、つまり現在のページを追跡できるようにしました。)
また、これを使用することもできます(デリゲートについて言うと、助けてくれてありがとう):
// 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;
}
私のために完璧に働く:)