ナビゲーションコントローラーの戻るボタンのアクションの設定


180

ナビゲーションコントローラーの[戻る]ボタンの既定のアクションを上書きしようとしています。ターゲットにカスタムボタンのアクションを提供しました。奇妙なことに、それをbackbutton属性に割り当てても、それらには注意が払われず、現在のビューをポップしてルートに戻るだけです。

UIBarButtonItem *backButton = [[UIBarButtonItem alloc] 
                                  initWithTitle: @"Servers" 
                                  style:UIBarButtonItemStylePlain 
                                  target:self 
                                  action:@selector(home)];
self.navigationItem.backBarButtonItem = backButton;

leftBarButtonItemon を設定するとすぐnavigationItemにアクションが呼び出されますが、ボタンは矢印の付いたボタンではなく、単純な丸いボタンのように見えます。

self.navigationItem.leftBarButtonItem = backButton;

ルートビューに戻る前にカスタムアクションを呼び出すにはどうすればよいですか?デフォルトの戻るアクションを上書きする方法はありますか、またはビューを離れるときに常に呼び出されるメソッドはありviewDidUnloadますか?


action:@selector(home)]; :セレクターアクションの後:@selector(home :)];が必要です。それ以外の場合は機能しません
PartySoft

7
@PartySoftメソッドがコロンで宣言されていない限り、これは当てはまりません。パラメーターをとらないボタンセレクターを呼び出すことは完全に有効です。
mbm29414


3
なぜAppleは、戻るボタンのような形のボタンを提供しないのですか?かなり明白なようです。
JohnK 2013年

回答:


363

これを、プレスを検出するビューコントローラに配置してみてください。

-(void) viewWillDisappear:(BOOL)animated {
    if ([self.navigationController.viewControllers indexOfObject:self]==NSNotFound) {
       // back button was pressed.  We know this is true because self is no longer
       // in the navigation stack.  
    }
    [super viewWillDisappear:animated];
}

1
これは、滑らかで、クリーンで、
すばらしく

6
素晴らしいハックを+1しますが、ポップのアニメーションを制御できません
matm

3
ボタンを介してデリゲートにメッセージを送信し、デリゲートがコントローラーをポップする場合、私には機能しません-これはまだ発生します。
SAHM、

21
別の問題は、ユーザーが[戻る]ボタンを押したのか、それともプログラムで[self.navigationController popViewControllerAnimated:YES]を呼び出したのかを区別できないことです
Chase Roberts

10
ただif (find(self.navigationController!.viewControllers as! [UIViewController],self)==nil)
参考までに

177

UIViewController-BackButtonHandler拡張機能を実装しました。それは何もサブクラス化する必要はありません、それをあなたのプロジェクトに入れてクラスのnavigationShouldPopOnBackButtonメソッドをオーバーライドするだけUIViewControllerです:

-(BOOL) navigationShouldPopOnBackButton {
    if(needsShowConfirmation) {
        // Show confirmation alert
        // ...
        return NO; // Ignore 'Back' button this time
    }
    return YES; // Process 'Back' button click and Pop view controler
}

サンプルアプリをダウンロードしてください


16
これは私が見た中で最もクリーンなソリューションであり、独自のカスタムUIButtonを使用するよりも簡単です。ありがとう!
ramirogm 2013年

4
これnavigationBar:shouldPopItem:UINavigationBarDelegateプロトコルの一部であるため、プライベートメソッドではありません。
onegray 2013年

1
UINavigationControllerは既にデリゲートを実装していYESますか?それとも将来的には?サブクラス化はおそらくより安全なオプションです
Sam

8
私はiOS 7.1にこれを実装しました(かなりクールなBTW)。NO戻るボタンを押した後、戻るボタンは無効な状態のままです(視覚的には、タッチイベントを受け取って反応するため)。チェックにelseステートメントを追加shouldPopし、ナビゲーションバーのサブビューを循環させalpha、アニメーションブロック内で必要に応じて値を1に戻すことで回避しました
boliva '25

2
これは私が今まで見た中で最高の拡張機能の1つです。どうもありがとうございます。
Srikanth、2014年

42

アマグラマーが言ったのとは異なり、それは可能です。をサブクラス化する必要がありますnavigationControllerここですべてを説明しました(コード例を含む)。


Appleのドキュメント(developer.apple.com/iphone/library/documentation/UIKit/…)には、「このクラスはサブクラス化を目的としていない」とあります。これが何を意味するのかはわかりませんが、「通常は行う必要はない」という意味、または「コントローラーをいじったらアプリを拒否する」という意味になるかもしれません...
Kuba Suder

これは確かにそれを行う唯一の方法です。私はあなたにハンスより多くのポイントを与えたいと思います!
Adam Eberbach、

1
この方法を使用して、ビューが実際に終了するのを防ぐことができますか?ビューを終了させたくない場合、popViewControllerAnimatedメソッドは何を返すようにしますか?
JosephH 2010

1
ええ、できます。実装でスーパークラスメソッドを呼び出さないでください。あなたはそれをすべきではありません、ユーザーはナビゲーションに戻ることを期待しています。あなたができることは確認を求めることです。Appleのドキュメントによると、popViewControllerは「スタックからポップされたビューコントローラ」を返します。したがって、何もポップされていない場合、nilを返す必要があります。
HansPinckaers、2010

1
@HansPickaersビューの終了を防ぐことについてのあなたの答えは、いくぶん正しくないかもしれません。popViewControllerAnimated:のサブクラス実装からの「確認」メッセージを表示する場合、NavigationBarは、何を返すかに関係なく、ツリー内で1レベル上にアニメーション化します。これは、戻るボタンをクリックすると、ナビゲーションバーのshouldPopNavigationItemが呼び出されるためと思われます。推奨どおり、サブクラスのメソッドからnilを返しています。
ディープウィンター

15

Swiftバージョン:

https://stackoverflow.com/a/19132881/826435の

ビューコントローラーでは、プロトコルに準拠し、必要なアクションを実行します。

extension MyViewController: NavigationControllerBackButtonDelegate {
    func shouldPopOnBackButtonPress() -> Bool {
        performSomeActionOnThePressOfABackButton()
        return false
    }
}

次に、クラスを作成して、NavigationController+BackButton次のコードをコピーして貼り付けます。

protocol NavigationControllerBackButtonDelegate {
    func shouldPopOnBackButtonPress() -> Bool
}

extension UINavigationController {
    public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
        // Prevents from a synchronization issue of popping too many navigation items
        // and not enough view controllers or viceversa from unusual tapping
        if viewControllers.count < navigationBar.items!.count {
            return true
        }

        // Check if we have a view controller that wants to respond to being popped
        var shouldPop = true
        if let viewController = topViewController as? NavigationControllerBackButtonDelegate {
            shouldPop = viewController.shouldPopOnBackButtonPress()
        }

        if (shouldPop) {
            DispatchQueue.main.async {
                self.popViewController(animated: true)
            }
        } else {
            // Prevent the back button from staying in an disabled state
            for view in navigationBar.subviews {
                if view.alpha < 1.0 {
                    UIView.animate(withDuration: 0.25, animations: {
                        view.alpha = 1.0
                    })
                }
            }

        }

        return false
    }
}

多分私は何かを逃したが、それは私のために機能しません、拡張機能のメソッドperformSomeActionOnThePressOfABackButtonは決して呼び出されません
Turvy

@FlorentBretonおそらく誤解?shouldPopOnBackButtonPressバグがない限り呼び出されるはずです。performSomeActionOnThePressOfABackButton存在しない単なる作り上げの方法です。
kgaidis 2016年

それがわかったので、performSomeActionOnThePressOfABackButton戻るボタンが押されたときに特定のアクションを実行するメソッドをコントローラーに作成しましたが、このメソッドは呼び出されませんでした。アクションは通常のバックリターンです
Turvy

1
私のためにも働いていません。shouldPopメソッドが呼び出されることはありません。デリゲートをどこかに設定しましたか?
Tim Autin 2017年

@TimAutinもう一度テストしたところ、何かが変わったようです。UINavigationControllerで、navigationBar.delegateがナビゲーションコントローラに設定されていることを理解するための重要な部分。したがって、メソッドは呼び出される必要があります。しかし、Swiftでは、サブクラスであっても呼び出すことができません。ただし、Objective-Cで呼び出されるようにしたので、今のところObjective-Cバージョンを使用します。Swiftのバグかもしれません。
kgaidis

5

直接行うことはできません。いくつかの選択肢があります:

  1. UIBarButtonItemテストに合格した場合にタップとポップで検証する独自のカスタムを作成する
  2. またはボタンがキーボードで押された後に呼び出されるUITextFieldなどのデリゲートメソッドを使用して、フォームフィールドの内容を検証します。-textFieldShouldReturn:ReturnDone

最初のオプションの欠点は、戻るボタンの左向き矢印スタイルにカスタムバーボタンからアクセスできないことです。したがって、画像を使用するか、通常のスタイルのボタンを使用する必要があります。

2番目のオプションは、デリゲートメソッドでテキストフィールドを取得できるので便利です。デリゲートコールバックメソッドに送信される特定のテキストフィールドを検証ロジックの対象にすることができます。


5

いくつかのスレッドの理由により、@ HansPinckaersによって言及された解決策は私には適切ではありませんでしたが、私は戻るボタンにタッチするのを非常に簡単にキャッチする方法を見つけました。他の誰か。トリックは本当に簡単です。透明なUIButtonをサブビューとしてUINavigationBarに追加し、セレクターを実際のボタンのように設定します。これはMonotouchとC#を使用した例ですが、objective-cへの変換はそれほど難しくないはずです。

public class Test : UIViewController {
    public override void ViewDidLoad() {
        UIButton b = new UIButton(new RectangleF(0, 0, 60, 44)); //width must be adapted to label contained in button
        b.BackgroundColor = UIColor.Clear; //making the background invisible
        b.Title = string.Empty; // and no need to write anything
        b.TouchDown += delegate {
            Console.WriteLine("caught!");
            if (true) // check what you want here
                NavigationController.PopViewControllerAnimated(true); // and then we pop if we want
        };
        NavigationController.NavigationBar.AddSubview(button); // insert the button to the nav bar
    }
}

楽しい事実は:テスト目的で、私の偽のボタンのために良い大きさを発見するために、私は青にその背景色を設定し...そして、それは示して後ろに戻るボタン!とにかく、元のボタンをターゲットとするタッチをキャッチします。


3

この手法を使用すると、ビューコントローラーのタイトルに影響を与えたり、アニメーション中に戻るボタンのテキストが変化したりすることなく、「戻る」ボタンのテキストを変更できます。

これを呼び出し側のビューコントローラーのinitメソッドに追加します。

UIBarButtonItem *temporaryBarButtonItem = [[UIBarButtonItem alloc] init];   
temporaryBarButtonItem.title = @"Back";
self.navigationItem.backBarButtonItem = temporaryBarButtonItem;
[temporaryBarButtonItem release];

3

最も簡単な方法

UINavigationControllerのデリゲートメソッドを使用できます。このメソッドwillShowViewControllerは、VCの戻るボタンが押されたときに呼び出されます。戻るbtnが押されたときに必要な操作を実行します。

- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated;

ビューコントローラがそれ自体を継承されたnavigationControllerのデリゲートとして設定し、UINavigationControllerDelegateプロトコルに準拠していることを確認してください
Justin Milo

3

これが私のSwiftソリューションです。UIViewControllerのサブクラスで、navigationShouldPopOnBackButtonメソッドをオーバーライドします。

extension UIViewController {
    func navigationShouldPopOnBackButton() -> Bool {
        return true
    }
}

extension UINavigationController {

    func navigationBar(navigationBar: UINavigationBar, shouldPopItem item: UINavigationItem) -> Bool {
        if let vc = self.topViewController {
            if vc.navigationShouldPopOnBackButton() {
                self.popViewControllerAnimated(true)
            } else {
                for it in navigationBar.subviews {
                    let view = it as! UIView
                    if view.alpha < 1.0 {
                        [UIView .animateWithDuration(0.25, animations: { () -> Void in
                            view.alpha = 1.0
                        })]
                    }
                }
                return false
            }
        }
        return true
    }

}

UIViewControllerのメソッドnavigationShouldPopOnBackButtonのオーバーライドが機能しません-プログラムは、オーバーライドされたメソッドではなく親メソッドを実行します。そのための解決策はありますか?誰か同じ問題がありますか?
Pawel Cala 2015

trueを返すとルートビューに戻ります
Pawriwes

@Pawriwesこれは私が書いた解決策で、私にとってはうまくいくようです:stackoverflow.com/a/34343418/826435
kgaidis

3

戻るボタンのスタイルも保持するソリューションを見つけました。次のメソッドをビューコントローラに追加します。

-(void) overrideBack{

    UIButton *transparentButton = [[UIButton alloc] init];
    [transparentButton setFrame:CGRectMake(0,0, 50, 40)];
    [transparentButton setBackgroundColor:[UIColor clearColor]];
    [transparentButton addTarget:self action:@selector(backAction:) forControlEvents:UIControlEventTouchUpInside];
    [self.navigationController.navigationBar addSubview:transparentButton];


}

次のメソッドで必要に応じて機能を提供します。

-(void)backAction:(UIBarButtonItem *)sender {
    //Your functionality
}

それがするすべては透明なボタンで戻るボタンを覆うことです;)


3

navigationBar(_ navigationBar:shouldPop)のオーバーライド:機能して、これは良い考えではありません。私にとっては、戻るときにランダムなクラッシュが発生しました。以下のように、navigationItemからデフォルトのbackButtonを削除し、カスタムの戻るボタンを作成して、戻るボタンをオーバーライドすることをお勧めします。

override func viewDidLoad(){
   super.viewDidLoad()
   
   navigationItem.leftBarButton = .init(title: "Go Back", ... , action: #selector(myCutsomBackAction) 

   ...
 
}

========================================

前の回答に構築UIAlertSwift5非同期方法


protocol NavigationControllerBackButtonDelegate {
    func shouldPopOnBackButtonPress(_ completion: @escaping (Bool) -> ())
}

extension UINavigationController: UINavigationBarDelegate {
    public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
      
        if viewControllers.count < navigationBar.items!.count {
            return true
        }
        
        // Check if we have a view controller that wants to respond to being popped
        
        if let viewController = topViewController as? NavigationControllerBackButtonDelegate {
            
            viewController.shouldPopOnBackButtonPress { shouldPop in
                if (shouldPop) {
                    /// on confirm => pop
                    DispatchQueue.main.async {
                        self.popViewController(animated: true)
                    }
                } else {
                    /// on cancel => do nothing
                }
            }
            /// return false => so navigator will cancel the popBack
            /// until user confirm or cancel
            return false
        }else{
            DispatchQueue.main.async {
                self.popViewController(animated: true)
            }
        }
        return true
    }
}

コントローラ上


extension MyController: NavigationControllerBackButtonDelegate {
    
    func shouldPopOnBackButtonPress(_ completion: @escaping (Bool) -> ()) {
    
        let msg = "message"
        
        /// show UIAlert
        alertAttention(msg: msg, actions: [
            
            .init(title: "Continuer", style: .destructive, handler: { _ in
                completion(true)
            }),
            .init(title: "Annuler", style: .cancel, handler: { _ in
                completion(false)
            })
            ])
   
    }

}

if viewControllers.count <navigationBar.items!.count {return true}で何が起こっているかについての詳細を提供できますか?
H4Hugo

//非常に多くのナビゲーションアイテムをポップするという同期の問題を回避します//異常なタップによるビューコントローラーの不足またはその逆
brahimm

2

これが簡単にできるとは思いません。これを回避するために私が信じる唯一の方法は、そこに配置するために独自の戻るボタンの矢印の画像を作成することです。最初はイライラしましたが、一貫性のために省略された理由がわかります。

通常のボタンを作成し、デフォルトの戻るボタンを非表示にすることで、矢印なしで閉じることができます。

self.navigationItem.leftBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:@"Servers" style:UIBarButtonItemStyleDone target:nil action:nil] autorelease];
self.navigationItem.hidesBackButton = YES;

2
ええ問題は、私がそれを通常の戻るボタンのように見せたいことです、それは私のカスタムアクションを最初に呼び出すためにそれを必要とするだけです...
Parrots

2

ただサブクラス化することにより、より簡単な方法がありますデリゲートメソッドのをUINavigationBarしてオーバーライドするShouldPopItem方法は


UINavigationControllerクラスをサブクラス化し、shouldPopItemメソッドを実装すると言っていると思います。それは私にとってうまくいきます。ただし、そのメソッドは、期待どおりに単にYESまたはNOを返すべきではありません。説明と解決策はここにあります:stackoverflow.com/a/7453933/462162
arlomedia

2

onegrayのソリューションは安全ではありません。Appleの公式ドキュメントhttps://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.htmlによると、これを回避する必要があります。

「カテゴリで宣言されたメソッドの名前が元のクラスのメソッドと同じか、同じクラス(またはスーパークラス)の別のカテゴリのメソッドと同じである場合、どのメソッドの実装が使用されるかについての動作は未定義です。独自のクラスでカテゴリを使用している場合、これは問題になる可能性は低いですが、カテゴリを使用して標準のCocoaまたはCocoa Touchクラスにメソッドを追加するときに問題が発生する可能性があります。


2

Swiftの使用:

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)
    if self.navigationController?.topViewController != self {
        print("back button tapped")
    }
}

iOS 10以降、おそらく以前のバージョンでは、これは機能していません。
Murray Sagal

2

これが発生する前にナビゲーションバーの戻るボタンイベントをキャッチするための@onewayのSwift 3バージョンの回答です。以下のようUINavigationBarDelegateに使用することはできませんUIViewController、あなたはときにトリガされますデリゲートを作成する必要がnavigationBar shouldPop呼び出されます。

@objc public protocol BackButtonDelegate {
      @objc optional func navigationShouldPopOnBackButton() -> Bool 
}

extension UINavigationController: UINavigationBarDelegate  {

    public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {

        if viewControllers.count < (navigationBar.items?.count)! {                
            return true
        }

        var shouldPop = true
        let vc = self.topViewController

        if vc.responds(to: #selector(vc.navigationShouldPopOnBackButton)) {
            shouldPop = vc.navigationShouldPopOnBackButton()
        }

        if shouldPop {
            DispatchQueue.main.async {
                self.popViewController(animated: true)
            }
        } else {
            for subView in navigationBar.subviews {
                if(0 < subView.alpha && subView.alpha < 1) {
                    UIView.animate(withDuration: 0.25, animations: {
                        subView.alpha = 1
                    })
                }
            }
        }

        return false
    }
}

次に、ビューコントローラーにデリゲート関数を追加します。

class BaseVC: UIViewController, BackButtonDelegate {
    func navigationShouldPopOnBackButton() -> Bool {
        if ... {
            return true
        } else {
            return false
        }        
    }
}

ユーザーが戻るかどうかを決定するためのアラートコントローラーを追加することがよくあることに気づきました。もしそうなら、あなたは常にできreturn falseにおけるnavigationShouldPopOnBackButton()役割と、このような何かを行うことによって、あなたのビューコントローラを閉じます。

func navigationShouldPopOnBackButton() -> Bool {
     let alert = UIAlertController(title: "Warning",
                                          message: "Do you want to quit?",
                                          preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "Yes", style: .default, handler: { UIAlertAction in self.yes()}))
            alert.addAction(UIAlertAction(title: "No", style: .cancel, handler: { UIAlertAction in self.no()}))
            present(alert, animated: true, completion: nil)
      return false
}

func yes() {
     print("yes")
     DispatchQueue.main.async {
            _ = self.navigationController?.popViewController(animated: true)
        }
}

func no() {
    print("no")       
}

エラーが発生Value of type 'UIViewController' has no member 'navigationShouldPopOnBackButton' します。コードをコンパイルしようとすると、次の行if vc.responds(to: #selector(v...self.topViewController返されます。また、オプションが返され、警告も表示されます。
Sankar

FWIW、私は作ることによってそのコードを修正しました:let vc = self.topViewController as! MyViewControllerそしてそれは今のところうまく機能しているようです。これが正しい変更であると思われる場合は、コードを編集できます。また、やってはいけないと感じたなら、その理由を教えていただければ幸いです。このコードをありがとう。この回答は投票によって埋葬されるため、おそらくこれについてブログ投稿を書く必要があります。
Sankar、

@SankarPエラーが発生した理由は、MyViewController準拠していない可能性があるためですBackButtonDelegate。アンラップを強制するのではなく、guard let vc = self.topViewController as? MyViewController else { return true }クラッシュの可能性を回避する必要があります。
Lawliet

ありがとう。ガードステートメントは次のようになるはずguard let vc = self.topViewController as? MyViewController else { self.popViewController(animated: true) return true }です。正しくキャストできない場合に備えて、画面が正しいページに移動していることを確認してください。navigationBarこのコードが存在するビューコントローラだけでなく、すべてのVCで関数が呼び出されることを理解しました。答えのコードも更新するとよいでしょうか?ありがとう。
Sankar、

2

Swift 4 iOS 11.3バージョン:

これは、https: //stackoverflow.com/a/34343418/4316579のkgaidisからの回答に基づいています。

拡張機能がいつ機能しなくなったかはわかりませんが、この記事の執筆時点(Swift 4)では、以下で説明するようにUINavigationBarDelegateへの準拠を宣言しない限り、拡張機能は実行されないようです。

これがなぜ拡張機能が機能しなくなったのか疑問に思っている人々に役立つことを願っています。

extension UINavigationController: UINavigationBarDelegate {
    public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {

    }
}

1

現在「nil」のままにしているターゲット変数とアクション変数を使用することで、ボタンが「選択された」ときに呼び出されるように、保存ダイアログをワイヤリングできるはずです。気をつけてください、これは奇妙な瞬間に引き起こされるかもしれません。

私は主にAmagrammerに同意しますが、矢印付きのボタンをカスタム化することはそれほど難しいことではないと思います。私は戻るボタンの名前を変更し、スクリーンショットを取り、必要なボタンのサイズをフォトショップで取り、それをボタンの上部にある画像にします。


私はあなたがphotoshopをすることができると同意します、そして私が本当にそれを望んでいるなら私はこれをするかもしれないと思いますが、今、私が望むようにこれを動作させるために外観を変更し、少し感じることに決めました。
John Ballinger、

はい、ただし、アクションがbackBarButtonItemにアタッチされている場合はトリガーされません。これがバグなのか機能なのかわかりません。Appleさえ知らない可能性があります。フォトショッピングの練習についても、Appleがこのアプリを正規のシンボルの誤用で拒否することに注意する必要があります。
Amagrammer、2009

ヘッズアップ:この回答は重複からマージされました。
Shog9 2013

1

NavigationBarsの右ボタンアイテムにアクセスして、そのセレクタープロパティを設定してみてください...参照UIBarButtonItem参照、これが機能しない場合のもう1つのことは、ナビゲーションバーの右ボタンアイテムをカスタムUIBarButtonItemに設定することです。セレクタを作成して設定します...これが役立つことを願っています


ヘッズアップ:この回答は重複からマージされました。
Shog9 2013

1

このようなユーザー入力を必要とするフォームの場合、ナビゲーションスタックの一部ではなく「モーダル」として呼び出すことをお勧めします。このようにして、フォームでのビジネスの面倒を見る必要があり、それを検証して、カスタムボタンを使用してフォームを閉じることができます。アプリの他の部分と同じに見えるが、より詳細に制御できるナビゲーションバーを設計することもできます。


ヘッズアップ:この回答は重複からマージされました。
Shog9 2013

1

戻るボタンをインターセプトするには、透明なUIControlでボタンを覆い、タッチをインターセプトします。

@interface MyViewController : UIViewController
{
    UIControl   *backCover;
    BOOL        inhibitBackButtonBOOL;
}
@end

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

    // Cover the back button (cannot do this in viewWillAppear -- too soon)
    if ( backCover == nil ) {
        backCover = [[UIControl alloc] initWithFrame:CGRectMake( 0, 0, 80, 44)];
#if TARGET_IPHONE_SIMULATOR
        // show the cover for testing
        backCover.backgroundColor = [UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:0.15];
#endif
        [backCover addTarget:self action:@selector(backCoverAction) forControlEvents:UIControlEventTouchDown];
        UINavigationBar *navBar = self.navigationController.navigationBar;
        [navBar addSubview:backCover];
    }
}

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

    [backCover removeFromSuperview];
    backCover = nil;
}

- (void)backCoverAction
{
    if ( inhibitBackButtonBOOL ) {
        NSLog(@"Back button aborted");
        // notify the user why...
    } else {
        [self.navigationController popViewControllerAnimated:YES]; // "Back"
    }
}
@end

ヘッズアップ:この回答は重複からマージされました。
Shog9 2013

1

少なくともXcode 5では、シンプルでかなり良い(完璧ではない)ソリューションがあります。IBで、[ユーティリティ]ペインの外にバーボタンアイテムをドラッグし、ナビゲーションバーの左側の[戻る]ボタンがある場所にドロップします。ラベルを「戻る」に設定します。IBActionに結び付けてviewControllerを閉じることができる機能ボタンがあります。私はいくつかの作業を行ってから、巻き戻しセグエをトリガーし、それは完全に機能します。

理想的でないのは、このボタンが<矢印を取得せず、以前のVCタイトルを引き継がないことですが、これは管理できると思います。私の目的のために、私は新しい[戻る]ボタンを「完了」ボタンに設定して、その目的を明確にしました。

また、IBナビゲーターには2つの戻るボタンが表示されますが、わかりやすくするためにラベルを付けるのは簡単です。

ここに画像の説明を入力してください


1

迅速

override func viewWillDisappear(animated: Bool) {
    let viewControllers = self.navigationController?.viewControllers!
    if indexOfArray(viewControllers!, searchObject: self) == nil {
        // do something
    }
    super.viewWillDisappear(animated)
}

func indexOfArray(array:[AnyObject], searchObject: AnyObject)-> Int? {
    for (index, value) in enumerate(array) {
        if value as UIViewController == searchObject as UIViewController {
            return index
        }
    }
    return nil
}

1

このアプローチは私にとってはうまくいきました(しかし、「戻る」ボタンには「<」記号がありません):

- (void)viewDidLoad
{
    [super viewDidLoad];

    UIBarButtonItem* backNavButton = [[UIBarButtonItem alloc] initWithTitle:@"Back"
                                                                      style:UIBarButtonItemStyleBordered
                                                                     target:self
                                                                     action:@selector(backButtonClicked)];
    self.navigationItem.leftBarButtonItem = backNavButton;
}

-(void)backButtonClicked
{
    // Do something...
    AppDelegate* delegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
    [delegate.navController popViewControllerAnimated:YES];
}

1

@onegrayの回答のSwiftバージョン

protocol RequestsNavigationPopVerification {
    var confirmationTitle: String { get }
    var confirmationMessage: String { get }
}

extension RequestsNavigationPopVerification where Self: UIViewController {
    var confirmationTitle: String {
        return "Go back?"
    }

    var confirmationMessage: String {
        return "Are you sure?"
    }
}

final class NavigationController: UINavigationController {

    func navigationBar(navigationBar: UINavigationBar, shouldPopItem item: UINavigationItem) -> Bool {

        guard let requestsPopConfirm = topViewController as? RequestsNavigationPopVerification else {
            popViewControllerAnimated(true)
            return true
        }

        let alertController = UIAlertController(title: requestsPopConfirm.confirmationTitle, message: requestsPopConfirm.confirmationMessage, preferredStyle: .Alert)

        alertController.addAction(UIAlertAction(title: "Cancel", style: .Cancel) { _ in
            dispatch_async(dispatch_get_main_queue(), {
                let dimmed = navigationBar.subviews.flatMap { $0.alpha < 1 ? $0 : nil }
                UIView.animateWithDuration(0.25) {
                    dimmed.forEach { $0.alpha = 1 }
                }
            })
            return
        })

        alertController.addAction(UIAlertAction(title: "Go back", style: .Default) { _ in
            dispatch_async(dispatch_get_main_queue(), {
                self.popViewControllerAnimated(true)
            })
        })

        presentViewController(alertController, animated: true, completion: nil)

        return false
    }
}

これで、すべてのコントローラーで、準拠するだけでRequestsNavigationPopVerification、この動作がデフォルトで採用されます。


1

使用する isMovingFromParentViewController

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(true)

    if self.isMovingFromParentViewController {
        // current viewController is removed from parent
        // do some work
    }
}

これが、[戻る]ボタンがタップされたことをどのように証明するかをさらに説明できますか?
Murray Sagal

これは簡単ですが、ロードする子ビューからそのビューに確実に戻る場合にのみ機能します。子がこのビューをスキップして親に戻ると、コードは呼び出されません(ビューは親から移動せずにすでに消えていました)。しかし、OPからの質問と同じように、[戻る]ボタンのトリガーでイベントを処理するだけの問題と同じです。したがって、これは彼の質問に対する簡単な答えです。
CMont 2017年

これは超シンプルでエレガントです。大好きです。ただ1つの問題:ユーザーが途中でキャンセルした場合でも、ユーザーがスワイプして戻った場合にもこれが発生します。おそらくより良い解決策は、このコードをに入れることですviewDidDisappear。このようにして、ビューが完全になくなったときにのみ起動します。
Phontaine Judd

1

@Williamからの回答は正しいですが、ユーザーがスワイプして戻るジェスチャーを開始した場合、viewWillDisappearメソッドが呼び出され、スワイプしてもselfナビゲーションスタックにself.navigationController.viewControllers含まれません(つまり、が含まれませんself)完了しておらず、View Controllerは実際にはポップされていません。したがって、解決策は次のとおりです。

  1. 次を使用して、スワイプして戻るジェスチャーを無効にviewDidAppearし、戻るボタンの使用のみを許可します。

    if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)])
    {
        self.navigationController.interactivePopGestureRecognizer.enabled = NO;
    }
  2. または、単にviewDidDisappear次のように代わりに使用します。

    - (void)viewDidDisappear:(BOOL)animated
    {
        [super viewDidDisappear:animated];
        if (![self.navigationController.viewControllers containsObject:self])
        {
            // back button was pressed or the the swipe-to-go-back gesture was
            // completed. We know this is true because self is no longer
            // in the navigation stack.
        }
    }

0

これまでに見つけた解決策はあまり良くありませんが、私にとってはうまくいきます。この答えを取って、私がプログラムでポップしているかどうかもチェックします:

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

  if ((self.isMovingFromParentViewController || self.isBeingDismissed)
      && !self.isPoppingProgrammatically) {
    // Do your stuff here
  }
}

プログラムでポップする前に、そのプロパティをコントローラーに追加してYESに設定する必要があります。

self.isPoppingProgrammatically = YES;
[self.navigationController popViewControllerAnimated:YES];

0

それを行う新しい方法が見つかりました:

Objective-C

- (void)didMoveToParentViewController:(UIViewController *)parent{
    if (parent == NULL) {
        NSLog(@"Back Pressed");
    }
}

迅速

override func didMoveToParentViewController(parent: UIViewController?) {
    if parent == nil {
        println("Back Pressed")
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.