回答:
更新:一部のコメントによると、元の回答のソリューションはiOS 8以降の特定のシナリオでは機能しないようです。これ以上の詳細がなければ、実際にそうであることを確認することはできません。
あなたのそれらのためにしかし、そのような状況では代替案があります。オーバーライドすることで、ビューコントローラがいつポップされているかを検出できますwillMove(toParentViewController:)
。基本的な考え方は、の場合、ビューコントローラーがポップされるということparent
ですnil
。
チェックアウト「コンテナビューコントローラの実装」詳細については。
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
}
}
で述べたように、この質問は、両方の特性を組み合わせることができます:
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if (self.isMovingFromParentViewController || self.isBeingDismissed) {
// Do your stuff here
}
}
他のソリューションは、の存在に依存していUINavigationBar
ます。代わりに、私のアプローチがより好きです。これは、実行に必要なタスクを、イベントをトリガーしたアクション(つまり、戻るボタンを押す)から切り離すためです。
self.isMovingFromParentViewController
プログラムを使用してナビゲーションスタックをポップしているとき、TRUEの値がありpopToRootViewControllerAnimated
ます。私はあなたの答えに反対票を投じるべきですか?(件名は「戻る」ボタンがnavbarで押されたと言います)
override func viewWillDisappear(animated: Bool) { super.viewWillDisappear(animated) if isMovingFromParentViewController(){ println("back button pressed") } }
-viewDidDisappear:
、それはあなたが買ってあげることが可能ですので、-viewWillDisappear:
することなく、-viewDidDisappear:
あなたはナビゲーションコントローラアイテムを却下し、そのスワイプをキャンセルするためにスワイプ起動したときのように(。
一方でviewWillAppear()
とviewDidDisappear()
されている戻るボタンをタップしたときに呼び出さ、それらはまた、他の時点で呼ばれています。詳細については、回答の最後を参照してください。
willMoveToParentViewController(_:)
ORを使用してVCがその親(NavigationController)から削除されている場合、戻るボタンの検出はより適切に行われます。didMoveToParentViewController()
親がnilの場合、ビューコントローラーはナビゲーションスタックからポップされ、閉じられます。親が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
}
}
ビューコントローラーが閉じられた後に動作するようにスワップアウト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
問題が解決しない場合は、例を見てみましょう。3つのView Controllerがあるとします。
上のコールを追跡することができますdetailVC
あなたがから行くようlistVC
にsettingsVC
と背中にlistVC
一覧>詳細(プッシュdetailVC)Detail.viewDidAppear
< -現れ
詳細>設定(プッシュsettingsVCを)Detail.viewDidDisappear
< -消えます
そして戻ると...
設定>詳細(ポップ設定 VC)<- 詳細をDetail.viewDidAppear
表示
>リスト(ポップ詳細 VC)Detail.viewDidDisappear
<-非表示
viewDidDisappear
戻るときだけでなく、進むときにも何度も呼び出されることに注意してください。必要な場合がある迅速な操作の場合は、保存するためのネットワーク呼び出しのようなより複雑な操作の場合は、そうでない場合があります。
didMoveToParantViewController:
ビューが表示されなくなったときに作業を行うユーザー。InteractiveGesutreでiOS7に役立つ
_ = self.navigationController?.popViewController(animated: true)
ため、[戻る]ボタンを押すだけでは呼び出されません。[ 戻る]が押されたときにのみ機能する通話を探しています。
最初の方法
- (void)didMoveToParentViewController:(UIViewController *)parent
{
if (![parent isEqual:self.parentViewController]) {
NSLog(@"Back pressed");
}
}
第二の方法
-(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];
}
これが機能しないと主張する人は間違っています:
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if self.isMovingFromParent {
print("we are being popped")
}
}
それはうまくいきます。それでは、それが広まっていない神話を広めているのは何ですか?
問題は別のメソッドの誤った実装、つまりのwillMove(toParent:)
呼び出しがを呼び出すのを忘れたことに起因するようsuper
です。
あなたが実装する場合はwillMove(toParent:)
呼び出さずsuper
、その後self.isMovingFromParent
になるfalse
との使用がviewWillDisappear
失敗しているように見えます。失敗しませんでした。あなたはそれを壊しました。
注:実際の問題は通常、最初のビューコントローラーがポップされたことを2番目のビューコントローラーが検出することです。こちらのより一般的な説明も参照してください:統一されたUIViewControllerが「最前線になった」検出?
編集コメントは、これがでviewDidDisappear
はなくであるべきだと示唆していviewWillDisappear
ます。
true
インタラクティブなスワイプポップジェスチャー(ビューコントローラーの左端から)に戻ります。スワイプで完全にポップしなかった場合でも同様です。そのwillDisappear
ため、でチェックインする代わりに、作業でチェックインしdidDisappear
ます。
私はこの問題で2日間遊んで(または戦って)います。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
これは、View ControllerがポップUINavigationController
さnavigationBar:shouldPopItem:
れるたびにへの呼び出しを受け取るため、機能します。そこで、戻るボタンが押されたかどうかが検出されます(他のボタン)。あなたがしなければならない唯一のことは、戻るが押されたビューコントローラにプロトコルを実装することです。
backButtonPressedSel
すべて問題がなければ、内部でビューコントローラを手動でポップすることを忘れないでください。
すでにサブクラス化UINavigationViewController
して実装navigationBar:shouldPopItem:
している場合でも、心配する必要はありません。
戻るジェスチャーを無効にすることもできます。
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
}
これは、Swiftを使用するiOS 9.3.xで私にとっては機能します。
override func didMoveToParentViewController(parent: UIViewController?) {
super.didMoveToParentViewController(parent)
if parent == self.navigationController?.parentViewController {
print("Back tapped")
}
}
ここの他のソリューションとは異なり、これは予期せずトリガーされるようには見えません。
記録として、これは彼が探していたもののほうが多いと思います…
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];
}
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];
ご協力いただきありがとうございます!
最良の方法は、UINavigationControllerデリゲートメソッドを使用することです
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
これを使用して、どのコントローラーがUINavigationControllerを表示しているかを知ることができます。
if ([viewController isKindOfClass:[HomeController class]]) {
NSLog(@"Show home controller");
}
この問題を解決するには、左側のナビゲーションバーに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];
}
}
それで全部です!
次のように、戻るボタンのコールバックを使用できます。
- (BOOL) navigationShouldPopOnBackButton
{
[self backAction];
return NO;
}
- (void) backAction {
// your code goes here
// show confirmation alert, for example
// ...
}
迅速なバージョンでは、グローバルスコープのようなものを行うことができます
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
}
}
その下で、戻るボタンのアクションを制御するビューコントローラに配置します。
override func navigationShouldPopOnBackButton() -> Bool {
self.backAction()//Your action you want to perform.
return true
}
navigationShouldPopOnBackButton
から来たの?パブリックAPIの一部ではありません。
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;
}
}
UINavigationBarDelegate Protocolを確認してください。この場合、navigationBar:shouldPopItem:メソッドを使用できます。
最終的に見つかったソリューション..私たちが探していたメソッドは、UINavigationControllerのデリゲートメソッドである "willShowViewController"です。
//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
}
}
}
MyViewController
することPushedController
です。