Проверка того, собирается ли UIViewController выходить из стека навигации?

Мне нужно знать, когда мой диспетчер представлений собирается выскочить из штабеля nav, чтобы я мог выполнить действие.

Я не могу использовать -viewWillDisappear, потому что он вызывается, когда контроллер просмотра перемещается за экран по какой-либо причине (например, если новый контроллер отображения нажат вверх).

Мне нужно знать, когда сам контроллер будет выгружаться.

Любые идеи были бы удивительными, спасибо заранее.

Ответ 1

Я не думаю, что для этого есть явное сообщение, но вы можете подклассифицировать UINavigationController и переопределить - popViewControllerAnimated (хотя я не пробовал это до себя).

В качестве альтернативы, если нет других ссылок на контроллер вида, можете ли вы добавить его в dealloc?

Ответ 2

Переопределите метод viewWillDisappear в представленном VC, затем проверьте флаг isMovingFromParentViewController в пределах переопределения и выполните определенную логику. В моем случае я скрываю панель инструментов навигационных контроллеров. Все еще требует, чтобы ваш представленный VC понял, что он был нажат, хотя и не идеально.

Ответ 3

Попробуйте переопределить willMoveToParentViewController: (вместо viewWillDisappear:) в вашем пользовательском подклассе UIViewController.

Вызывается непосредственно перед добавлением или удалением контроллера вида из контроллера представления контейнера.

- (void)willMoveToParentViewController:(UIViewController *)parent
{
    [super willMoveToParentViewController:parent];
    if (!parent) {
        // `self` is about to get popped.
    }
}

Ответ 4

К счастью, к тому времени, когда вызывается метод viewWillDisappear, viewController уже удален из стека, поэтому мы знаем, что viewController выталкивает, потому что его больше нет в self.navigationController.viewControllers.

Swift 4

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    if let nav = self.navigationController {
        let isPopping = !nav.viewControllers.contains(self)
        if isPopping {
            // popping off nav
        } else {
            // on nav, not popping off (pushing past, being presented over, etc.)
        }
    } else {
        // not on nav at all
    }
}

Оригинальный код

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    if ((self.navigationController) && 
        (![self.navigationController.viewControllers containsObject:self])) {
        NSLog(@"I've been popped!");
    }
}

Ответ 5

Это работает для меня.

- (void)viewDidDisappear:(BOOL)animated
{
    if (self.parentViewController == nil) {
        NSLog(@"viewDidDisappear doesn't have parent so it been popped");
        //release stuff here
    } else {
        NSLog(@"PersonViewController view just hidden");
    }
}

Ответ 6

Вы можете поймать его здесь.

- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {

    if (viewController == YourAboutToAppearController) {
            // do something
    }
}

Это будет срабатывать непосредственно перед отображением нового представления. Никто еще не двигался. Я использую все время, чтобы делать магию перед asinine NavigationController. Вы можете устанавливать заголовки и названия кнопок и делать что угодно.

Ответ 7

У меня та же проблема. Я попытался с viewDisDisappear, но у меня нет функции get called:( (не знаю, почему, может быть, потому, что все мои VC - UITableViewController). Предложение Alex работает нормально, но оно не работает, если ваш навигационный контроллер отображается на вкладке "Дополнительно". В этом случае все контроллеры ваших навигационных контроллеров имеют navigationController как UIMoreNavigationController, а не контроллер навигации, который вы подклассифицировали, поэтому вы не будете уведомлены навигатором, когда VC вот-вот выскочит.
Наконец, я решил проблему с категорией UINavigationController, просто переписываю - (UIViewController *) popViewControllerAnimated: (BOOL) анимированный

- (UIViewController *)popViewControllerAnimated:(BOOL)animated{
   NSLog(@"UINavigationController(Magic)");
   UIViewController *vc = self.topViewController;
   if ([vc respondsToSelector:@selector(viewControllerWillBePopped)]) {
      [vc performSelector:@selector(viewControllerWillBePopped)];
   }
   NSArray *vcs = self.viewControllers;
   UIViewController *vcc = [vcs objectAtIndex:[vcs count] - 2];
   [self popToViewController:vcc animated:YES];
   return vcc;}

Это хорошо работает для меня: D

Ответ 8

Я пробовал это:

- (void) viewWillDisappear:(BOOL)animated {
    // If we are disappearing because we were removed from navigation stack
    if (self.navigationController == nil) {
        // YOUR CODE HERE
    }

    [super viewWillDisappear:animated];
}

Идея состоит в том, что при вызове контроллера навигации viewController установлено значение nil. Поэтому, если представление должно исчезнуть, и у него больше есть navigationController, я пришел к выводу, что он был выбит. (может не работать в других сценариях).

Нельзя ручаться, что viewWillDisappear будет вызван при появлении, так как он не упоминается в документах. Я попробовал это, когда вид был сверху, и ниже верхнего вида - и он работал в обоих.

Удачи, Одед.

Ответ 9

Подкласс UINavigationController и переопределить popViewController:

Swift 3

protocol CanPreventPopProtocol {
    func shouldBePopped() -> Bool
}

class MyNavigationController: UINavigationController {
    override func popViewController(animated: Bool) -> UIViewController? {
        let viewController = self.topViewController

        if let canPreventPop = viewController as? CanPreventPopProtocol {
            if !canPreventPop.shouldBePopped() {
                return nil
            }
        }
        return super.popViewController(animated: animated)
    }

    //important to prevent UI thread from freezing
    //
    //if popViewController is called by gesture recognizer and prevented by returning nil
    //UI will freeze after calling super.popViewController
    //so that, in order to solve the problem we should not return nil from popViewController
    //we interrupt the call made by gesture recognizer to popViewController through
    //returning false on gestureRecognizerShouldBegin
    //
    //tested on iOS 9.3.2 not others
    func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        let viewController = self.topViewController

        if let canPreventPop = viewController as? CanPreventPopProtocol {
            if !canPreventPop.shouldBePopped() {
                return false
            }
        }

        return true
    }

}

Ответ 10

Вы можете использовать этот:

if(self.isMovingToParentViewController)
{
    NSLog(@"Pushed");
}
else
{
    NSLog(@"Popped");
}

Ответ 11

Возможно, вы могли бы использовать метод UINavigationBarDelegate navigationBar: shouldPopItem.

Ответ 12

Попробуйте сделать эту проверку в viewwilldisappear if ([self.navigationController.viewControllers indexOfObject: self] == NSNotFound) { // Появление этого представления произошло. }

Ответ 13

Мне также нужно было иногда не давать совать, поэтому лучший ответ для меня написал Орхан Алиханов. Но это не сработало, потому что делегат не был установлен, поэтому я сделал окончательный вариант:

import UIKit

class CustomActionsNavigationController: UINavigationController, 
                                         UIGestureRecognizerDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()
        interactivePopGestureRecognizer?.delegate = self
    }

    override func popViewController(animated: Bool) -> UIViewController? {
        if let delegate = topViewController as? CustomActionsNavigationControllerDelegate {
            guard delegate.shouldPop() else { return nil }
        }
        return super.popViewController(animated: animated)
    }

    // important to prevent UI thread from freezing
    //
    // if popViewController is called by gesture recognizer and prevented by returning nil
    // UI will freeze after calling super.popViewController
    // so that, in order to solve the problem we should not return nil from popViewController
    // we interrupt the call made by gesture recognizer to popViewController through
    // returning false on gestureRecognizerShouldBegin
    func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        if let delegate = topViewController as? CustomActionsNavigationControllerDelegate {
            if !delegate.shouldPop() {
                return false
            }
        }

        // This if statement prevents navigation controller to pop when there is only one view controller
        if viewControllers.count == 1 {
            return false
        }

        return true
    }
}

protocol CustomActionsNavigationControllerDelegate {
    func shouldPop() -> Bool
}

UPDATE

Я добавил случай viewControllers.count == 1, потому что, если в стеке один контроллер и пользователь делает жест, он замораживает пользовательский интерфейс вашего приложения.

Ответ 14

Вы можете наблюдать уведомление:

- (void)viewDidLoad{
    [super viewDidLoad];
    [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(navigationControllerWillShowViewController:) name:@"UINavigationControllerWillShowViewControllerNotification" object:nil];
}

- (void)navigationControllerDidShowViewController:(NSNotification *)notification{
    UIViewController *lastVisible = notification.userInfo[@"UINavigationControllerLastVisibleViewController"];
    if(lastVisible == self){
        // we are being popped
    }
}