- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if (self.isMovingFromParentViewController || self.isBeingDismissed) {
// Do your stuff here
}
}
// 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
}
}
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
}
}
}
-(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
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if ((self.isMovingFromParentViewController || self.isBeingDismissed)
&& !self.isPoppingProgrammatically) {
// Do your stuff here
}
}
- (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;
}
}
//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
}
}
}
17 回答
UPDATE: 根据一些评论,原始答案中的解决方案在iOS 8的某些情况下似乎不起作用 . 如果没有进一步的细节,我无法验证实际情况 .
对于那些在那种情况下的人来说,还有另一种选择 . 通过覆盖
willMove(toParentViewController:)
可以检测何时弹出视图控制器 . 基本思想是parent
是nil
时正在弹出视图控制器 .查看"Implementing a Container View Controller"了解更多详情 .
从iOS 5开始,我发现处理这种情况的最简单方法是使用新方法
- (BOOL)isMovingFromParentViewController
:当你在导航堆栈中推送和弹出控制器时,
- (BOOL)isMovingFromParentViewController
是有意义的 .但是,如果要呈现模态视图控制器,则应使用
- (BOOL)isBeingDismissed
代替:如this question中所述,您可以组合这两个属性:
其他解决方案依赖于
UINavigationBar
的存在 . 我更喜欢这种方法,因为它将触发事件的动作(即按下后退按钮)分离所需的任务 .当点按后退按钮时会调用
viewWillAppear()
和viewDidDisappear()
,而其他时候也会调用它们 . 有关详细信息,请参阅答案的结尾 .使用UIViewController.parent
在
willMoveToParentViewController(_:)
或didMoveToParentViewController()
的帮助下从VC的父节点(NavigationController)中删除VC时,最好检测后退按钮如果parent为nil,则视图控制器将从导航堆栈弹出并被解除 . 如果parent不是nil,则将其添加到堆栈并显示 .
将
willMove
换成didMove
并检查self.parent以在取消视图控制器后执行工作 .停止解雇
请注意,如果您需要进行某种异步保存,检查父级不允许您“暂停”转换 . 为此,您可以实现以下功能 . 唯一的缺点就是你失去了花哨的iOS风格/动画后退按钮 . 此处还要注意交互式滑动手势 . 使用以下方法处理此案例 .
更多关于视图的信息/确实会出现
如果你没有得到
viewWillAppear
viewDidDisappear
问题,让我们来看一个例子 . 假设您有三个视图控制器:ListVC:事物的表格视图
DetailVC:有关事物的详细信息
SettingsVC:某物的一些选项
让我们按照
detailVC
上的调用,从listVC
到settingsVC
再回到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
被多次调用,不仅在返回时,而且在向前时 . 对于可能需要的快速操作,但对于更复杂的操作(如网络调用保存),可能不会 .First Method
Second Method
我已经和这个问题玩了两天了 . IMO最好的方法就是创建扩展类和协议,如下所示:
这是有效的,因为每次弹出视图控制器时
UINavigationController
都会收到对navigationBar:shouldPopItem:
的调用 . 在那里我们检测是否按下了背部(任何其他按钮) . 您唯一需要做的就是在视图控制器中实现协议,在该控制器中按下后退 .如果一切正常,请记住在
backButtonPressedSel
内手动弹出视图控制器 .如果您已经将子类化为
UINavigationViewController
并实施了navigationBar:shouldPopItem:
,则不会干扰它 .您可能还有兴趣禁用后退手势 .
这适用于iOS 9.3.x with Swift:
与此处的其他解决方案不同,这似乎并未意外触发 .
为了记录,我认为这更像是他在寻找...
那些声称这不起作用的人是错误的:
这很好 . 那么是什么导致了广泛的神话呢?
问题似乎是由于实施不正确一种不同的方法,即执行
willMove(toParent:)
忘了调用super
.如果在不调用
super
的情况下实现willMove(toParent:)
,则self.isMovingFromParent
将为false
,并且viewWillDisappear
的使用将显示为失败 . 它没有失败;你打破了它 .正如
purrrminator
所说,elitalon
的答案并不完全正确,因为即使以编程方式弹出控制器,也会执行your stuff
.到目前为止我找到的解决方案并不是很好,但它对我有用 . 除了
elitalon
所说的,我还会检查我是否以编程方式弹出:您必须将该属性添加到控制器并在以编程方式弹出之前将其设置为YES:
谢谢你的帮助!
最好的方法是使用UINavigationController委托方法
使用它,您可以知道控制器正在显示UINavigationController .
7ynk3r的答案与我最后使用的答案非常接近,但它需要一些调整:
你应该看看UINavigationBarDelegate Protocol . 在这种情况下,您可能希望使用navigationBar:shouldPopItem:方法 .
正如Coli88所说,你应该检查UINavigationBarDelegate协议 .
在更一般的方式中,当当前可见视图控制器保留的视图即将消失时,您还可以使用
- (void)viewWillDisapear:(BOOL)animated
执行自定义工作 . 不幸的是,这将打扰推动和弹出案件 .对于带有UINavigationController的Swift:
我通过向左侧的navigationBar添加UIControl解决了这个问题 .
当视图消失时,您需要记住将其删除:
就这样!
您可以使用后退按钮回调,如下所示:
self.navigationController.isMovingFromParentViewController在iOS8和9上不再工作我使用:
(SWIFT)
finaly找到解决方案..我们正在寻找的方法是“willShowViewController”,这是UINavigationController的委托方法