我已经将一个视图推到了导航控制器上,当我按下back按钮时,它会自动转到上一个视图。在从堆栈中弹出视图之前,当按下back按钮时,我想做一些事情。哪一个是后退按钮回调函数?
发布于 2012-07-09 19:44:52
William Jockusch的answer用简单的技巧解决了这个问题。
-(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];
}
发布于 2013-05-30 20:13:20
在我看来最好的解决方案。
- (void)didMoveToParentViewController:(UIViewController *)parent
{
if (![parent isEqual:self.parentViewController]) {
NSLog(@"Back pressed");
}
}
但它只适用于iOS5+
发布于 2019-03-07 12:51:25
也许为时已晚,但我之前也想要同样的行为。我使用的解决方案在App Store上的一个应用程序上运行得很好。因为我还没有看到任何人使用类似的方法,所以我想在这里分享它。这种解决方案的缺点是它需要对UINavigationController
进行子类化。尽管使用Method Swizzling可能有助于避免这种情况,但我并没有走得那么远。
因此,默认的后退按钮实际上是由UINavigationBar
管理的。当用户点击back按钮时,UINavigationBar
通过调用navigationBar(_:shouldPop:)
询问其委托是否应该弹出顶部的UINavigationItem
。UINavigationController
实际上实现了这一点,但是它没有公开声明它采用了UINavigationBarDelegate
(为什么!?)。要截获此事件,请创建UINavigationController
的子类,声明其与UINavigationBarDelegate
的一致性并实现navigationBar(_:shouldPop:)
。如果顶部的项应该弹出,则返回true
。如果应该保留,则返回false
。
有两个问题。首先,您必须在某个时刻调用navigationBar(_:shouldPop:)
的UINavigationController
版本。但是UINavigationBarController
没有公开声明它符合UINavigationBarDelegate
,尝试调用它将导致编译时错误。我使用的解决方案是使用Objective-C运行时直接获取实现并调用它。如果有人有更好的解决方案,请告诉我。
另一个问题是,如果用户点击后退按钮,popViewController(animated:)
会先调用navigationBar(_:shouldPop:)
,然后再调用。如果通过调用popViewController(animated:)
弹出视图控制器,则顺序颠倒。在本例中,我使用一个布尔值来检测在navigationBar(_:shouldPop:)
之前是否调用了popViewController(animated:)
,这意味着用户已经点击了back按钮。
另外,我对UIViewController
做了一个扩展,让导航控制器询问视图控制器,如果用户点击back按钮,是否应该弹出导航控制器。视图控制器可以返回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(_:)
。如果您不实现此方法,则默认行为将是在用户点击back按钮时立即弹出视图控制器,就像平常一样。
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
}
}
你可以看看我的演示here。
https://stackoverflow.com/questions/5217992
复制相似问题