जब कोई ऐप बैकग्राउंड से वापस आता है तो viewWillAppear क्यों नहीं कहा जाता है?


279

मैं एक ऐप लिख रहा हूं और अगर फोन पर बात करते समय उपयोगकर्ता ऐप को देख रहा है तो मुझे दृश्य बदलने की आवश्यकता है।

मैंने निम्नलिखित विधि लागू की है:

- (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: पद्धति में डाल सकता हूं, और सभी संभावित परिदृश्यों को संभालने दूंगा।

मैंने भी viewWillAppear को कॉल करने की कोशिश की है: ApplicationWillEnterForeground :, से, लेकिन मैं उस बिंदु पर वर्तमान दृश्य नियंत्रक है जो इंगित करने के लिए प्रतीत नहीं कर सकता।

क्या किसी को इससे निपटने का उचित तरीका पता है? मुझे यकीन है कि मुझे एक स्पष्ट समाधान याद आ रहा है।


1
आपको यह applicationWillEnterForeground:निर्धारित करने के लिए उपयोग करना चाहिए कि आपके आवेदन ने सक्रिय स्थिति में फिर से प्रवेश किया है।
सूदो आरएम -rf

मैंने कहा कि मैं अपने प्रश्न में कोशिश कर रहा था। कृपया ऊपर देखें। क्या आप यह निर्धारित करने का एक तरीका प्रदान कर सकते हैं कि ऐप प्रतिनिधि के भीतर से वर्तमान दृश्य नियंत्रक कौन सा है?
फिलिप वाल्टन

आप अपनी आवश्यकताओं के आधार पर उपयोग isMemberOfClassकर सकते हैं या कर isKindOfClassसकते हैं।
सूदो rm -rf

@sudo rm -rf फिर वह काम कैसे होगा? वह किस पर कॉल करने जा रहा है किंडऑफक्लास?
occulus

@ कोकुलस: अच्छाई जानता है, मैं सिर्फ उसके सवाल का जवाब देने की कोशिश कर रहा था। यह सुनिश्चित करने के लिए कि आपका यह करने का तरीका है।
सूदो rm -rf

जवाबों:


202

प्रक्रिया viewWillAppear को आपके स्वयं के अनुप्रयोग में क्या हो रहा है, के संदर्भ में लिया जाना चाहिए, न कि आपके आवेदन के संदर्भ में अग्रभूमि में रखा जाए जब आप इसे किसी अन्य ऐप से वापस स्विच करते हैं।

दूसरे शब्दों में, यदि कोई व्यक्ति किसी अन्य एप्लिकेशन को देखता है या फोन कॉल लेता है, तो अपने ऐप पर वापस आ जाता है जो पहले पृष्ठभूमि में था, आपका UIViewController जो कि आपके ऐप को छोड़ने के लिए पहले से ही दिखाई दे रहा था 'बोलने की परवाह नहीं करता' - जहां तक ​​इसका संबंध है, यह कभी गायब नहीं हुआ और अभी भी दिखाई दे रहा है - और इसी तरह 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 को प्रस्तुत करने के लिए ऐसी चीज़ का उपयोग कर सकते हैं जिसे प्रस्तुत किया गया है।

अपडेट करें

यदि आप विभिन्न बिट्स पर उपयुक्त ऑटोरेस्सिगिंग मास्क के साथ यूआई को लेआउट करते हैं, तो कभी-कभी आपको अपने यूआई से बाहर ले जाने वाले 'मैनुअल' से निपटने की भी आवश्यकता नहीं होती है - यह सिर्फ निपटा जाता है ...


101
इस समाधान के लिए धन्यवाद। मैं वास्तव में UIApplicationDidBecomeActiveNotification के लिए पर्यवेक्षक जोड़ता हूं और यह बहुत अच्छा काम करता है।
वेन लियू

2
यह निश्चित रूप से सही उत्तर है। ध्यान दें, हालांकि, "वर्तमान 'UIViewController" बताने के लिए "कोई भी तरीका नहीं है," के जवाब में, मेरा मानना ​​है कि self.navigationController.topViewControllerप्रभावी रूप से इसे प्रदान करता है, या कम से कम स्टैक के शीर्ष पर, जो होगा वर्तमान एक यदि यह कोड मुख्य थ्रेड पर व्यू कॉनोलर में फायर कर रहा है। (गलत हो सकता है, इसके साथ बहुत खेला नहीं गया है, लेकिन काम करने लगता है।)
मैथ्यू फ्रेडरिक

appDelegate.rootViewControllerयह भी काम करेगा, लेकिन यह एक वापसी कर सकता है UINavigationController, और फिर आपको .topViewController@MatthewFrederick के रूप में आवश्यकता होगी ।
शिमशोन

7
UIApplicationDidBecomeActiveNotification गलत है (सभी लोग इसे अपवित्र करने के बावजूद)। ऐप स्टार्ट पर (और केवल ऐप शुरू होने पर!) इस नोटिफिकेशन को अलग तरह से कहा जाता है - इसे व्यूविलेयर के अलावा कहा जाता है, इसलिए इस उत्तर के साथ आपको इसे दो बार कॉल करना होगा। Apple ने यह अधिकार प्राप्त करना अनावश्यक रूप से कठिन बना दिया - डॉक्स अभी भी गायब हैं (2013 के अनुसार!)।
एडम

1
मैं जिस समाधान के साथ आया था, वह एक स्थिर चर ('स्थिर BOOL enterBackground;' के साथ एक वर्ग का उपयोग करने के लिए था। फिर मैं वर्ग विधियों को बसाने और प्राप्त करने वालों को जोड़ता हूं। ApplicationDidEnterBackground में, मैंने चर को सही पर सेट किया। फिर ApplicationDidBecomeActive में, मैं स्थैतिक बूल की जांच करता हूं। , और अगर यह सच है, तो मैं "doMyLayoutStuff" और चर को 'NO' पर रीसेट करता हूं। यह रोकता है: viewWillAppear के साथ ApplicationDidBecomeActive टकराव, और यह भी सुनिश्चित करता है कि मेमोरी प्रेशर के कारण समाप्त होने पर एप्लिकेशन इसे पृष्ठभूमि से दर्ज नहीं करता है।
वेजमार्टिन

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। यहां एक नमूना परियोजना है जो दिखाती है कि कौन सी घटनाएं कब घटित होती हैं। (यह इस उद्देश्य-सी उत्तर का एक अनुकूलन है ।)

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विधि में पर्यवेक्षक को हटाने के लिए एक अच्छा विचार हो सकता है ।
अनकिनू

2
viewDidLoad नहीं सबसे अच्छा तरीका viewDidUnload में पर्यवेक्षक के रूप में स्वयं को जोड़ने के लिए, यदि हां, निकालें पर्यवेक्षक
Injectios

स्वयं पर्यवेक्षक को जोड़ने का सबसे अच्छा तरीका क्या है?
पायोत्र वासिलिवेज़

केवल एक अधिसूचना, यानी, UIApplicationWillEnterForegroundNotification के लिए व्यूकंट्रोलर निरीक्षण नहीं कर सकता। दोनों की क्यों सुनें?
zulkarnain shah

34

viewWillAppear:animated:, मेरी राय में आईओएस एसडीके में सबसे भ्रमित करने वाली विधियों में से एक है, ऐसी स्थिति में कभी भी लागू नहीं किया जाएगा, अर्थात, आवेदन स्विचिंग। यह विधि केवल दृश्य नियंत्रक के दृश्य और एप्लिकेशन की विंडो के बीच संबंध के अनुसार लागू की जाती है , अर्थात, संदेश केवल एक दृश्य नियंत्रक को भेजा जाता है, यदि उसका दृश्य अनुप्रयोग की विंडो पर दिखाई देता है, स्क्रीन पर नहीं।

जब आपका एप्लिकेशन पृष्ठभूमि में जाता है, तो स्पष्ट रूप से एप्लिकेशन विंडो के सबसे ऊपरी दृश्य अब उपयोगकर्ता को दिखाई नहीं देते हैं। आपके एप्लिकेशन विंडो के परिप्रेक्ष्य में, हालांकि, वे अभी भी सबसे ऊपरी दृश्य हैं और इसलिए वे खिड़की से गायब नहीं हुए। बल्कि, वे दृश्य गायब हो गए क्योंकि एप्लिकेशन विंडो गायब हो गई। वे गायब नहीं हुए क्योंकि वे गायब हो गए थे खिड़की थे।

इसलिए, जब उपयोगकर्ता आपके एप्लिकेशन पर वापस जाता है, तो वे स्पष्ट रूप से स्क्रीन पर दिखाई देते हैं, क्योंकि विंडो फिर से दिखाई देती है। लेकिन खिड़की के दृष्टिकोण से, वे बिल्कुल गायब नहीं हुए। इसलिए दृश्य नियंत्रकों को कभी viewWillAppear:animatedसंदेश नहीं मिलता है ।


2
इसके अतिरिक्त, -viewWillDisappear: animated: यह ऐप से बाहर निकलने के बाद से राज्य को बचाने के लिए एक सुविधाजनक स्थान हुआ करता था। यह तब नहीं कहा जाता है जब एप्लिकेशन को पृष्ठभूमि में लिया जाता है, हालांकि, और एक पृष्ठभूमि वाले ऐप को बिना चेतावनी के मारा जा सकता है।
टीसी

6
एक और वास्तव में बुरी तरह से नामित विधि viewDidUnload है। आपको लगता है कि यह viewDidLoad के विपरीत था, लेकिन नहीं; यह केवल तब कहा जाता है जब एक कम स्मृति स्थिति थी जो दृश्य को अनलोड करने का कारण बनी, और हर बार दृश्य वास्तव में डीलडॉक समय पर अनलोड नहीं किया गया था।
occulus

मैं @occulus से बिल्कुल सहमत हूं। viewWillAppear के पास इसका बहाना है क्योंकि (सॉर्ट) मल्टीटास्किंग नहीं थी, लेकिन viewDidUnload निश्चित रूप से एक बेहतर नाम हो सकता है।
एमएचसी

मेरे लिए ViewDidDisappear IS को बुलाया जाता है जब ऐप को iOS7 पर बैकग्राउंड किया जाता है। क्या मुझे पुष्टि मिल सकती है?
माइक कोगन

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


}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.