iOS 7のUINavigationControllerで戻るスワイプジェスチャーを無効にする方法


326

iOS 7では、Appleは新しいデフォルトのナビゲーション動作を追加しました。画面の左端からスワイプすると、ナビゲーションスタックに戻ることができます。しかし、私のアプリでは、この動作がカスタムの左メニューと競合します。では、UINavigationControllerでこの新しいジェスチャーを無効にすることは可能ですか?



2
また、を設定するとnavigationItem.hidesBackButton = true、このジェスチャーも無効になることがわかりました。私の場合、カスタムの戻るボタンを実装し、次のものを追加しますleftBarButtonItem
Umair

回答:


586

私は解決策を見つけました:

Objective-C:

if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
    self.navigationController.interactivePopGestureRecognizer.enabled = NO;
}

Swift 3以降:
self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false


29
もちろん、古いバージョンのiOSをサポートしている場合は、新しいメソッドが利用可能かどうかを確認する必要があります。
ArtFeel 2013年

2
ビューの一部でそれを無効にする方法はありますか?
Marc

11
/でenable / disable認識機能を使用できます。または、より複雑なロジックでプロトコルを実装し、それをプロパティとして設定できます。viewDidAppear:viewDidDisappearUIGestureRecognizerDelegaterecognizer.delegate
ArtFeel 2013年

26
iOS8で、設定self.navigationController.interactivePopGestureRecognizer.enabledプロパティは、ビューのメソッドを次のように動作しません:viewDidLoadviewWillAppearviewDidAppearviewDidDisappear、しかし、この方法で作品viewWillDisappear。iOS7では、上記のすべての方法で機能します。そのため、viewControllerでの作業中に他のメソッドで使用してみてください。ビューの内側のボタンをクリックすると、iOS8で機能することを確認します。
Sihad Begovic 2014

8
これはiOS8ではviewDidLoadとviewWillAppearで機能しないことを確認できます。viewwilllayoutgubviewsに入れるとうまくいきました
tonytastic

47

ジェスチャーを無効に設定するだけでは機能しない場合があることがわかりました。それは機能しますが、私にとっては、私が一度バックジェスチャーを使用した後にのみ機能しました。2回目は、バックジェスチャーをトリガーしません。

私の修正は、ジェスチャーを委任し、shouldbeginメソッドを実装してNOを返すことでした:

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    // Disable iOS 7 back gesture
    if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
        self.navigationController.interactivePopGestureRecognizer.enabled = NO;
        self.navigationController.interactivePopGestureRecognizer.delegate = self;
    }
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];

    // Enable iOS 7 back gesture
    if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
        self.navigationController.interactivePopGestureRecognizer.enabled = YES;
        self.navigationController.interactivePopGestureRecognizer.delegate = nil;
    }
}

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    return NO;
}

1
ありがとう!これは、バックスワイプを完全に無効にするために必要です。それはまだiOS 8に存在し、Appleのバグのようなにおいがします。
Eric Chen

ありがとう、うまくいった唯一のことのようです。
Ben

理由はわかりませんが、アプリのビューコントローラーが何らかの理由でこのバックジェスチャーでクラッシュしました。このバックジェスチャーは必要ないため、このコードを使用して無効にしたため、このコードを使用できなくなりました。+ 1
Ahsan Ebrahim

1
@AhsanEbrahimは、戻るジェスチャーが始まるとviewWillAppear、現在のビューの背後にあるビューで呼び出されます。現在のビューがまだアクティブであるため、コードロジックに混乱を引き起こす可能性があります。クラッシュの原因である可能性があります。
ファットマン、2015年

されているenabledはい/いいえ行は必要ありませんか?NOから戻りgestureRecognizerShouldBeginます。それで十分ではありませんか?
ToolmakerSteve 2016

30

NavigationControllerからジェスチャー認識機能を削除するだけです。iOS 8で動作します。

if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)])
    [self.navigationController.view removeGestureRecognizer:self.navigationController.interactivePopGestureRecognizer];

iOS 8および9で実際に機能する唯一のソリューション
Kappe 2015年

7
これはiOS 10でも機能します。これは受け入れられる答えです。ちなみに、再度有効にしたい場合は、[self.navigationController.view addGestureRecognizer:self.navigationController.interactivePopGestureRecognizer]どこかで行ってください。
2016年

22

iOS 8以降、受け入れられた回答は機能しなくなりました。私はメインのゲーム画面でジェスチャーを閉じるためにスワイプを停止する必要があったので、これを実装しました:

- (void)viewDidAppear:(BOOL)animated
{
     [super viewDidAppear:animated];

if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
    self.navigationController.interactivePopGestureRecognizer.delegate = self;
    }
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
    self.navigationController.interactivePopGestureRecognizer.delegate = nil;
    }

}

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
     return NO;
}

2
これはiOS8で機能しますが、*。delegate = self;という行で警告が表示されます。説明:互換性のないタイプ 'ViewController * const __strong'からid <UIGestureRecognizerDelegate> 'への割り当て
David Douglas

2
iOS8の時点では、受け入れられた回答は引き続き期待どおりに機能します。あなたは、おそらく..間違って何かをやっている
アレクサンドル・G

受け入れられた回答をviewWillLayoutSubviewsで呼び出すことにより、それを半機能させることができました。ただし、スワイプするとページが再び「viewDidLoad」を呼び出すため、上記の私の答えに戻りました
Charlie Seligman

@DavidDouglas:次のコードで警告を排除できるでしょう:__weak __typeof(self)theSafeSelf = self?次に、デリゲートをtheSafeSelfに設定します。
lifjoy 2015年

1
@DavidDouglas:あなたはその警告を取り除くために、インターフェイスに<UIGestureRecognizerDelegate>を追加する必要があります
primehalo

20

次の理由により、Twanの回答を少し調整しました。

  1. ビューコントローラーが他のジェスチャー認識機能へのデリゲートとして設定されている可能性があります
  2. デリゲートをに設定するnilと、ルートビューコントローラーに戻ってスワイプジェスチャーを行うときに、他の場所に移動するときに問題が発生します。

次の例では、iOS 7を想定しています。

{
    id savedGestureRecognizerDelegate;
}

- (void)viewWillAppear:(BOOL)animated
{
    savedGestureRecognizerDelegate = self.navigationController.interactivePopGestureRecognizer.delegate;
    self.navigationController.interactivePopGestureRecognizer.delegate = self;
}

- (void)viewWillDisappear:(BOOL)animated
{
    self.navigationController.interactivePopGestureRecognizer.delegate = savedGestureRecognizerDelegate;
}

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    if (gestureRecognizer == self.navigationController.interactivePopGestureRecognizer) {
        return NO;
    }
    // add whatever logic you would otherwise have
    return YES;
}

+1 「デリゲートをnilに設定すると、ルートビューコントローラーに戻り、スワイプジェスチャーを行ってから、他の場所に移動するときに問題が発生します。」
albertamg

10

これをルートvcに設定してください:

-(void)viewDidAppear:(BOOL)animated{
    [super viewDidAppear:YES];
    self.navigationController.interactivePopGestureRecognizer.enabled = NO;

}

-(void)viewDidDisappear:(BOOL)animated{
    [super viewDidDisappear:YES];
    self.navigationController.interactivePopGestureRecognizer.enabled = YES;
}

9

Swiftの場合:

navigationController!.interactivePopGestureRecognizer!.enabled = false

12
これは機能しますが、強制アンラップの代わりにオプションのチェーンを使用することをお勧めします。例:self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
Womble

5

それは私にとってios 10以降で動作します:

- (void)viewWillAppear:(BOOL)animated {
    if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
        self.navigationController.interactivePopGestureRecognizer.enabled = NO;
    }

}

viewDidLoad()メソッドでは機能しません。


5

編集

特定のナビゲーションコントローラーのスワイプバック機能を管理する場合は、SwipeBackの使用を検討してください

これで、設定できます navigationController.swipeBackEnabled = NO

例えば:

#import <SwipeBack/SwipeBack.h>

- (void)viewWillAppear:(BOOL)animated
{
    navigationController.swipeBackEnabled = NO;
}

CocoaPods経由でインストールできます。

pod 'SwipeBack', '~> 1.0'

説明が足りないことをお詫び申し上げます。


6
あなたが関与しているプロジェクトを推進するとき、あなたはそれとの関係を開示しなければなりません。

2
さらに、プロジェクトの唯一の目的は、デフォルトのシステムジェスチャーが機能していないときにスワイプジェスチャーを手動で有効にすることですが、質問ではそのシステム全体のジェスチャーを無効にする方法を尋ねるので、設定したとしてもself.navigationController.swipeBackEnabled = NO、ライブラリのスワイプバックジェスチャーですが、システムのジェスチャーは引き続き有効です。

1
短い回答で申し訳ありません。「特定のナビゲーションコントローラーに便利です」という追加情報で回答を編集しました。ありがとう!
devxoul 2015

許可されなくなったスウィズルを使用しているようです。iOS8?
マット

1
@devxoulごめんなさい!少し前に、スウィズリングは許可されないと言って読んだと思いました。しかし、私はこれを言うものを見つけることができません。私が間違っていると思います。
マット

4

私の方法。それらすべてを支配する1つのジェスチャー認識機能:

class DisabledGestureViewController: UIViewController: UIGestureRecognizerDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()
        navigationController!.interactivePopGestureRecognizer!.delegate = self
    }

    func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {
        // Prevent going back to the previous view
        return !(navigationController!.topViewController is DisabledGestureViewController)
    }
}

重要:ナビゲーションスタックのどこにもデリゲートをリセットしないでください。 navigationController!.interactivePopGestureRecognizer!.delegate = nil


3

これがSwift 3の方法です

私のために働く

    self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false

3

これらのソリューションはすべて、Appleのジェスチャー認識機能を推奨しない方法で操作します。私は友人からもっと良い解決策があると言われました:

[navigationController.interactivePopGestureRecognizer requireGestureRecognizerToFail: myPanGestureRecognizer];

ここで、myPanGestureRecognizerは、メニューの表示などに使用するジェスチャ認識機能です。そうすることで、新しいナビゲーションコントローラーを押してもAppleのジェスチャー認識機能がオンに戻らず、携帯電話がスリープ状態になったり、負荷がかかったりした場合に早く発火する可能性があるハッキーな遅延に頼る必要がありません。

これは、次回必要になったときに覚えていないことがわかっているので、ここに残します。この問題の解決策はここにあります。


3

swift 5、swift 4.2は以下のコードを使用できます。

// disable
self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
// enable
self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true

2

与えられた回答のどれも私が問題を解決するのに役立ちませんでした。ここに私の答えを投稿します。誰かのために役立つかもしれません

private var popGesture: UIGestureRecognizer?ビューコントローラーでグローバル変数として宣言します。次に、viewDidAppearメソッドとviewWillDisappearメソッドにコードを実装します。

override func viewDidAppear(animated: Bool) {

    super.viewDidAppear(animated)

    if self.navigationController!.respondsToSelector(Selector("interactivePopGestureRecognizer")) {

        self.popGesture = navigationController!.interactivePopGestureRecognizer
        self.navigationController!.view.removeGestureRecognizer(navigationController!.interactivePopGestureRecognizer!)
    }
}


override func viewWillDisappear(animated: Bool) {

    super.viewWillDisappear(animated)

    if self.popGesture != nil {
        navigationController!.view.addGestureRecognizer(self.popGesture!)
    }
}

これにより、iOS v8.x以降でスワイプバックが無効になります


私はこれがどのような状況で機能するかを想像しようとしていますが、ジャックは機能しません。あなたは他のすべての答えを試したと言います:ジャックを試したときに何が悪かったのですか?
ToolmakerSteve 2016

一方、これはジャックのものより単純に見えるので、おそらくそれは重要ではありません。私のクラスをデリゲートとして宣言したり操作したりする必要がないため、これが好きだと判断しましたinteractivePopGestureRecognizer.delegate
ToolmakerSteve

ところで、コードは簡略化できます。を削除しif( .. respondsToSelector ..ます。次の行はpopGestureを認識エンジンまたはnilに設定します。次に、その値を使用しますif (self.popGesture != nil) self.navigationController .. removeGestureRecognizer( self.popGesture )
ToolmakerSteve 2016

2

これviewDidLoad:はiOS 8で機能します。

  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
      self.navigationController.interactivePopGestureRecognizer.enabled = false;
  });

多くの問題は、良いolの助けを借りて解決できます dispatch_after

このソリューションは安全でない可能性があることに注意してください。独自の推論を使用してください。

更新

iOS 8.1の場合、遅延時間は0.5秒にする必要があります

iOS 9.3では遅延は必要なくなりました。これをviewDidLoad:に 配置するだけで機能します
(iOS 9.0-9.3で機能する場合は未定)

navigationController?.interactivePopGestureRecognizer?.enabled = false

ジェスチャレコグナイザがいつビューにインストールされたかがわからない場合、無効にするために任意の時間待機することは、機能する場合と機能しない場合があります。
kalperin

@kalperinは、動作が保証されていませんが、非常に便利な解決策になる場合もあります。独自の推論を使用してください。
Dannie P 2016

iOS 8.1以上のバージョンを持っている私にとってはそれは機能します:)
iChirag

viewDidLoad加えて遅延は危険なプログラミング慣行です。始めに悪い習慣。遅延した通話が始まる前にユーザーがスワイプを開始した場合はどうなりますか?十分な長さを保証しているが、長すぎない安全な時間はありません。そのため、あなたの回答よりずっと前に投稿された他の回答が、コードをに配置することを推奨していますviewDidAppear。これにより、すべてがインストールされます。任意の遅延を発明しないでください。Appleの一連の呼び出しを意図したとおりに使用します。
ToolmakerSteve 2016

1
@iChirag true。私は8.1のために、あなたが0.5秒の遅延必要があることを指摘してきました
Dannie P

1

スウィフト4、これは動作します:

class MyViewController: UIViewController, UIGestureRecognizerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        self.navigationController?.interactivePopGestureRecognizer?.gesture.delegate = self
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(true)

        self.navigationController?.interactivePopGestureRecognizer?.gesture.isEnabled = false
    }

}

インタラクティブなポップジェスチャーデリゲートをオーバーライドしないでください。ドキュメント化されていない動作が発生します
Josh Bernfeld

デリゲートをオーバーライドするのではなく、まさにこの目的のために提供したブール変数を変更するだけなので、問題にはなりません
Lok SN

0

ほとんどのビューコントローラーでうまくいきました。

self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false

UIPageViewControllerのような一部のビューコントローラーでは機能しませんでした。以下のUIPageViewControllerのpagecontentviewcontrollerでコードが機能しました。

override func viewDidLoad() {
   self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
   self.navigationController?.interactivePopGestureRecognizer?.delegate = self
}
override func viewWillDisappear(_ animated: Bool) {
   self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
   self.navigationController?.interactivePopGestureRecognizer?.delegate = nil
}

UIGestureRecognizerDelegateで、

func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
   if gestureRecognizer == self.navigationController?.interactivePopGestureRecognizer {
      return false
}
      return true
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.