我已经将一个视图推入导航控制器,当我按下返回按钮时,它会自动回到上一个视图。在弹出视图之前,我想在返回按钮被按下时执行一些操作。哪个是返回按钮的回调函数?
我已经将一个视图推入导航控制器,当我按下返回按钮时,它会自动回到上一个视图。在弹出视图之前,我想在返回按钮被按下时执行一些操作。哪个是返回按钮的回调函数?
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设置为这个UIBarButtonItem,传入一个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委托方法,在按下返回按钮时执行动作,而无需创建新的返回按钮。
UINavigationControllerDelegate
没有在返回按钮被点击时调用的方法。 - meaning-matters UINavigationController
。虽然使用Method Swizzling可能有助于避免这种情况,但我没有走得那么远。 UINavigationBar
管理的。当用户点击返回按钮时, UINavigationBar
通过调用 navigationBar( _:shouldPop :)
询问其委托是否应弹出顶部 UINavigationItem
。 UINavigationController
实际上实现了这一点,但它没有公开声明它采用了 UINavigationBarDelegate
(为什么!?)。为拦截此事件,请创建 UINavigationController
的子类,声明其符合 UINavigationBarDelegate
并实现 navigationBar(_:shouldPop :)
。如果应弹出顶部项,则返回 true
。如果应保留,则返回 false
。UINavigationController
版本的navigationBar(_:shouldPop:)
。但是UINavigationBarController
没有公开声明其符合UINavigationBarDelegate
,尝试调用它会导致编译时错误。我采用的解决方案是使用Objective-C运行时直接获取实现并调用它。如果有更好的解决方案,请告诉我。navigationBar(_:shouldPop:)
,然后调用popViewController(animated:)
。如果通过调用popViewController(animated:)
弹出视图控制器,则顺序相反。在这种情况下,我使用布尔值来检测是否在调用navigationBar(_:shouldPop:)
之前调用了popViewController(animated:)
,这意味着用户已经点击了返回按钮。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
}
}
这是检测此问题的正确方法。
- (void)willMoveToParentViewController:(UIViewController *)parent{
if (parent == nil){
//do stuff
}
}
当视图被推入时,会调用这种方法。因此检查parent==nil是为了从堆栈中弹出视图控制器。
-(void)viewDidDisappear:(BOOL)animated{
if (self.isMovingToParentViewController) {
}
if (self.isMovingFromParentViewController) {
//moving back
//pass to viewCollection delegate and update UI
[self.delegateObject passBackSavedData:self.dataModel];
}
}
[super viewDidDisappear:animated]
。 - SamB对于“从堆栈中弹出视图之前”:
- (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;
}
这是我另一种实现方式(没有测试过取消 segue,但它可能不会有区别,因为其他人已经在本页上述解决方案中提到),以使父视图控制器在推送的子 VC 从视图堆栈中弹出之前执行操作(我在原始 UINavigationController 的几个级别下使用了此方法)。这也可以用于在推送 childVC 之前执行操作。这样做的附加优势是与 iOS 系统返回按钮配合使用,而无需创建自定义 UIBarButtonItem 或 UIButton。
Have your parent VC adopt the UINavigationControllerDelegate
protocol and register for delegate messages:
MyParentViewController : UIViewController <UINavigationControllerDelegate>
-(void)viewDidLoad {
self.navigationcontroller.delegate = self;
}
Implement this UINavigationControllerDelegate
instance method in MyParentViewController
:
- (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 (i.e., 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;
}
If you specify a specific callback function in the above UINavigationControllerDelegate
instance method
-(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;
}
如果无法使用“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
@implementation MyViewController {
- (void)viewDidLoad
{
[super viewDidLoad];
((CCNavigationController*)self.navigationController).viewController = self;
}
}