viewWillDisappear: Визначте, чи з’являється контролер подання чи показує контролер підвиду


134

Я намагаюся знайти хороше рішення цієї проблеми. У -viewWillDisappear:методі контролера перегляду мені потрібно знайти спосіб визначити, чи це тому, що контролер перегляду висувається на стек контролера навігації, чи це тому, що контролер перегляду зникає через те, що він вискочив

На даний момент я встановлюю такі прапори, як, isShowingChildViewControllerале це стає досить складним. Єдиний спосіб, на який я можу це виявити, - це -deallocметод.

Відповіді:


228

Можна скористатися наступним.

- (void)viewWillDisappear:(BOOL)animated {
  [super viewWillDisappear:animated];
  NSArray *viewControllers = self.navigationController.viewControllers;
  if (viewControllers.count > 1 && [viewControllers objectAtIndex:viewControllers.count-2] == self) {
    // View is disappearing because a new view controller was pushed onto the stack
    NSLog(@"New view controller was pushed");
  } else if ([viewControllers indexOfObject:self] == NSNotFound) {
    // View is disappearing because it was popped from the stack
    NSLog(@"View controller was popped");
  }
}

Це, звичайно, можливо, тому що стек контролера перегляду UINavigationController (відкритий через властивість viewControllers) був оновлений до часу виклику viewWillDisappear.


2
Ідеально! Я не знаю, чому я про це не думав! Я думаю, я не думав, що стек буде змінено, поки методи зникнення не будуть викликані! Спасибі :-)
Водоспад Майкл

1
Я щойно намагався виконати те саме, але в, viewWillAppearі здавалося б, що чи виявляється контролер перегляду його натисканням, або щось вище, ніж його вискакує, масив viewControllers - це однаково і в інший спосіб! Будь-які ідеї?
Водоспад Майкл

Я також повинен зазначити, що контролер перегляду є стійким протягом життя програми, тому я не можу виконувати свої дії, viewDidLoadоскільки це викликається лише один раз! Хм, хитрий один!
Водоспад Майкл

4
@Sbrocket Чи є причина, яку ти не зробив ![viewControllers containsObject:self]замість цього [viewControllers indexOfObject:self] == NSNotFound? Вибір стилю?
zekel

24
Ця відповідь застаріла з iOS 5. -isMovingFromParentViewControllerМетод, згаданий нижче, дозволяє перевірити, чи явно відкривається погляд.
grahamparks

136

Я думаю, що найпростіший спосіб:

 - (void)viewWillDisappear:(BOOL)animated
{
    if ([self isMovingFromParentViewController])
    {
        NSLog(@"View controller was popped");
    }
    else
    {
        NSLog(@"New view controller was pushed");
    }
    [super viewWillDisappear:animated];
}

Швидкий:

override func viewWillDisappear(animated: Bool)
{
    if isMovingFromParent
    {
        print("View controller was popped")
    }
    else
    {
        print("New view controller was pushed")
    }
    super.viewWillDisappear(animated)
}

Що стосується iOS 5, це відповідь, можливо, також перевірити isBeingDismissed
d370urn3ur

4
Для iOS7 я повинен перевірити [self.navigationController.viewControllers indexOfObject: self] == NSNotFound ще раз, оскільки фонове додаток також пройде цей тест, але не видалить себе із стеку навігації.
Ерік Чен

3
Apple надала документально підтверджений спосіб зробити це - stackoverflow.com/a/33478133/385708
Shyam Bhat

Проблема з використанням viewWillDisappear полягає в тому, що можливо, що контролер вискакує зі стека, поки подання вже зникло. Наприклад, ще один контролер перегляду можна натиснути на вершину стека, а потім викликати popToRootViewControllerAnimated, минаючи viewWillDisappear на ті посередині.
Джон К

Припустимо, у вашому навігаційному стеці є два контролери (root vc та інший натиснутий). Коли третій висувається viewWillDisappear викликається другий, погляд якого зникне, правда? Отже, коли ви переходите на кореневий контролер перегляду (з’являється третій та другий), viewWillDisappear викликається на третьому, тобто останньому vc на стеці, оскільки його перегляд знаходиться на вершині і в цей час він зникне, а подання другого вже зникло. Ось чому цей метод називається viewWillDisappear, а не viewControllerWillBePopped.
RTasche

61

З документації Apple в UIViewController.h:

"Ці чотири способи можна використовувати у зворотній формі виклику контролера перегляду, щоб визначити, чи він подається, відхиляється чи додається чи видаляється як дочірній контролер перегляду. Наприклад, контролер перегляду може перевірити, чи він зникає, оскільки він був відхилений або вискакував, запитуючи себе у своєму viewWillDisappear: метод, перевіряючи вираз ([self isBeingDismissed] || [self isMovingFromParentViewController]). "

- (BOOL)isBeingPresented NS_AVAILABLE_IOS(5_0);

- (BOOL)isBeingDismissed NS_AVAILABLE_IOS(5_0);

- (BOOL)isMovingToParentViewController NS_AVAILABLE_IOS(5_0);

- (BOOL)isMovingFromParentViewController NS_AVAILABLE_IOS(5_0);

Так, так, єдиний документально підтверджений спосіб це наступний:

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    if ([self isBeingDismissed] || [self isMovingFromParentViewController]) {
    }
}

Версія Swift 3:

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    
    if self.isBeingDismissed || self.isMovingFromParentViewController { 
    }
}

18

Якщо ви просто хочете дізнатись, чи вискакує ваш погляд, я щойно виявив, що self.navigationControllerзнаходиться nilв ньому viewDidDisappear, коли він виймається зі стопки контролерів. Тож це простий альтернативний тест.

(Це я виявляю, пробуючи всілякі інші викривлення. Я здивований, що немає протоколу навігаційного контролера, який би зареєстрував контролер перегляду, який повинен отримувати сповіщення у спливних вікнах. Ви не можете користуватися, UINavigationControllerDelegateоскільки це насправді працює на реальному екрані.)


16

Швидкий 4

override func viewWillDisappear(_ animated: Bool)
    {
        super.viewWillDisappear(animated)
        if self.isMovingFromParent
        {
            //View Controller Popped
        }
        else
        {
            //New view controller pushed
        }
    }

6

У Свіфті:

 override func viewWillDisappear(animated: Bool) {
    if let navigationController = self.navigationController {
        if !contains(navigationController.viewControllers as! Array<UIViewController>, self) {
        }
    }

    super.viewWillDisappear(animated)

}

Обов’язково використовуйте як! замість як
dfmuir

2

Мені здається, що документацію Apple щодо цього важко зрозуміти. Це розширення допомагає побачити стани на кожній навігації.

extension UIViewController {
    public func printTransitionStates() {
        print("isBeingPresented=\(isBeingPresented)")
        print("isBeingDismissed=\(isBeingDismissed)")
        print("isMovingToParentViewController=\(isMovingToParentViewController)")
        print("isMovingFromParentViewController=\(isMovingFromParentViewController)")
    }
}

1

Це питання досить старе, але я бачив його випадково, тому хочу опублікувати кращі практики (afaik)

можна просто зробити

if([self.navigationController.viewControllers indexOfObject:self]==NSNotFound)
 // view controller popped
}

1

Це стосується iOS7 , поняття не має, якщо воно стосується будь-яких інших. З того, що я знаю, viewDidDisappearна вигляд уже вискочив. Що означає, що при запиті self.navigationController.viewControllersви отримаєте nil. Тому просто перевірте, чи це нуль.

TL; DR

 - (void)viewDidDisappear:(BOOL)animated
 {
    [super viewDidDisappear:animated];
    if (self.navigationController.viewControllers == nil) {
        // It has been popped!
        NSLog(@"Popped and Gone");
    }
 }

1

Segues може бути дуже ефективним способом вирішення цієї проблеми в iOS 6+. Якщо ви вказали ідентифікатор segue у програмі Interface Builder, ви можете перевірити його prepareForSegue.

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.identifier isEqualToString:@"LoginSegue"]) {
       NSLog(@"Push");
       // Do something specific here, or set a BOOL indicating
       // a push has occurred that will be checked later
    }
}

1

Дякуємо @Bryan Henry, все ще працює в Swift 5

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        if let controllers = navigationController?.children{
            if controllers.count > 1, controllers[controllers.count - 2] == self{
                // View is disappearing because a new view controller was pushed onto the stack
                print("New view controller was pushed")
            }
            else if controllers.firstIndex(of: self) == nil{
                // View is disappearing because it was popped from the stack
                print("View controller was popped")
            }
        }

    }

-1

Я припускаю, що ви маєте на увазі, що ваш погляд переміщується вниз по стеку навігаційного контролера, натискаючи на новий погляд, коли ви скажете, що його натиснули на стек. Я запропонував би використовувати viewDidUnloadметод , щоб додати NSLogзаяву , щоб написати що - то на консоль , так що ви можете побачити , що відбувається, ви можете додати NSLogв viewWillDissappeer.


-1

Ось категорія, щоб виконати те саме, що і відповідь sbrocket:

Заголовок:

#import <UIKit/UIKit.h>

@interface UIViewController (isBeingPopped)

- (BOOL) isBeingPopped;

@end

Джерело:

#import "UIViewController+isBeingPopped.h"

@implementation UIViewController (isBeingPopped)

- (BOOL) isBeingPopped {
    NSArray *viewControllers = self.navigationController.viewControllers;
    if (viewControllers.count > 1 && [viewControllers objectAtIndex:viewControllers.count-2] == self) {
        return NO;
    } else if ([viewControllers indexOfObject:self] == NSNotFound) {
        return YES;
    }
    return NO;
}

@end
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.