首页 文章

检测导航栏上按下'back'按钮的时间

提问于
浏览
113

我需要在导航栏上按下后退按钮(返回上一屏幕,返回父视图)按钮时执行一些操作 .

是否有一些方法可以实现捕获事件并触发一些操作以在屏幕消失之前暂停和保存数据?

17 回答

  • 1

    UPDATE: 根据一些评论,原始答案中的解决方案在iOS 8的某些情况下似乎不起作用 . 如果没有进一步的细节,我无法验证实际情况 .

    对于那些在那种情况下的人来说,还有另一种选择 . 通过覆盖 willMove(toParentViewController:) 可以检测何时弹出视图控制器 . 基本思想是 parentnil 时正在弹出视图控制器 .

    查看"Implementing a Container View Controller"了解更多详情 .


    从iOS 5开始,我发现处理这种情况的最简单方法是使用新方法 - (BOOL)isMovingFromParentViewController

    - (void)viewWillDisappear:(BOOL)animated {
      [super viewWillDisappear:animated];
    
      if (self.isMovingFromParentViewController) {
        // Do your stuff here
      }
    }
    

    当你在导航堆栈中推送和弹出控制器时, - (BOOL)isMovingFromParentViewController 是有意义的 .

    但是,如果要呈现模态视图控制器,则应使用 - (BOOL)isBeingDismissed 代替:

    - (void)viewWillDisappear:(BOOL)animated {
      [super viewWillDisappear:animated];
    
      if (self.isBeingDismissed) {
        // Do your stuff here
      }
    }
    

    this question中所述,您可以组合这两个属性:

    - (void)viewWillDisappear:(BOOL)animated {
      [super viewWillDisappear:animated];
    
      if (self.isMovingFromParentViewController || self.isBeingDismissed) {
        // Do your stuff here
      }
    }
    

    其他解决方案依赖于 UINavigationBar 的存在 . 我更喜欢这种方法,因为它将触发事件的动作(即按下后退按钮)分离所需的任务 .

  • 0

    当点按后退按钮时会调用 viewWillAppear()viewDidDisappear() ,而其他时候也会调用它们 . 有关详细信息,请参阅答案的结尾 .

    使用UIViewController.parent

    willMoveToParentViewController(_:)didMoveToParentViewController() 的帮助下从VC的父节点(NavigationController)中删除VC时,最好检测后退按钮

    如果parent为nil,则视图控制器将从导航堆栈弹出并被解除 . 如果parent不是nil,则将其添加到堆栈并显示 .

    // Objective-C
    -(void)willMoveToParentViewController:(UIViewController *)parent {
         [super willMoveToParentViewController:parent];
        if (!parent){
           // The back button was pressed or interactive gesture used
        }
    }
    
    
    // Swift
    override func willMove(toParentViewController parent: UIViewController?) {
        super.willMove(toParentViewController:parent)
        if parent == nil {
            // The back button was pressed or interactive gesture used
        }
    }
    

    willMove 换成 didMove 并检查self.parent以在取消视图控制器后执行工作 .

    停止解雇

    请注意,如果您需要进行某种异步保存,检查父级不允许您“暂停”转换 . 为此,您可以实现以下功能 . 唯一的缺点就是你失去了花哨的iOS风格/动画后退按钮 . 此处还要注意交互式滑动手势 . 使用以下方法处理此案例 .

    var backButton : UIBarButtonItem!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
         // Disable the swipe to make sure you get your chance to save
         self.navigationController?.interactivePopGestureRecognizer.enabled = false
    
         // Replace the default back button
        self.navigationItem.setHidesBackButton(true, animated: false)
        self.backButton = UIBarButtonItem(title: "Back", style: UIBarButtonItemStyle.Plain, target: self, action: "goBack")
        self.navigationItem.leftBarButtonItem = backButton
    }
    
    // Then handle the button selection
    func goBack() {
        // Here we just remove the back button, you could also disabled it or better yet show an activityIndicator
        self.navigationItem.leftBarButtonItem = nil
        someData.saveInBackground { (success, error) -> Void in
            if success {
                self.navigationController?.popViewControllerAnimated(true)
                // Don't forget to re-enable the interactive gesture
                self.navigationController?.interactivePopGestureRecognizer.enabled = true
            }
            else {
                self.navigationItem.leftBarButtonItem = self.backButton
                // Handle the error
            }
        }
    }
    

    更多关于视图的信息/确实会出现

    如果你没有得到 viewWillAppear viewDidDisappear 问题,让我们来看一个例子 . 假设您有三个视图控制器:

    • ListVC:事物的表格视图

    • DetailVC:有关事物的详细信息

    • SettingsVC:某物的一些选项

    让我们按照 detailVC 上的调用,从 listVCsettingsVC 再回到 listVC

    List > Detail (push detailVC) Detail.viewDidAppear < - 出现
    Detail > Settings (push settingsVC) Detail.viewDidDisappear < - 消失

    当我们回去......
    Settings > Detail (pop settingsVC) Detail.viewDidAppear < - 出现
    Detail > List (pop detailVC) Detail.viewDidDisappear < - 消失

    请注意, viewDidDisappear 被多次调用,不仅在返回时,而且在向前时 . 对于可能需要的快速操作,但对于更复杂的操作(如网络调用保存),可能不会 .

  • 0

    First Method

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

    Second Method

    -(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

    我已经和这个问题玩了两天了 . IMO最好的方法就是创建扩展类和协议,如下所示:

    @protocol UINavigationControllerBackButtonDelegate <NSObject>
    /**
     * Indicates that the back button was pressed.
     * If this message is implemented the pop logic must be manually handled.
     */
    - (void)backButtonPressed;
    @end
    
    @interface UINavigationController(BackButtonHandler)
    @end
    
    @implementation UINavigationController(BackButtonHandler)
    - (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
    {
        UIViewController *topViewController = self.topViewController;
        BOOL wasBackButtonClicked = topViewController.navigationItem == item;
        SEL backButtonPressedSel = @selector(backButtonPressed);
        if (wasBackButtonClicked && [topViewController respondsToSelector:backButtonPressedSel]) {
            [topViewController performSelector:backButtonPressedSel];
            return NO;
        }
        else {
            [self popViewControllerAnimated:YES];
            return YES;
        }
    }
    @end
    

    这是有效的,因为每次弹出视图控制器时 UINavigationController 都会收到对 navigationBar:shouldPopItem: 的调用 . 在那里我们检测是否按下了背部(任何其他按钮) . 您唯一需要做的就是在视图控制器中实现协议,在该控制器中按下后退 .

    如果一切正常,请记住在 backButtonPressedSel 内手动弹出视图控制器 .

    如果您已经将子类化为 UINavigationViewController 并实施了 navigationBar:shouldPopItem: ,则不会干扰它 .

    您可能还有兴趣禁用后退手势 .

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

    这适用于iOS 9.3.x with Swift:

    override func didMoveToParentViewController(parent: UIViewController?) {
        super.didMoveToParentViewController(parent)
    
        if parent == self.navigationController?.parentViewController {
            print("Back tapped")
        }
    }
    

    与此处的其他解决方案不同,这似乎并未意外触发 .

  • 0

    为了记录,我认为这更像是他在寻找...

    UIBarButtonItem *l_backButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRewind target:self action:@selector(backToRootView:)];
    
        self.navigationItem.leftBarButtonItem = l_backButton;
    
    
        - (void) backToRootView:(id)sender {
    
            // Perform some custom code
    
            [self.navigationController popToRootViewControllerAnimated:YES];
        }
    
  • 6

    那些声称这不起作用的人是错误的:

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        if self.isMovingFromParent {
            print("we are being popped")
        }
    }
    

    这很好 . 那么是什么导致了广泛的神话呢?

    问题似乎是由于实施不正确一种不同的方法,即执行 willMove(toParent:) 忘了调用 super .

    如果在不调用 super 的情况下实现 willMove(toParent:) ,则 self.isMovingFromParent 将为 false ,并且 viewWillDisappear 的使用将显示为失败 . 它没有失败;你打破了它 .

  • -2

    正如 purrrminator 所说, elitalon 的答案并不完全正确,因为即使以编程方式弹出控制器,也会执行 your stuff .

    到目前为止我找到的解决方案并不是很好,但它对我有用 . 除了 elitalon 所说的,我还会检查我是否以编程方式弹出:

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

    谢谢你的帮助!

  • 88

    最好的方法是使用UINavigationController委托方法

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

    使用它,您可以知道控制器正在显示UINavigationController .

    if ([viewController isKindOfClass:[HomeController class]]) {
        NSLog(@"Show home controller");
    }
    
  • 3

    7ynk3r的答案与我最后使用的答案非常接近,但它需要一些调整:

    - (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item {
    
        UIViewController *topViewController = self.topViewController;
        BOOL wasBackButtonClicked = topViewController.navigationItem == item;
    
        if (wasBackButtonClicked) {
            if ([topViewController respondsToSelector:@selector(navBackButtonPressed)]) {
                // if user did press back on the view controller where you handle the navBackButtonPressed
                [topViewController performSelector:@selector(navBackButtonPressed)];
                return NO;
            } else {
                // if user did press back but you are not on the view controller that can handle the navBackButtonPressed
                [self popViewControllerAnimated:YES];
                return YES;
            }
        } else {
            // when you call popViewController programmatically you do not want to pop it twice
            return YES;
        }
    }
    
  • 0

    你应该看看UINavigationBarDelegate Protocol . 在这种情况下,您可能希望使用navigationBar:shouldPopItem:方法 .

  • 2

    正如Coli88所说,你应该检查UINavigationBarDelegate协议 .

    在更一般的方式中,当当前可见视图控制器保留的视图即将消失时,您还可以使用 - (void)viewWillDisapear:(BOOL)animated 执行自定义工作 . 不幸的是,这将打扰推动和弹出案件 .

  • 15

    对于带有UINavigationController的Swift:

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

    我通过向左侧的navigationBar添加UIControl解决了这个问题 .

    UIControl *leftBarItemControl = [[UIControl alloc] initWithFrame:CGRectMake(0, 0, 90, 44)];
    [leftBarItemControl addTarget:self action:@selector(onLeftItemClick:) forControlEvents:UIControlEventTouchUpInside];
    self.leftItemControl = leftBarItemControl;
    [self.navigationController.navigationBar addSubview:leftBarItemControl];
    [self.navigationController.navigationBar bringSubviewToFront:leftBarItemControl];
    

    当视图消失时,您需要记住将其删除:

    - (void) viewWillDisappear:(BOOL)animated
    {
        [super viewWillDisappear:animated];
        if (self.leftItemControl) {
            [self.leftItemControl removeFromSuperview];
        }    
    }
    

    就这样!

  • 285

    您可以使用后退按钮回调,如下所示:

    - (BOOL) navigationShouldPopOnBackButton
    {
        [self backAction];
        return NO;
    }
    
    - (void) backAction {
        // your code goes here
        // show confirmation alert, for example
        // ...
    }
    
  • -1

    self.navigationController.isMovingFromParentViewController在iOS8和9上不再工作我使用:

    -(void) viewWillDisappear:(BOOL)animated
    {
        [super viewWillDisappear:animated];
        if (self.navigationController.topViewController != self)
        {
            // Is Popping
        }
    }
    
  • 8

    (SWIFT)

    finaly找到解决方案..我们正在寻找的方法是“willShowViewController”,这是UINavigationController的委托方法

    //IMPORT UINavigationControllerDelegate !!
    class PushedController: UIViewController, UINavigationControllerDelegate {
    
        override func viewDidLoad() {
            //set delegate to current class (self)
            navigationController?.delegate = self
        }
    
        func navigationController(navigationController: UINavigationController, willShowViewController viewController: UIViewController, animated: Bool) {
            //MyViewController shoud be the name of your parent Class
            if var myViewController = viewController as? MyViewController {
                //YOUR STUFF
            }
        }
    }
    

相关问题