181

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

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

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

self.navigationItem.leftBarButtonItem = backButton;

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

4

29 に答える 29

363

プレスを検出したいView Controllerにこれを入れてみてください:

-(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];
}
于 2010-08-10T04:20:03.527 に答える
181

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
}

サンプル アプリをダウンロードします

于 2013-10-02T08:28:48.237 に答える
40

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

于 2009-11-28T13:43:25.860 に答える
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
    }
}

楽しい事実: テスト目的で、偽のボタンの適切なサイズを見つけるために、背景色を青に設定しました...そして、戻るボタンの後ろに表示されます! とにかく、元のボタンを対象とするタッチは引き続きキャッチします。

于 2011-07-06T23:46:33.380 に答える
5

直接行うことはできません。いくつかの代替手段があります。

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

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

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

于 2010-05-25T19:58:01.357 に答える
3

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

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

UIBarButtonItem *temporaryBarButtonItem = [[UIBarButtonItem alloc] init];   
temporaryBarButtonItem.title = @"Back";
self.navigationItem.backBarButtonItem = temporaryBarButtonItem;
[temporaryBarButtonItem release];
于 2010-07-23T01:36:58.270 に答える
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
}

戻るボタンを透明なボタンで覆うだけです ;)

于 2015-08-21T11:13:33.957 に答える
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
    }

}
于 2015-03-14T22:12:36.200 に答える
2

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

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

self.navigationItem.leftBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:@"Servers" style:UIBarButtonItemStyleDone target:nil action:nil] autorelease];
self.navigationItem.hidesBackButton = YES;
于 2009-07-31T23:08:23.683 に答える
2

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

- (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];
}
于 2015-05-01T21:18:56.020 に答える
2

スウィフトの使用:

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)
    if self.navigationController?.topViewController != self {
        print("back button tapped")
    }
}
于 2016-04-19T02:51:59.000 に答える
2

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

于 2013-08-27T18:49:29.137 に答える
2

スウィフト 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 {

    }
}
于 2018-04-30T18:37:47.280 に答える
2

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

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

于 2015-11-02T06:02:52.217 に答える
1

現在「nil」のままにしているターゲット変数とアクション変数を使用することで、ボタンが「選択」されたときに呼び出されるように保存ダイアログを接続できるはずです。注意してください、これは奇妙な瞬間にトリガーされる可能性があります。

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

于 2009-07-27T01:06:23.237 に答える
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
}
于 2015-02-28T17:22:36.223 に答える
1

@onegrayの回答の迅速なバージョン

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、この動作がデフォルトで採用されています。

于 2016-06-13T13:29:25.293 に答える
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.
        }
    }
    
于 2016-01-26T19:33:34.737 に答える
1

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

于 2012-11-08T15:40:10.283 に答える
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
于 2013-02-19T09:46:54.200 に答える
1

NavigationBars の右ボタン項目にアクセスして、そのセレクター プロパティを設定することができます...ここに参照UIBarButtonItem 参照があります。セレクターを作成して設定します...これが役立つことを願っています

于 2010-05-25T19:58:01.263 に答える
0

これまでに見つけた解決策はあまり良くありませんが、私にとってはうまくいきます。this answerを使用して、プログラムでポップしているかどうかも確認します。

- (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];
于 2015-05-11T11:07:42.930 に答える
0

それを行う新しい方法を見つけました:

Objective-C

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

迅速

override func didMoveToParentViewController(parent: UIViewController?) {
    if parent == nil {
        println("Back Pressed")
    }
}
于 2015-07-14T16:52:47.073 に答える