I'm developing a game with SpriteKit that can be paused during execution and can be resumed.
But I have a problem with applicationDidEnterBackground when the home button is pressed while the game is paused because when I resume the game the entities start moving immediately even though the game was paused before.
I cannot find a way to implement applicationDidEnterBackground and other related method in AppDelegate since there is no connection between that and my GameScene.swift
I'm actually pausing the entities with the code
entitiesLayerNode.paused = true
gameState = .GamePaused
EDIT:
I want to explicitly pause the entitiesLayerNode because I have other 'moving' nodes that I want to keep. To the problem is that with the suggestion given below that method pauses everything! I just need to pause that layer.
I think that problem is that I cannot access the entitiesLayerNode from the View Controller.
I generally use the snippet
let mainScene = scene as GameScene
mainScene.entitiesLayerNode.pause = true
But Xcode gives me an error that scene is an unresolved identifier.
Pretty much the same as here
AppDelegate:
func applicationWillResignActive(application: UIApplication) {
NSNotificationCenter.defaultCenter().postNotificationName("PauseGameScene", object: self)
}
GameSceneViewController:
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("pauseGameScene"), name: "PauseGameScene", object: nil)
}
func pauseGameScene() {
if self.skView.scene != nil {
self.skView.paused = self.skView.scene.paused = true
}
}
Related
I am using AVPlayer to play online videos. It is working fine. Now the problem is, when I pop out from the view before the videos starts playing, the background process keeps running. And when the videos gets loaded, it starts playing in background.
Before dismiss view pause player
if player != nil {
player.pause()
player = nil
}
and also you need to pause player when your app goes to background
add this in your viewDidLoad
NotificationCenter.default.addObserver(self,
selector: #selector(applicationDidEnterBackground),
name: NSNotification.Name.UIApplicationDidEnterBackground,
object: nil)
add this in your viewController
#objc func applicationDidEnterBackground() {
if player != nil {
player.pause()
player = nil
}
}
By handling those both scenario you can stop player when you dismiss from that viewController also when your app goes into background
Hope this will help you
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if self.player != nil {
self.player.pause()
}
}
I am trying to perform the Finite-Length background task in my app. However, as of now my code is not executed before the app is suspended.
I've followed quite a few tutorials that claims the following to be the way, but obviously I'm getting something wrong. Relevant code should be posted below (please just ask for any clarification if I'm missing something):
class Manager {
private var backgroundTask: UIBackgroundTaskIdentifier = UIBackgroundTaskInvalid
init(){
// Add observer able of detecting when app will go to background
NotificationCenter.default.addObserver(self, selector: #selector(self.didEnterBackground(_:)), name: .UIApplicationDidEnterBackground, object: nil)
}
deinit {
// Observers removed when view controller is dismissed / deallocated
NotificationCenter.default.removeObserver(self)
}
// Routine performed when app will resign from active
#objc private func didEnterBackground(_ notification: Notification){
registerBackgroundTask()
// Code that needs to be executed before app is suspended ------
DispatchQueue.global().sync {
self.isBackgrounding = true
self.shutdownSession()
self.isConnected = false
self.isActivated = false
self.activate = false
self.connectionManager.closeConnectionToPeripherals()
}
// -----------------------------------------------------
self.endBackgroundTask()
}
func registerBackgroundTask() {
backgroundTask = UIApplication.shared.beginBackgroundTask { [weak self] in
self?.endBackgroundTask()
}
assert(backgroundTask != UIBackgroundTaskInvalid)
}
func endBackgroundTask() {
print("\n\n\nBackground task ended.\n\n\n")
UIApplication.shared.endBackgroundTask(backgroundTask)
backgroundTask = UIBackgroundTaskInvalid
}
}
I am facing a problem where the background task always seem to be ended before executing the code. How do I ensure that the code in my synchronous block gets executed before app is suspended?
I don't quite understand the "registerBackgroundTask()" either - even though the internet insists on implementing it this way - as it calls the endBackgroundTask().
You need to move your self.endBackgroundTask() call into your DispatchQueue block, right after self.connectionManager.closeConnectionToPeripherals().
The registerBackgroundTask method does not directly call self.endBackgroundTask(), instead it is only called if the background task expired. Usually, this happens if your app does not complete the task after ~30s in background.
My app continues to play music even when the app is in the background and I am hoping to send the user a notification when the AVPlayer gets overridden (e.g. if the user uses another app that over rides it).
Currently my solution is to use a checkInTimer that sets up a notification and if the app does not checkIn after x amount of time, then the notification goes off, but if it does checkIn it deletes the notification and sets up another one. But this solution sucks..
Any ideas?
You need to observe audio interruptions:
import AVFoundation
func setup() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(myInterruptionHandler), name: AVAudioSessionInterruptionNotification, object: nil)
}
func myInterruptionHandler(notification: NSNotification) {
var info = notification.userInfo!
var intValue: UInt = 0
(info[AVAudioSessionInterruptionTypeKey] as! NSValue).getValue(&intValue)
if let type = AVAudioSessionInterruptionType(rawValue: intValue) {
switch type {
case .Began:
// interruption began
case .Ended:
// interruption ended
}
}
}
Hi I build a basic game with swift that has background music. The game has multiple image views. I have the music playing with with the code below. How do I stop the music if the user exits out of the app using the home button. as it is now the music continues to play even when the user presses the home button. The background music continues through out all image views which is what i want.
func playBackgroundMusic(thesong: String) {
let url = NSBundle.mainBundle().URLForResource(thesong, withExtension: nil)
guard let newURL = url else {
print("Could not find file: \(thesong)")
return
}
do {
backgroundMusicPlayer = try AVAudioPlayer(contentsOfURL: newURL)
backgroundMusicPlayer.numberOfLoops = -1
backgroundMusicPlayer.prepareToPlay()
backgroundMusicPlayer.play()
} catch let error as NSError {
print(error.description)
}
}
playBackgroundMusic("thesong.mp3")
You can use an observer in your controller like this:
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("pauseSong:"), name:UIApplicationDidEnterBackgroundNotification, object: nil)
And call this function when your controller detect when your app is running in background:
func pauseSong(notification : NSNotification) {
backgroundMusicPlayer.pause()
}
Then, when your app is opened again, add another observer to play the song:
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("playSong:"), name:UIApplicationWillEnterForegroundNotification, object: nil)
Then call this function:
func playSong(notification : NSNotification) {
backgroundMusicPlayer.play()
}
Hope it helps you ;)
You have to use this function in your controller:
func applicationDidEnterBackground(application: UIApplication) {
// 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.
backgroundMusicPlayer.pause()
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("update"), userInfo: nil, repeats: true)
}
func update() {
println("Something cool")
}
}
It's ok for the Simulator ,I will get continuous "Something cool" through I tapped the home button. But it worked out when I debug the app with my iPhone. I did't get anything when I tapped the home button and make my app run background.
Someone told me I can play a long blank music to make my App run in the background.But I don't know how to play music in the background with swift #matt
You can use beginBackgroundTaskWithExpirationHandler to get some background execution time.
class ViewController: UIViewController {
var backgroundTaskIdentifier: UIBackgroundTaskIdentifier?
override func viewDidLoad() {
super.viewDidLoad()
backgroundTaskIdentifier = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler({
UIApplication.sharedApplication().endBackgroundTask(self.backgroundTaskIdentifier!)
})
var timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "update", userInfo: nil, repeats: true)
}
func update() {
println("Something cool")
}
}
Swift 3.0
backgroundTaskIdentifier = UIApplication.shared.beginBackgroundTask(expirationHandler: {
UIApplication.shared.endBackgroundTask(self.backgroundTaskIdentifier!)
})
This code was inspired by this answer, but I ported to swift.
This apparently only runs for 3 minutes on iOS 7+.
I'm willing to bet that you don't need to run a timer in the background, what you need is to know the time that has elapsed while you were suspended. That is a much better use of your resources than trying to play an empty sound file.
Use applicationWillResignActive and applicationDidBecomeActive to determine how much time has elapsed. First save the fireDate of your timer and invalidate it in applicationWillResignActive
func applicationWillResignActive(application: UIApplication) {
guard let t = self.timer else { return }
nextFireDate = t.fireDate
t.invalidate()
}
Next, determine how much time is left for the timer in applicationDidBecomeActive by comparing the time now to the fireDate you saved in the applicationWillResignActive call:
func applicationDidBecomeActive(application: UIApplication) {
guard let n = nextFireDate else { return }
let howMuchLonger = n.timeIntervalSinceDate(NSDate())
if howMuchLonger < 0 {
print("Should have already fired \(howMuchLonger) seconds ago")
target!.performSelector(selector!)
} else {
print("should fire in \(howMuchLonger) seconds")
NSTimer.scheduledTimerWithTimeInterval(howMuchLonger, target: target!, selector: selector!, userInfo: nil, repeats: false)
}
}
If you need a repeating one, just do math to figure out how many times the timer should have fired while in the background
let howManyTimes = abs(howMuchLonger) / repeatInterval
when I tapped the home button and make my app run background
No, that's a false assumption. When you tap the home button, you do not make your app run in the background. You make your app suspend in the background. Your code does not run when you are in the background.
Apps can get special permission to run in the background just in order to perform certain limited activities, like continuing to play music or continuing to use Core Location. But your app does none of those things, so it goes into the background and stops.
add this code in appdelegate for run a task in background , its working finely,
var backgroundUpdateTask: UIBackgroundTaskIdentifier = 0
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
return true
}
func applicationWillResignActive(application: UIApplication) {
self.backgroundUpdateTask = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler({
self.endBackgroundUpdateTask()
})
}
func endBackgroundUpdateTask() {
UIApplication.sharedApplication().endBackgroundTask(self.backgroundUpdateTask)
self.backgroundUpdateTask = UIBackgroundTaskInvalid
}
func applicationWillEnterForeground(application: UIApplication) {
self.endBackgroundUpdateTask()
}