28

I have several iOS apps that all use the same port to listen for a network beacon. On the main view I use viewWillDisappear to close the port when another view is opened, which was working great. Then I noticed if I pressed the home button from the main view controller without opening another view to close the port, then the port stays open and non of my other apps can listen on that port any more. I then tried using viewWillUnload, but that doesn't seem to get called when I press the home button.

-(void)viewWillUnload
{
    //[super viewWillUnload];
    NSLog(@"View will unload");
    [udpSocket close];
    udpSocket = nil;
}

View will unload is never displayed in the console, which leads me to believe that the method is never getting called.

Is there a way to detect when the home button is pressed so I can close my port?

nick
  • 2,833
  • 3
  • 34
  • 61
  • 3
    Try to use "applicationWillTerminate" method. :-) –  Apr 25 '12 at 22:35
  • 1
    The "applicationWillTerminate" method does not exist. However, subclasses can register for the UIApplicationWillTerminateNotification and then do their own cleanup or closing down. – Elise van Looij May 18 '12 at 15:02

6 Answers6

46

These are your options

In your app delegate:

- (void)applicationWillResignActive:(UIApplication *)application
{
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}

- (void)applicationWillTerminate:(UIApplication *)application
{
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
Mick MacCallum
  • 129,200
  • 40
  • 280
  • 281
  • 2
    I had the code to close the port in viewWillDisappear and it didn't seem to be getting called. The port would remain open and all other apps that used the port would fail. I set up a class method to close the port and call it from applicationDidEnterBackground and it works great – nick Apr 26 '12 at 04:19
  • 9
    `viewWillDisappear` or `viewDidDisappear` doesn't get called when Home button is pressed or the app is terminated. The best solution is using `UIApplicationWillResignActiveNotification` notification – Sam Oct 07 '13 at 10:42
32

The easiest way to handle this is to register to receive the UIApplicationWillResignActiveNotification notification in your view controller.

The event is issued upon a home button press, lock and upon a phone call

- (void) applicationWillResign{
    NSLog(@"About to lose focus");
}

- (void) myVcInitMethod { 
    [[NSNotificationCenter defaultCenter]
        addObserver:self
        selector:@selector(applicationWillResign)
        name:UIApplicationWillResignActiveNotification 
        object:nil];
}
PeterPurple
  • 433
  • 4
  • 13
  • 3
    Why pass NULL instead of nil? – mszaro Aug 16 '13 at 18:04
  • 8
    Actually `applicationWillResignActive` notification is _not_ always the best way to do this, since resign active also includes (accidental) slide down top menu, or the new slide up bottom menu in ios 7. `applicationDidEnterBackground` means your app was "minimized", and is available from iOS 4. – bobobobo Jan 01 '14 at 18:26
13

In case of Swift User

you can write it like this

override func viewDidLoad() {
    super.viewDidLoad()

    // code here...

    NSNotificationCenter.defaultCenter().addObserver(
        self,
        selector: "applicationWillResignActive:",
        name: UIApplicationWillResignActiveNotification,
        object: nil)
}

func applicationWillResignActive(notification: NSNotification) {
    print("I'm out of focus!")
}

also, Don't forget to close it when your app is terminate

deinit {

    // code here...

    NSNotificationCenter.defaultCenter().removeObserver(self)
}
Sruit A.Suk
  • 7,073
  • 7
  • 61
  • 71
  • If you are using iOS 9 or further you can forget about removing observers in the deinit method. But, only if you don’t plan on supporting iOS 8 or earlier. And, as @bobobobo said, you should use applicationDidEnterBackground – Matias Jurfest Oct 20 '17 at 13:20
5

viewWillUnload is often not called except in the case of low memory. You're better off implementing the application delegate methods applicationDidEnterBackground: or applicationWillTerminate: and doing the work there or sending a notification to the part of your application that knows how to handle the cleanup process.

warrenm
  • 31,094
  • 6
  • 92
  • 116
5

viewWillUnload is usually not called except in the case of low memory. Use these instead:

In your App Delegate:

- (void)applicationWillResignActive:(UIApplication *)application
{
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}

- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}

- (void)applicationWillTerminate:(UIApplication *)application
{
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}

Or if you want to use code in your View Controller:

- (void)viewDidDisappear:(BOOL)animated
{
//Put code here
}

- (void)viewWillDisappear:(BOOL)animated
{
//Put code here
}
dgund
  • 3,459
  • 4
  • 39
  • 64
  • viewWill/DidDisappear will call when the app is closing. Not when Home Button pressed to minimize applications to control center. – khunshan Aug 06 '14 at 11:14
5

Better to use UIApplicationWillResignActive and UIApplicationDidBecomeActive due to they catch 'top rectangle catch and release event'. I would suggest to use this root class:

class VBase: UIViewController {
    fileprivate var listenersActivated = false
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        onStart()
    }
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        onStop()
        removeListeners()
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
        onStop()
        removeListeners()
    }

    internal func iniListeners() {
        if (!listenersActivated) {
            NotificationCenter.default.addObserver(self, selector: #selector(onStop), name: NSNotification.Name.UIApplicationWillResignActive, object: nil)
            NotificationCenter.default.addObserver(self, selector: #selector(onStart), name: NSNotification.Name.UIApplicationDidBecomeActive, object: nil)
            listenersActivated = true
        } else {

        }
    }
    internal func removeListeners() {
        NotificationCenter.default.removeObserver(self)
        listenersActivated = false
    }
    internal func onStop() {

    }
    internal func onStart() {
        iniListeners()
    }

}

Override onStop() and onStart() inside childs to catch all view appearance/disappearance

That is,

class SomeViewController: VBase {

...
    override func onStart() {
        super.onStart()
        someFunctionToInitialize()
    }
    override func onStop() {
        super.onStop()
        stopTimer()
        someFunctionToDesctruction()
    }
}
Vyacheslav
  • 26,359
  • 19
  • 112
  • 194