Prevent SKNode from unpausing automatically when app resumes - ios

In my game, I have functions to pause and unpause an SKNode that contains the gameplay elements. Currently, the system automatically pauses when the home button is pressed, and unpauses when the app becomes active again.
I would like to do this on my own terms. For instance, when the app becomes active again, it should show the pause menu, and stay paused until the user manually unpauses.
Is there a way to override this system behavior?

You can register to receive the UIApplicationDidEnterBackgroundNotification or UIApplicationWillEnterForegroundNotification notification, and trigger the pause menu in the notification selector.

Somewhere in your code, you'd have to register a notification. There are plenty of notifications to choose from. Here is one for when the App comes back from the background:
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "didBecomeActive:", name: UIApplicationDidBecomeActiveNotification, object: nil)
}
func didBecomeActive(test: NSNotification) {
self.unpause()
}
Notice that the selector in the addObserver method is the name of the function that will be called when the application comes back from the background. Also the didBecomeActive method needs an argument of type NSNotification.

Related

How to detect if notification is turn on when the app comes back from Settings in Swift 5?

I have a switch button that will trigger notification alerts. I was able to implement asking for Notification permission (first time the user trigger the switch), I was also able to implement bringing the user to open the app's notification settings when the user click on the switch button again (if the user decline the first request). But I am having problem on how to get that Notification is turn on information back to the app.
App is launch, user click on toggle switch (Notification request dialogue will pop up).
If user decline the initial request and click on toggle switch again, a dialogue will pop up and suggest/bring the user to the app's settings. (the app goes to background, Settings is front)
when the user turn on the Notification Settings, then go back to the app (Settings goes to background, the app comes to foreground).
At this stage, how can I detect Notification setting for the app is turn on and show the switch button as 'on'?
I tried override func viewWillAppear (this doesn't work because the viewcontroller never calls/reload again when you go back to the app from Settings.)
I also tried in AppDelegate, applicationWillEnterForeground and applicationDidBecomeActive, both didn't work either.
I hope this make sense. Thank you for any suggestion.
Import UserNotifications into your ViewController
import UserNotifications
Create a method to check the current notification status
private func hasNotificationPermission(completion: #escaping (Bool) -> Void) {
UNUserNotificationCenter.current().getNotificationSettings { (settings) in
completion(settings.authorizationStatus == .authorized)
}
}
Add observer to detect when app becomes active
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(self.openactivity), name: NSNotification.UIApplicationDidBecomeActive, object: nil)
}
When coming back from background check for Notification status and update your UI
#objc func appBecomeActive() {
if hasNotificationPermission {
// Notifications are enabled
} else {
// Notifications are not enabled
}
}

Detect when app is closed, terminated but ignore when the phone is turned off

I want to know when someone closes my app (taps the home button) or terminates my app (double tap swipe up) however I do not want to know when the user is using my app and simply turns their phone off because when they turn their phone back on it will still be on my app.
I have tried using the applicationWillResignActive, applicationDidEnterBackground, applicationWillTerminate and registering it in my view controller
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self, selector: #selector(appMovedToBackground), name: UIApplication.willResignActiveNotification, object: nil)
These either didn't tell me when my app wasn't the focus or if they did work they also told me when my app was still open just the phone was closed.
You cannot draw the distinction you are asking to draw. Whether the user clicks the Home button or turns off the screen, your app is deactivated and backgrounded and you are told so — and that is all you are told.
However, you do not need to draw this distinction. When your app is backgrounded, just do whatever is appropriate. You’ll be told when your app is foregrounded again, even if that is just because the user turned the screen back on.
So after some research the only possible way I found was:
func DidUserPressLockButton() -> Bool
{
let oldBrightness = UIScreen.main.brightness
UIScreen.main.brightness = oldBrightness + (oldBrightness <= 0.01 ? (0.01) : (-0.01))
return oldBrightness != UIScreen.main.brightness
}
func applicationDidEnterBackground(_ application: UIApplication)
{
if (DidUserPressLockButton())
{
//User pressed lock button
}
else
{
//user pressed home button
}}
This method works by seeing if the app can change the brightness however some say this will get rejected by apple because they don't want you to do which makes sense why there isn't a built in function for it however there are apps on the App Store that know how you left the app so there you go

How to prevent SpriteKit game from crashing when Notification Center is opened

I am having a weird issue with a SpriteKit game. The app crashes when a user opens Notification center or Control Center. I want the worldNode layer to be paused and the menuNode layer to be presented when the applicationWillResignActive is called, which is working fine when the home button is pressed. I've tried pausing my game from within the AppDelegate functions applicationWillResignActive and I've tried pausing when applicationDidBecomeActive is called if the game has started. I've tried using the NotificationCenter approach from within the ViewController both work when the Home Button/Home Gesture is used. How Should I handle this? Or is this just a bug in iOS 11?
I have this in the viewDidLoad method of my ViewController
NotificationCenter.default.addObserver(self, selector: #selector(appWillResignActive), name: NSNotification.Name.UIApplicationWillResignActive, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(appDidBecomeActive), name: NSNotification.Name.UIApplicationDidBecomeActive, object: nil)
Then each one of those selectors
It crashes on both if trying to present the menu when the appDidBecomeActive and appWillResignActive
#objc func appDidBecomeActive() {
print("appDidBecomeActive")
if GameScene.sharedInstance != nil {
print("presentingMenu")
GameScene.sharedInstance.presentMenuNode()
}
}
#objc func appWillResignActive() {
print("appWillResignActive")
if GameScene.sharedInstance != nil {
print("presentingMenu")
GameScene.sharedInstance.presentMenuNode()
}
}
I feel like I may be trying to approach this the wrong way, but what I don't understand is why does it work when the Home button/Home gesture is fired?
Edit:
After more testing I found that everything works as expected when running on iOS 10 devices. However when I run the DemoBots app that apple provides from their sample code it doesn't crash on iOS 11 and basically does what I want to do, so there has got to be a better way to handle the transitions of the game state, any input is appreciated.

AVPlayer stopped early Swift 3

I currently have the following listener for my AVPlayer
NotificationCenter.default.addObserver(self, selector: #selector(self.didFinishPlaying), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: video.currentItem)
I am wondering how to add a listener for when the AVPlayer is stopped prematurely, as when I user is watching at full scene and hits the done in the top corner.
Register your listener as an observer in Notification Center, for a
custom notification name e.g.
Notification.Name("UserStoppedAvPlayer"), register a selector for this notification such as func userDidStopAvPlayer().
When your user taps the done button, within the selector/method thats triggered, where you stop the AvPlayer you should then post a notification to Notification Center with the custom name you registered on the listner.
Your listener will then fire the selector you registered and you should do your logic in there.

Keeping timers running when app enters background

I am making an app that relies on a timer that fires every minute to change a label from "x minutes left" to "(x-1) minutes left. Basically, it decrements the number every minute. I need the timer to function even when I close the app (not entirely, just press the home screen and leave it running in the background), so that when the user comes back to the app after leaving it in the background for 5 minutes, the label will say "(x-5) minutes left".
Right now, when I run it in the simulator it works perfectly, but when I run it on my phone it does not work. If I have the app open on my phone, it works, but if the app is running in the background, the label never decrements. Could this be due to differences in the way the simulator and actual iPhone handle multitasking? If so, how can I change my code so that the timer will still update the label every minute so that the correct number is displayed when the user reopens the app?
Here is my timer setup:
var individualTaskTimer = NSTimer()
func createTimerForTopTask(){
individualTaskTimer = NSTimer.scheduledTimerWithTimeInterval(tasks[0].minutes * 60, target: self, selector: "deleteTopTask", userInfo: nil, repeats: true)
}
As mentioned in Ewan Mellor's answer, you will not be able to rely on a timer while the app is in the background. So you will need to adjust as necessary when your app returns to the foreground.
Upon first reading the documentation, it might seem like viewWillAppear and viewWillDisappear (or viewDidDisappear) are the correct places to handle this. However, they do not get called when the app moves to/from the background.
Instead, you can make use of two notifications, UIApplicationWillResignActiveNotification and UIApplicationDidBecomeActiveNotification. The first notification will be sent to your app when it is about to go into the background. The second notification will be sent to your app when it is about to return to the foreground.
So in viewWillAppear you can register for the notifications as follows:
override func viewWillAppear(animated: Bool) {
// some other code
NSNotificationCenter.defaultCenter().addObserver(self, selector: "activeAgain", name: "UIApplicationDidBecomeActiveNotification", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "goingAway", name: "UIApplicationWillResignActiveNotification", object: nil)
}
where activeAgain and goingAway are two functions you've written to enable and disable the timer. So based on the code snippet in your question, they would look something like this:
func activeAgain() {
let newTime = // calculate how much time is left (in seconds)
individualTaskTimer = NSTimer.scheduledTimerWithTimeInterval(newTime, target: self, selector: "deleteTopTask", userInfo: nil, repeats: true)
}
func goingAway() {
individualTaskTimer.invalidate()
}
Note that you need to unregister for the notifications when you switch away from this view. Doing this in viewWillDisappear is probably a good spot.
You can't do this. iOS will suspend your app after a short while when it goes into the background. Just update your label with the correct time when the app comes back to the foreground.

Resources