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");
  }
}

물론 viewinillDisappear가 호출 될 때까지 UINavigationController의 뷰 컨트롤러 스택 (viewControllers 속성을 통해 노출됨)이 업데이트 되었기 때문에 가능합니다.


2
완전한! 왜 그런 생각을하지 않았는지 모르겠어요! 사라지는 메소드가 호출 될 때까지 스택이 변경 될 것이라고 생각하지 않았습니다! 감사합니다 :-)
마이클 폭포

1
방금 동일한 작업을 수행하려고했지만 viewWillAppear뷰 컨트롤러가 푸시되어 표시되는지 또는 위에있는 것이 표시되는지 여부에 관계없이 viewControllers 배열은 두 가지 방식으로 동일합니다! 어떤 아이디어?
Michael Waterfall

또한 뷰 컨트롤러는 앱 수명 동안 지속되므로 viewDidLoad한 번만 호출되므로 작업을 수행 할 수 없습니다 ! 흠, 까다로운 하나!
Michael Waterfall

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를 다시 확인해야합니다.
Eric Chen

3
- 애플은이 할 수있는 문서화 된 방법을 제공하고있다 stackoverflow.com/a/33478133/385708
시암 바트

viewWillDisappear 사용시 문제점은보기가 이미 사라진 상태에서 스택에서 컨트롤러가 튀어 나올 수 있다는 것입니다. 예를 들어, 다른 뷰 컨트롤러를 스택 위에 밀어 넣은 다음 중간에있는 viewWillDisappear를 우회하여 popToRootViewControllerAnimated를 호출 할 수 있습니다.
John K

탐색 스택에 두 개의 제어기 (root vc 및 다른 제어기)가 있다고 가정하십시오. 세 번째 뷰를 누르면 뷰가 사라질 두 번째 뷰에서 WillDisappear가 호출됩니다. 따라서 루트보기 컨트롤러에 팝업 할 때 (세 번째와 두 번째 팝) viewWillDisappear는 세 번째 즉, 스택의 마지막 vc에서 호출됩니다.보기가 맨 위에 있고 현재 사라지고 두 번째보기는 이미 사라졌기 때문입니다. 그래서이 메소드는 viewControllerWillBePopped가 아닌 viewWillDisappear라고합니다.
RTasche

61

UIViewController.h의 Apple 문서에서 :

"이 4 가지 방법은 뷰 컨트롤러의 모양 콜백에서 하위 뷰 컨트롤러로 표시, 해제 또는 추가 또는 제거되는지 여부를 결정하는 데 사용될 수 있습니다. 예를 들어, 뷰 컨트롤러는 뷰 컨트롤러가 해제되었거나 사라져서 사라 졌는지 확인할 수 있습니다. 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]) {
    }
}

스위프트 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에 적용되며 다른 iOS 에 적용되는지 전혀 모릅니다. 내가 아는 것에서 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에 식별자를 부여한 경우에서 확인할 수 있습니다 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당신이 무슨 일이 일어나고 있는지 볼 수 있도록 콘솔에 쓰기 뭔가 문을, 당신은 추가 할 수 있습니다 NSLogviewWillDissappeer.


-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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.