Turn off gravity when a user sends the application to the background - sprite-kit

I have a simple gravity related game in SpriteKit. Before the game starts gravity is off. When the screen is touched the following code is called:
missile?.physicsBody?.affectedByGravity = true
If you take a message and come back to it I would like gravity to switch off until you touch the screen again.
I'm guessing it is something in the AppDelegate, but I am literally stuck there.
Thank you in advance for any pointers.

First add add an observer for UIApplication.willResignActiveNotification to your mainNode like this:
NotificationCenter.default.addObserver(self, selector: #selector(appMovedToBackground), name: UIApplication.willResignActiveNotification, object: nil)
now you can pause your mainNode in appMoveToBackground:
#objc func appMovedToBackground() {
mainNode.isPaused = true
}
Make sure to remove the observer in deinit:
deinit {
NotificationCenter.default.removeObserver(self)
}

Related

How to make a button in Xcode that globally stops AVAudioPlayer?

I'm trying to make a tableviewcell stop all sounds in the app when it is clicked. I know how to stop sounds with soundEffect.stop() soundEffect.currentTime = 0, but I don't know how to apply this to a cell and this doesn't work if the StopSound button is on another viewController with a different class. How do I make a function that globally stops AVAudioPlayer and apply that to a cell?
The problem is that the player is not alone. Try this.
To stop AVAudioPlayer on another ViewController you may use NotificationCenter.
Example...
First time, add function at VC, where you need to stop AVAudioPlayer.
#objc func stopAVonThisVC() {
AVaudioPlayer.stop()
}
Second, add NotificationCenter observer at this VC, and after just post on tap button.
// At VC, where you need to stop AVAudioPlayer.
NotificationCenter.default.addObserver(self, selector:
#selector(stopAVonThisVC), name: NSNotification.Name("Stop at VC1"), object: nil)
// At VC2
NotificationCenter.default.post(name: NSNotification.Name("Stop at VC1"), object: nil)
Sorry for poor english.
Create a BaseViewController for your project and subclass all your view controllers from there,
Define the AVAudioPlayer in BaseViewController and invoke soundEffect.stop() soundEffect.currentTime = 0 from any of your subclass, it should resolve the issue.
Example:
class BaseViewController: UIViewController {
var audioPayer: AVAudioPlayer!
}
class ViewControllerA: BaseViewController {
//start or stop audio player
}
class ViewControllerB: BaseViewController {
//start or stop audio player
}

NotificationCenter actions stacking ontop of eachother?

I've got buttons that represent user posts from Firebase.
When the viewAppears I'm redrawing the circles to keep it up to date with information. However, each time I'm redrawing the circles it's messing up the actions I have associated with the buttons by adding 1 onto the amount of times the actions are called.
I have a class that takes care of adding the actions onto the button.
override init(frame: CGRect) {
super.init(frame: frame)
let doubleTap = UITapGestureRecognizer(target: self, action: #selector(self.doubleTapAction(sender:)))
self.addGestureRecognizer(doubleTap)
}
func doubleTapAction(sender : UIButton) {
print("Double tapped")
NotificationCenter.default.post(name: .didDoubleTap , object: nil, userInfo: ["tagTapped" : self.tag])
}
So that code is all done from within my "buttonPost" class.
Then on my mainVC I'm adding observers for the NotificationCenter for .didDoubleTap in viewDidLoad
NotificationCenter.default.addObserver(self, selector: #selector(self.didDoubleTapOnACircle(sender:)), name: .didDoubleTap , object: nil)
and finally I have the function that handles what I'm wanting it to do:
func didDoubleTapOnACircle(sender: Notification) {
print("double tapped action called")
}
What is happening is when I first load up the page buttons are being drawn in and work as they should. If I doubletap on a circle I get both the "Double tapped" from my class function and the "double tapped action called" from my observer function.
The problem is when I leave the viewcontroller that's in charge of drawing the circles and then I come back to it "didDoubleTapOnCircle" gets called twice, "double tapped action called" gets printed out twice, but "double tapped" gets printed out once. If I were to leave and come back to the main page (aka redrawing the circles 10 times), "double tapped action called" would be printed 10 times and "Double tapped" would still be called one time.
What is causing this? I do not understand why if I leave the view controller and come back I'm not getting duplicate buttons being drawn overtop of the old ones, but the gesture recognizer actions are being stacked ontop of eachother from the last time they were drawn in.
So the tl;dr is I'm representing user posts from firebase as buttons that can be doubletapped on the front page. When I initially load in the buttons work perfectly, the doubletapp action gets called a single time. Each time I leave and come back to the button page the amount of times the didDoubleTapOnCircle function is called is increased by 1.
I needed to remove the observer to the notification. I assumed because I was redrawing the buttons completely that this wasn't necessary but removing the observers fixed the issue.
Apple suggests handling observer listeners in viewWillAppear / Dissapear.
override func viewWillAppear(_ animated: Bool) {
NotificationCenter.default.addObserver(self, selector: #selector(self.handleTapped(sender:)), name: .didTap , object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.didDoubleTapOnACircle(sender:)), name: .didDoubleTap , object: nil)
}
override func viewWillDisappear(_ animated: Bool) {
NotificationCenter.default.removeObserver(self, name: .didTap, object: nil)
NotificationCenter.default.removeObserver(self, name: .didDoubleTap, object: nil)
NotificationCenter.default.removeObserver(self)
}

NSTimer with drawing barChart in Background

I'm using an NSTimer in an iOS App in background, which is saving some data every 30 seconds in an array. The app shows the last 10 values (values of 5 minutes) in a linechart.
My problem is to use the function of saving data into the array every 30 seconds also in background, when the app isn't on screen. I've written a lot of themes about this, but I don't understand it.
My timer is the following:
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: (#selector(ViewController.counting)), userInfo: nil, repeats: true)
func counting() {
timerCounter += 1 //Int
if timerCounter%30==0 {
arrayOfValues.append(...) //Appending the array
reloadLineChart() // reload chart
}
}
Could anyone show me how to solve this? I know, there must be something with the background-methods in the ViewController, but don't now what to type in.
I think there must be a function, which is counting in background and a function that is reloading the chart when I'm back in the app.
I might understand that you don't want to declare the counter in app delegate, for whatever reason you might have,although I would recommend it. However you can call the functions I mentioned, from the same class in which you have defined the counter. You would need to call it like this:
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(
self,
selector: "applicationWillResignActive:",
name: UIApplicationWillResignActiveNotification,
object: nil)
}
func applicationWillResignActive(notification: NSNotification) {
// stop your counter
}
Then do the same with the other function. Hope is clear.
In your app delegate you can use this method :
func applicationWillResignActive(application: UIApplication) {}
The code you add in this function will run right before your app goes in the background . Therefore you can write there the code to stop the counter.
Afterwards you need to use the following function to activate the counter again:
func applicationWillEnterForeground(application: UIApplication) {}
The code you write in this function will run when you come back to the app again. Hope it is clear enough.

Swift: Long press cancelled by movie playing. How do I persist the long press?

I'm building some functionality similar to SnapChat. Press and hold on a view, it brings up a movie to play, then returns to the main view controller when the movie finishes (currently working), or when the user picks up their finger (not working. That's what this question is about).
My problem lies in the IBAction, when the video comes up, the UIGestureRecognizerState changes from Began to Cancelled.
I'd like for the state to not change until the user lifts their finger, which should register UIGestureRecognizerState as Ended
Any tips on how to do that would be awesome. Thanks!
class ViewController: UIViewController {
var moviePlayer: MPMoviePlayerViewController!
#IBAction func handleLongPress(recognizer: UILongPressGestureRecognizer) {
println("\(recognizer)")
if recognizer.state == UIGestureRecognizerState.Began {
playVideo()
}
if recognizer.state == UIGestureRecognizerState.Ended {
self.moviePlayer.moviePlayer.stop()
}
}
func videoHasFinishedPlaying(notification: NSNotification){
println("Video finished playing")
}
func playVideo() {
// get path and url of movie
let path = NSBundle.mainBundle().pathForResource("IMG_8602", ofType:"MOV")
let url = NSURL.fileURLWithPath(path!)
moviePlayer = MPMoviePlayerViewController(contentURL: url)
presentMoviePlayerViewControllerAnimated(moviePlayer)
// construct the views
moviePlayer.view.frame = self.view.bounds
self.view.addSubview(moviePlayer.view)
// remove controls at top and bottom of video
moviePlayer.moviePlayer.controlStyle = MPMovieControlStyle.None
NSNotificationCenter.defaultCenter().addObserver(self, selector: "videoHasFinishedPlaying:",
name: MPMoviePlayerPlaybackDidFinishNotification, object: nil)
}
}
Edit: One possible solution
So this was a total fluke, but I figured out how to do this in this context.
I simply removed the line presentMoviePlayerViewControllerAnimated(moviePlayer) from my playVideo function.
Not entirely sure why that worked but it worked for my context.
This is ONE possibility but I'm open to better suggestions.
Add a 'master view' on top of where you need the touch event
Add the gesture recognizer to that view
Handle the logic within the gesture as you are now, but the subview that needs to be added needs to be added below the view thats receiving the touch.
You can now get the .ended state when the user lifts their finger.
Word of caution. If your view has other touch events make sure this 'master view' is not over top of them because it will intercept those events and hog it for its gesture (unless you set it up to recognize them simultaneously). If thats the case, you can make this 'master view' only a small port of the screen where you need to listen for the touch event without interruption

How to remove ADBannerView in Swift

I am creating a SpriteKit game with multiple scenes. I want to remove the ad once the users starts to play the actual game, then create another ad when the user transitions to the Gameover scene. Hiding the adbannerview does not work, as it will not reappear once the user loses again.
So how do you remove an AdBannerView?
Try to use a NSNotification in the GameViewController, something like this:
func hideAdBanner(notification: NSNotification) {
println("hiding banner")
adBanner.alpha = 0
adBanner.hidden = true
}
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "hideAdBanner:" , name: "hideAdBanner", object: nil)
}
Then call this NSNotification in your scene