检测导航栏上按下"后退"按钮的时间

120 iphone xcode objective-c ios

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

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

eli*_*lon 303

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

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

有关更多详细信息,请查看"实现容器视图控制器".


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

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

  if (self.isMovingFromParentViewController) {
    // Do your stuff here
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

  if (self.isBeingDismissed) {
    // Do your stuff here
  }
}
Run Code Online (Sandbox Code Playgroud)

本问题所述,您可以将两个属性组合在一起:

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

  if (self.isMovingFromParentViewController || self.isBeingDismissed) {
    // Do your stuff here
  }
}
Run Code Online (Sandbox Code Playgroud)

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

  • 当我使用`popToRootViewControllerAnimated`以编程方式弹出导航堆栈时,`self.isMovingFromParentViewController`具有TRUE值 - 没有任何触摸后退按钮.我应该低估你的答案吗?(主题说"在导航栏上按下''后退'按钮") (3认同)
  • 看起来不再是一个可靠的解决方案.在我第一次使用它时工作(它是iOS 10).但现在我不小心发现它平静地停止工作(iOS 11).不得不切换到"willMove(toParentViewController)"解决方案. (3认同)
  • 非常好的回答,非常感谢你.在Swift我用过:`override func viewWillDisappear(animated:Bool){super.viewWillDisappear(animated)if isMovingFromParentViewController(){println("back button pressed")}}` (2认同)
  • 您应该只在 `-viewDidDisappear:` 中执行此操作,因为您可能会得到一个没有 `-viewDidDisappear:` 的 `-viewWillDisappear:`(就像当您开始滑动以关闭导航控制器项目然后取消该滑动时。 (2认同)

WCB*_*rne 91

虽然viewWillAppear()viewDidDisappear() 当返回按钮被窃听叫,它们也被称为在其他时间.有关详细信息,请参阅答案的结尾.

使用UIViewController.parent

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

如果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(toParent parent: UIViewController?) {
    super.willMove(toParent: parent)
    if parent == nil {
        // The back button was pressed or interactive gesture used
    }
}
Run Code Online (Sandbox Code Playgroud)

换出willMovedidMove检查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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


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

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

  1. ListVC:事物的表格视图
  2. DetailVC:有关事物的详细信息
  3. SettingsVC:一个东西的一些选项

让我们按照detailVC你的方式跟随listVC,settingsVC然后回到listVC

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

当我们返回...
设置>详细信息(pop settingsVC) Detail.viewDidAppear< - 出现
详细信息>列表(pop detailVC)Detail.viewDidDisappear< - 消失

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

  • 当弹出到父视图控制器时,parent参数为nil;当显示此方法的视图时,父参数为nil.只有在按下"返回"按钮时才能使用该事实,而不是在到达视图时执行操作.毕竟,那是原来的问题.:) (2认同)

Xar*_*mer 16

第一种方法

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

第二种方法

-(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];
}
Run Code Online (Sandbox Code Playgroud)


7yn*_*k3r 9

我已经和这个问题玩了两天了.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
Run Code Online (Sandbox Code Playgroud)

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

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

如果你已经有子类UINavigationViewController并且已经实现了,navigationBar:shouldPopItem:请不要担心,这不会干扰它.

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

if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
    self.navigationController.interactivePopGestureRecognizer.enabled = NO;
}
Run Code Online (Sandbox Code Playgroud)


小智 7

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

- (BOOL) navigationShouldPopOnBackButton
{
    [self backAction];
    return NO;
}

- (void) backAction {
    // your code goes here
    // show confirmation alert, for example
    // ...
}
Run Code Online (Sandbox Code Playgroud)

对于 swift 版本,您可以在全局范围内执行类似的操作

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

extension UINavigationController: UINavigationBarDelegate {
     public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
          return self.topViewController?.navigationShouldPopOnBackButton() ?? true
    }
}
Run Code Online (Sandbox Code Playgroud)

在下面一个视图控制器中放置要控制后退按钮操作的位置:

override func navigationShouldPopOnBackButton() -> Bool {
    self.backAction()//Your action you want to perform.

    return true
}
Run Code Online (Sandbox Code Playgroud)

  • 不知道为什么有人投了反对票。这似乎是迄今为止最好的答案。 (2认同)

Chr*_*lla 7

这适用于iOS 9.3.x with Swift:

override func didMoveToParentViewController(parent: UIViewController?) {
    super.didMoveToParentViewController(parent)

    if parent == self.navigationController?.parentViewController {
        print("Back tapped")
    }
}
Run Code Online (Sandbox Code Playgroud)

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


mat*_*att 7

那些声称这行不通的人被误认为:

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

很好 那么,是什么导致了这个事实却没有呢?

问题似乎是由于不同方法的错误实现导致的,即willMove(toParent:)忘记调用的实现super

如果您在willMove(toParent:)未调用的情况下实施super,那么self.isMovingFromParentwill将无法false使用viewWillDisappear。它没有失败;你把它弄坏了。

  • 此代码在点击后退按钮时执行,但如果以编程方式弹出 VC,也会执行此代码。 (3认同)
  • 不幸的是,对于交互式滑动弹出手势(从视图控制器的左边缘),即使滑动没有完全弹出它,这也会返回“true”。因此,不用在“willDisappear”中检查它,而是在“didDisappear”中检查它。 (3认同)
  • @biomiker 当然,但其他方法也是如此。噗噗就是噗噗。问题是当您没有以编程方式弹出时如何检测弹出。如果你以编程方式弹出,你已经_知道_你正在弹出,所以没有什么可检测的。 (2认同)
  • @badhanganesh 谢谢,编辑答案以包含该信息。 (2认同)

Col*_*i88 1

您应该查看UINavigationBarDelegate Protocol。在这种情况下,您可能需要使用 navigationBar:shouldPopItem: 方法。


归档时间:

查看次数:

120482 次

最近记录:

5 年,11 月 前