簡體   English   中英

iOS中的navigationController中的后退按鈕回調

[英]back button callback in navigationController in iOS

我已將視圖推到導航控制器上,當我按下后退按鈕時,它會自動轉到上一個視圖。 我想在按下后退按鈕之前做一些事情,然后再將視圖彈出堆棧。 哪個是后退按鈕回調功能?

William Jockusch的回答用簡單的技巧解決了這個問題。

-(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];
}

在我看來,最好的解決方案。

- (void)didMoveToParentViewController:(UIViewController *)parent
{
    if (![parent isEqual:self.parentViewController]) {
         NSLog(@"Back pressed");
    }
}

但它只適用於iOS5 +

覆蓋后退按鈕可能更好,這樣您就可以彈出視圖之前處理事件例如用戶確認。

在viewDidLoad中創建一個UIBarButtonItem並將self.navigationItem.leftBarButtonItem設置為傳遞給它的sel

- (void) viewDidLoad
{
// change the back button to cancel and add an event handler
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:@”back”
style:UIBarButtonItemStyleBordered
target:self
action:@selector(handleBack:)];

self.navigationItem.leftBarButtonItem = backButton;
[backButton release];

}
- (void) handleBack:(id)sender
{
// pop to root view controller
[self.navigationController popToRootViewControllerAnimated:YES];

}

然后你可以做一些事情,如提升UIAlertView來確認動作,然后彈出視圖控制器等。

或者,不是創建新的后退按鈕,而是可以符合UINavigationController委托方法,以便在按下后退按鈕時執行操作。

我最終得到了這個解決方案。 當我們點擊后退按鈕viewDidDisappear方法調用時。 我們可以通過調用返回true的isMovingFromParentViewController選擇器來檢查。 我們可以傳回數據(使用Delegate).hope這可以幫助某人。

-(void)viewDidDisappear:(BOOL)animated{

    if (self.isMovingToParentViewController) {

    }
    if (self.isMovingFromParentViewController) {
       //moving back
        //pass to viewCollection delegate and update UI
        [self.delegateObject passBackSavedData:self.dataModel];

    }
}

這是檢測此問題的正確方法。

- (void)willMoveToParentViewController:(UIViewController *)parent{
    if (parent == nil){
        //do stuff

    }
}

在推送視圖時也會調用此方法。 所以檢查parent == nil是用於從堆棧彈出視圖控制器

對於“BEFORE彈出堆棧視圖”:

- (void)willMoveToParentViewController:(UIViewController *)parent{
    if (parent == nil){
        NSLog(@"do whatever you want here");
    }
}

有一種比詢問viewControllers更合適的方法。 您可以使控制器成為具有后退按鈕的navigationBar的委托。 這是一個例子。 在您要處理按下后退按鈕的控制器的實現中,告訴它它將實現UINavigationBarDelegate協議:

@interface MyViewController () <UINavigationBarDelegate>

然后在初始化代碼中的某個地方(可能在viewDidLoad中)使您的控制器成為其導航欄的委托:

self.navigationController.navigationBar.delegate = self;

最后,實現shouldPopItem方法。 按下后退按鈕時會調用此方法。 如果堆棧中有多個控制器或導航項,您可能想要檢查哪些導航項被彈出(item參數),以便您只在預期時執行自定義操作。 這是一個例子:

-(BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
{
    NSLog(@"Back button got pressed!");
    //if you return NO, the back button press is cancelled
    return YES;
}

如果您不能使用“viewWillDisappear”或類似的方法,請嘗試子類UINavigationController。 這是標題類:

#import <Foundation/Foundation.h>
@class MyViewController;

@interface CCNavigationController : UINavigationController

@property (nonatomic, strong) MyViewController *viewController;

@end

實施班:

#import "CCNavigationController.h"
#import "MyViewController.h"

@implementation CCNavigationController {

}
- (UIViewController *)popViewControllerAnimated:(BOOL)animated {
    @"This is the moment for you to do whatever you want"
    [self.viewController doCustomMethod];
    return [super popViewControllerAnimated:animated];
}

@end

另一方面,您需要將此viewController鏈接到您的自定義NavigationController,因此,在常規viewController的viewDidLoad方法中執行以下操作:

@implementation MyViewController {
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        ((CCNavigationController*)self.navigationController).viewController = self;
    }
}

這是我實現的另一種方式(沒有使用unwind segue測試它,但它可能不會像其他人在本頁中的其他解決方案中所說的那樣區分)讓父視圖控制器在子VC推送之前執行操作從視圖堆棧中彈出(我從原始的UINavigationController中使用了幾個級別)。 這也可以用於在推送childVC之前執行操作。 這具有使用iOS系統后退按鈕的附加優勢,而不必創建自定義UIBarButtonItem或UIButton。

  1. 讓您的父VC采用UINavigationControllerDelegate協議並注冊委托消息:

     MyParentViewController : UIViewController <UINavigationControllerDelegate> -(void)viewDidLoad { self.navigationcontroller.delegate = self; } 
  2. MyParentViewController實現此UINavigationControllerDelegate實例方法:

     - (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC { // Test if operation is a pop; can also test for a push (ie, do something before the ChildVC is pushed if (operation == UINavigationControllerOperationPop) { // Make sure it's the child class you're looking for if ([fromVC isKindOfClass:[ChildViewController class]]) { // Can handle logic here or send to another method; can also access all properties of child VC at this time return [self didPressBackButtonOnChildViewControllerVC:fromVC]; } } // If you don't want to specify a nav controller transition return nil; } 
  3. 如果在上面的UINavigationControllerDelegate實例方法中指定了特定的回調函數

     -(id <UIViewControllerAnimatedTransitioning>)didPressBackButtonOnAddSearchRegionsVC:(UIViewController *)fromVC { ChildViewController *childVC = ChildViewController.new; childVC = (ChildViewController *)fromVC; // childVC.propertiesIWantToAccess go here // If you don't want to specify a nav controller transition return nil; 

    }

也許這有點太晚了,但我之前也想要同樣的行為。 我使用的解決方案在App Store上的一個應用程序中運行良好。 由於我沒有看到任何人使用類似的方法,我想在這里分享。 這個解決方案的缺點是它需要子類化UINavigationController 雖然使用Method Swizzling可能有助於避免這種情況,但我沒有那么做。

因此,默認后退按鈕實際上由UINavigationBar管理。 當用戶點擊后退按鈕時, UINavigationBar詢問其委托是否應該通過調用navigationBar(_:shouldPop:)來彈出頂部UINavigationItem UINavigationController實際上實現了這一點,但它沒有公開聲明它采用UINavigationBarDelegate (為什么!?)。 要攔截此事件,請創建UINavigationController的子類,聲明其與UINavigationBarDelegate一致性並實現navigationBar(_:shouldPop:) 如果應彈出頂部項,則返回true 如果它應該停留,則返回false

有兩個問題。 首先,您必須在某個時刻調用navigationBar(_:shouldPop:)UINavigationController版本。 但是UINavigationBarController沒有公開聲明它與UINavigationBarDelegate一致性,試圖調用它將導致編譯時錯誤。 我使用的解決方案是使用Objective-C運行時直接獲取實現並調用它。 如果有人有更好的解決方案,請告訴我。

另一個問題是如果用戶點擊后退按鈕,則首先調用navigationBar(_:shouldPop:) ,然后調用popViewController(animated:) 如果通過調用popViewController(animated:)彈出視圖控制器,則順序會反轉。 在這種情況下,我使用布爾值來檢測是否在navigationBar(_:shouldPop:)之前調用了popViewController(animated:) navigationBar(_:shouldPop:) ,這意味着用戶已經點擊了后退按鈕。

此外,我對UIViewController進行了擴展,讓導航控制器詢問視圖控制器是否應該彈出,如果用戶點擊后退按鈕。 視圖控制器可以返回false並執行任何必要的操作,稍后再調用popViewController(animated:)

class InterceptableNavigationController: UINavigationController, UINavigationBarDelegate {
    // If a view controller is popped by tapping on the back button, `navigationBar(_:, shouldPop:)` is called first follows by `popViewController(animated:)`.
    // If it is popped by calling to `popViewController(animated:)`, the order reverses and we need this flag to check that.
    private var didCallPopViewController = false

    override func popViewController(animated: Bool) -> UIViewController? {
        didCallPopViewController = true
        return super.popViewController(animated: animated)
    }

    func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
        // If this is a subsequence call after `popViewController(animated:)`, we should just pop the view controller right away.
        if didCallPopViewController {
            return originalImplementationOfNavigationBar(navigationBar, shouldPop: item)
        }

        // The following code is called only when the user taps on the back button.

        guard let vc = topViewController, item == vc.navigationItem else {
            return false
        }

        if vc.shouldBePopped(self) {
            return originalImplementationOfNavigationBar(navigationBar, shouldPop: item)
        } else {
            return false
        }
    }

    func navigationBar(_ navigationBar: UINavigationBar, didPop item: UINavigationItem) {
        didCallPopViewController = false
    }

    /// Since `UINavigationController` doesn't publicly declare its conformance to `UINavigationBarDelegate`,
    /// trying to called `navigationBar(_:shouldPop:)` will result in a compile error.
    /// So, we'll have to use Objective-C runtime to directly get super's implementation of `navigationBar(_:shouldPop:)` and call it.
    private func originalImplementationOfNavigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
        let sel = #selector(UINavigationBarDelegate.navigationBar(_:shouldPop:))
        let imp = class_getMethodImplementation(class_getSuperclass(InterceptableNavigationController.self), sel)
        typealias ShouldPopFunction = @convention(c) (AnyObject, Selector, UINavigationBar, UINavigationItem) -> Bool
        let shouldPop = unsafeBitCast(imp, to: ShouldPopFunction.self)
        return shouldPop(self, sel, navigationBar, item)
    }
}

extension UIViewController {
    @objc func shouldBePopped(_ navigationController: UINavigationController) -> Bool {
        return true
    }
}

在您查看控制器時,實現shouldBePopped(_:) 如果您沒有實現此方法,則默認行為是在用戶點擊后退按鈕時立即彈出視圖控制器。

class MyViewController: UIViewController {
    override func shouldBePopped(_ navigationController: UINavigationController) -> Bool {
        let alert = UIAlertController(title: "Do you want to go back?",
                                      message: "Do you really want to go back? Tap on \"Yes\" to go back. Tap on \"No\" to stay on this screen.",
                                      preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "No", style: .cancel, handler: nil))
        alert.addAction(UIAlertAction(title: "Yes", style: .default, handler: { _ in
            navigationController.popViewController(animated: true)
        }))
        present(alert, animated: true, completion: nil)
        return false
    }
}

你可以在這里查看我的演示。

在此輸入圖像描述

這是它在Swift中對我有用的東西:

override func viewWillDisappear(_ animated: Bool) {
    if self.navigationController?.viewControllers.index(of: self) == nil {
        // back button pressed or back gesture performed
    }

    super.viewWillDisappear(animated)
}

如果您正在使用故事板並且您來自推送segue,您也可以覆蓋shouldPerformSegueWithIdentifier:sender: .

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM