앱이 백그라운드에서 돌아올 때 viewWillAppear가 호출되지 않는 이유는 무엇입니까?


280

앱을 작성 중이며 전화 통화 중 사용자가 앱을보고있는 경우보기를 변경해야합니다.

다음 방법을 구현했습니다.

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    NSLog(@"viewWillAppear:");
    _sv.frame = CGRectMake(0.0, 0.0, 320.0, self.view.bounds.size.height);
}

그러나 앱이 포 그라운드로 돌아올 때 호출되지 않습니다.

구현할 수 있음을 알고 있습니다.

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarFrameChanged:) name:UIApplicationDidChangeStatusBarFrameNotification object:nil];

그러나 나는 이것을하고 싶지 않습니다. 오히려 모든 레이아웃 정보를 viewWillAppear : 메서드에 넣고 가능한 모든 시나리오를 처리하도록하십시오.

applicationWillEnterForeground :에서 viewWillAppear :를 호출하려고 시도했지만 해당 시점의 현재 뷰 컨트롤러를 정확하게 파악할 수 없습니다.

아무도 이것을 처리하는 올바른 방법을 알고 있습니까? 분명한 해결책이 빠져 있다고 확신합니다.


1
applicationWillEnterForeground:애플리케이션이 활성 상태를 다시 입력 한시기를 판별 하는 데 사용해야합니다 .
sudo rm -rf

나는 내 질문에 그것을 시도하고 있다고 말했다. 위를 참조하십시오. 앱 델리게이트 내에서 현재 뷰 컨트롤러를 결정하는 방법을 제공 할 수 있습니까?
Philip Walton

당신은 사용할 수 있습니다 isMemberOfClass또는 isKindOfClass귀하의 요구에 따라.
sudo rm -rf

@ sudo rm -rf 그러면 어떻게 작동합니까? 그가 isKindOfClass를 무엇이라고 부르겠습니까?
occulus

@occulus : Goodness는 그의 질문에 답하려고했습니다. 확실히 당신의 방법은 갈 길입니다.
sudo rm -rf

답변:


202

이 방법 viewWillAppear은 다른 응용 프로그램에서 다시 전환 할 때 포 그라운드에 놓이는 응용 프로그램의 컨텍스트가 아니라 자신의 응용 프로그램에서 진행되는 상황에서 취해야합니다.

즉, 누군가 다른 응용 프로그램을 보거나 전화를받는 경우 이전에 백그라운드에 있던 앱으로 다시 전환합니다. 그것이 우려되는 한, 그것은 결코 사라지지 않고 여전히 볼 수 있으며 그렇게 viewWillAppear불리지 않습니다.

나는 viewWillAppear당신 자신 을 부르지 말 것을 권합니다. 그것은 당신이 파괴해서는 안되는 특정한 의미를 가지고 있습니다! 동일한 효과를 얻기 위해 수행 할 수있는 리팩토링은 다음과 같습니다.

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self doMyLayoutStuff:self];
}

- (void)doMyLayoutStuff:(id)sender {
    // stuff
}

그런 다음 doMyLayoutStuff적절한 알림에서 트리거 합니다.

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(doMyLayoutStuff:) name:UIApplicationDidChangeStatusBarFrameNotification object:self];

그런데 '현재'UIViewController가 무엇인지 알 수있는 방법은 없습니다. 그러나 UIViewController가 언제 제공되는지 알아내는 UINavigationController의 대리자 메소드가 있습니다. 그런 것을 사용하여 제시 된 최신 UIViewController를 추적 할 수 있습니다.

최신 정보

다양한 비트에 적절한 자동 크기 조정 마스크를 사용하여 UI를 레이아웃하면 UI에 배치 된 '수동'을 처리 할 필요조차없는 경우가 있습니다.


101
이 솔루션에 감사드립니다. 실제로 UIApplicationDidBecomeActiveNotification에 대한 관찰자를 추가하고 매우 잘 작동합니다.
Wayne Liu

2
이것이 정답입니다. 그러나 "현재 UIViewController가 무엇인지 알 수있는 방법이 없습니다"라는 대답에 따라 self.navigationController.topViewController효과적으로 또는 적어도 스택 맨 위에있는 것을 제공한다고 생각합니다. 이 코드가 메인 컨트롤러에서 뷰 컨트롤러에서 실행되는 경우 현재 코드입니다. (잘못 될 수 있지만, 많이 연주하지는 않았지만 작동하는 것 같습니다.)
Matthew Frederick

appDelegate.rootViewController작동하지만을 반환 UINavigationController할 수 .topViewController있으며 @MatthewFrederick이 말한 것처럼 필요합니다 .
samson

7
UIApplicationDidBecomeActiveNotification이 올바르지 않습니다 (모든 사람들이 그것을지지하지만). 앱 시작시 (및 앱 시작시에만)이 알림은 다르게 호출됩니다. viewWillAppear에 추가하여 호출되므로이 답변을 사용하면 두 번 호출됩니다. Apple 은이 권리를 불필요하게 어렵게 만들었습니다. 문서는 여전히 누락되었습니다 (2013 년 기준)!
Adam

1
내가 찾은 해결책은 정적 변수가있는 클래스를 사용하는 것입니다 ( '정적 BOOLEnterBackground;'그런 다음 클래스 메소드 setter 및 getter를 추가합니다 .applicationDidEnterBackground에서 변수를 true로 설정 한 다음 applicationDidBecomeActive에서 정적 부울을 확인합니다 true 인 경우 "doMyLayoutStuff"로 변수를 'NO'로 재설정합니다. 이렇게하면 viewWillAppear with applicationDidBecomeActive 충돌이 발생하지 않으며 메모리 부족으로 인해 종료 된 경우 응용 프로그램이 백그라운드에서 입력 된 것으로 간주하지 않습니다.
vejmartin

196

빠른

짧은 답변

NotificationCenter대신 관찰자를 사용하십시오 viewWillAppear.

override func viewDidLoad() {
    super.viewDidLoad()

    // set observer for UIApplication.willEnterForegroundNotification
    NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)

}

// my selector that was defined above
@objc func willEnterForeground() {
    // do stuff
}

긴 대답

앱이 백그라운드에서 언제 돌아 오는지 알아 보려면 NotificationCenter대신 관찰자를 사용하십시오 viewWillAppear. 다음은 언제 어떤 이벤트가 발생하는지 보여주는 샘플 프로젝트입니다. (이것은 이 Objective-C 답변 의 적응입니다 .)

import UIKit
class ViewController: UIViewController {

    // MARK: - Overrides

    override func viewDidLoad() {
        super.viewDidLoad()
        print("view did load")

        // add notification observers
        NotificationCenter.default.addObserver(self, selector: #selector(didBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)

    }

    override func viewWillAppear(_ animated: Bool) {
        print("view will appear")
    }

    override func viewDidAppear(_ animated: Bool) {
        print("view did appear")
    }

    // MARK: - Notification oberserver methods

    @objc func didBecomeActive() {
        print("did become active")
    }

    @objc func willEnterForeground() {
        print("will enter foreground")
    }

}

앱을 처음 시작할 때 출력 순서는 다음과 같습니다.

view did load
view will appear
did become active
view did appear

홈 버튼을 누른 다음 앱을 다시 포 그라운드로 가져온 후 출력 순서는 다음과 같습니다.

will enter foreground
did become active 

원래 사용하려고한다면 그래서 viewWillAppear다음 UIApplication.willEnterForegroundNotification당신이 원하는 아마.

노트

iOS 9 이상에서는 관찰자를 제거 할 필요가 없습니다. 설명서 에는 다음 이 명시되어 있습니다.

앱이 iOS 9.0 이상 또는 macOS 10.11 이상을 대상으로하는 경우 해당 dealloc방법으로 옵저버를 등록 해제 할 필요가 없습니다 .


6
신속한 4.2에서 알림 이름은 이제 UIApplication.willEnterForegroundNotification 및 UIApplication.didBecomeActiveNotification
hordurh

140

viewDidLoad:ViewController 의 메소드 에서 알림 센터를 사용하여 메소드를 호출하고 메소드에서 수행해야 할 작업을 수행하십시오 viewWillAppear:. viewWillAppear:직접 전화 하는 것은 좋은 방법이 아닙니다.

- (void)viewDidLoad
{
    [super viewDidLoad];
    NSLog(@"view did load");

    [[NSNotificationCenter defaultCenter] addObserver:self 
        selector:@selector(applicationIsActive:) 
        name:UIApplicationDidBecomeActiveNotification 
        object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self 
        selector:@selector(applicationEnteredForeground:) 
        name:UIApplicationWillEnterForegroundNotification
        object:nil];
}

- (void)applicationIsActive:(NSNotification *)notification {
    NSLog(@"Application Did Become Active");
}

- (void)applicationEnteredForeground:(NSNotification *)notification {
    NSLog(@"Application Entered Foreground");
}

9
dealloc방법 에서 관찰자를 제거하는 것이 좋습니다 .
AncAinu

2
그렇다면 viewDidLoad에하지 않는 가장 좋은 방법은 viewDidUnload에서, 관찰자로 제거 관찰자 자체를 추가
Injectios

자기 관찰자를 추가하는 가장 좋은 방법은 무엇입니까?
Piotr Wasilewicz

뷰 컨트롤러가 UIApplicationWillEnterForegroundNotification과 같은 알림 하나만 관찰 할 수 없습니다. 왜 둘 다 들어?
zulkarnain shah

34

viewWillAppear:animated:필자의 의견으로는 iOS SDK에서 가장 혼란스러운 방법 중 하나 인 응용 프로그램 전환과 같은 상황에서 호출되지 않습니다. 이 메소드는 뷰 컨트롤러의보기와 응용 프로그램의 창 사이의 관계에 따라서 만 호출됩니다 . 즉, 메시지가 화면이 아닌 응용 프로그램의 창에 나타나는 경우에만 메시지가 뷰 컨트롤러로 전송됩니다.

응용 프로그램이 배경이되면 응용 프로그램 창의 최상위 뷰가 더 이상 사용자에게 보이지 않습니다. 그러나 응용 프로그램 창의 관점에서는 여전히 최상위 뷰이므로 창에서 사라지지 않았습니다. 오히려 응용 프로그램 창이 사라져서 해당 뷰가 사라졌습니다. 그들은 창 에서 사라져 사라지지 않았다 .

따라서 사용자가 응용 프로그램으로 다시 전환하면 창이 다시 나타나기 때문에 분명히 화면에 나타나는 것처럼 보입니다. 그러나 창문의 관점에서 그들은 전혀 사라지지 않았습니다. 따라서 뷰 컨트롤러는 viewWillAppear:animated메시지를 받지 않습니다 .


2
또한 -viewWillDisappear : animated :는 앱 종료시 호출되므로 상태를 저장하기에 편리한 장소였습니다. 앱이 백그라운드 일 때는 호출되지 않으며 백그라운드 된 앱은 경고없이 종료 될 수 있습니다.
tc.

6
실제로 잘못 명명 된 또 다른 방법은 viewDidUnload입니다. 당신은 그것이 viewDidLoad의 반대라고 생각할 것입니다. 메모리가 부족하여 뷰가 언로드 될 때만 호출되며 뷰가 실제로 할당 해제시 언로드 될 때마다 호출되지는 않습니다.
occulus

나는 @occulus에 전적으로 동의합니다. viewWillAppear는 (다양한) 멀티 태스킹이 없었기 때문에 변명의 여지가 있지만 viewDidUnload는 확실히 더 나은 이름을 가질 수 있습니다.
MHC

나에게 viewDidDisappear는 iOS7에서 앱이 백그라운드 일 때 호출됩니다. 확인을받을 수 있습니까?
Mike Kogan 2016 년

4

스위프트 4.2 / 5

override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground),
                                           name: Notification.Name.UIApplicationWillEnterForeground,
                                           object: nil)
}

@objc func willEnterForeground() {
   // do what's needed
}

3

가능한 한 쉽게 만들기 위해 아래 코드를 참조하십시오.

- (void)viewDidLoad
{
   [self appWillEnterForeground]; //register For Application Will enterForeground
}


- (id)appWillEnterForeground{ //Application will enter foreground.

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(allFunctions)
                                                 name:UIApplicationWillEnterForegroundNotification
                                               object:nil];
    return self;
}


-(void) allFunctions{ //call any functions that need to be run when application will enter foreground 
    NSLog(@"calling all functions...application just came back from foreground");


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