cancelling a tap gesture recongiser - ios

Referring to my last question:
Sprite moves two places after being paused and then unpaused
Hi, I have a tap gesture which moves a sprite in my game forward 1 space and when I press the pause button it continues to register the tap gesture and then when I resume the gameplay It moves two spaces.
so I managed to define a bool variable that detects (using if statements) if I have paused the tap gesture
var tapIsPaused: Bool = false
func tapUp(){
if(tapIsPaused == true) {
//do nothing
} else if (tapIsPaused == false) {
let amountToMove:CGFloat = levelUnitHeight
let move:SKAction = SKAction.moveByX(0, y: amountToMove, duration: 0.1)
menubutton.hidden = true
settingsButton.hidden = true
highscoreLabel.hidden = true
pauseButton.hidden = false
thePlayer.runAction(move)
clearNodes()
}
}
But the problem I have now is that when I press the resume button to resume the gameplay it still moves the sprite, but this time it's only moving one space up, which is because when I press the resume button it turns the tap on which then registers the tap of the resume button to move the player up.
How can I fix this?
Here is my pause button:
else if (node == pauseButton) {
tapIsPaused = true
pauseButton.removeFromParent()
addChild(resumeButton)
addChild(restartButton)
self.runAction (SKAction.runBlock(self.pauseGame))
}
Here is my resume button:
else if (node == resumeButton) {
resumeButton.removeFromParent()
restartButton.removeFromParent()
addChild(pauseButton)
self.runAction (SKAction.runBlock(self.resumeGame))
tapIsPaused = false
}
Here is my tap gesture handler code:
let TapUpRec = UITapGestureRecognizer()
TapUpRec.addTarget(self, action: "tapUp")
self.view!.addGestureRecognizer(TapUpRec)

You can remove Gesture on Pause click using following:
self.view.removeGestureRecognizer(YOUR_GESTURE_RECOGNISER)
and add it again if resume game

Modify your resume function as:
else if (node == resumeButton) {
resumeButton.removeFromParent()
restartButton.removeFromParent()
addChild(pauseButton)
tapIsPaused = false
self.runAction (SKAction.runBlock(self.resumeGame))
}

Very simple and easiest way.No need to add or remove Gesture.
You can do it with enable or disable your gesture.
For swift 2.3
TapUpRec.enabled = false //pause click
TapUpRec.enabled = true //resume click
For swift 3.0
TapUpRec.isEnabled = false //pause click
TapUpRec.isEnabled = true //resume click

Related

Create pause menu in Spritekit

I am trying to create a pause menu on the game layer. I'm doing this just by adding two buttons on top when the pause button is pressed so the player can still see the stage of the game
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
if atPoint(location).name == "pause" {
let button: SKSpriteNode?
let button2: SKSpriteNode?
button = SKSpriteNode(imageNamed: "button_continue")
button?.name = "button_continue"
button?.size.height = 55
button?.size.width = 215
button?.physicsBody = SKPhysicsBody(rectangleOf: button!.size)
button?.physicsBody?.allowsRotation = false
button?.position = CGPoint(x: 0, y: 160)
button?.zPosition = 100
self.addChild(button!)
button2 = SKSpriteNode(imageNamed: "button_finish")
button2?.name = "button_finish"
button2?.size.height = 55
button2?.size.width = 215
button2?.physicsBody = SKPhysicsBody(rectangleOf: button2!.size)
button2?.physicsBody?.allowsRotation = false
button2?.position = CGPoint(x: 0, y: -50)
button2?.zPosition = 100
self.addChild(button2!)
// The two buttons are created but here I am trying to stop everything until the continue button is pressed and I don't find a way
}
}
}
I have tried sleep() or timer.invalidate() but neither of them work with if statements, and I can't use while loops because then buttons don't appear until the while loop finishes:
while atPoint(location).name != "button_cotinue" {
timer1.invalidate()
}
That is what I tried and doesn't work.
Then when the continue button is pressed I will also delete the buttons but I can code that correctly. As well the finish button sends the player to the main menu scene.
what I do is put the controls (buttons) in a layer named controlsLayer that is separate from all of the game play items such as player, enemies and obstacles. Then I put all of the game items that I want to be paused in a layer named gameLayer. I make the controlsLayer have a high zPosition like 100.
then when I click my pause button I call gameLayer.isPaused = true and when I click continue I would call the opposite. gameLayer.isPaused = false. by just pausing the gameLayer, you can still run other actions like transitions or effects on your pause buttons. If you pause the whole scene you won't be able to run any actions or effects on any items.

UIView.animate not setting image and label alphas on VC change

EDIT: I figured out I need to resume the layer if its paused, but am not sure how to resume the layer when the view returns. viewDidLoad doesn't seem to trigger on segue
I have an app with an animation, sound, and a webView. The animation is triggered by a button and a segue to the webView is triggered by another button. If the animation is playing when the app segues to the webView, once the app segues back, I have it set so the animation restarts when the play button is tapped. This works fine
However, when the animation is paused and the view changes, upon returning to the main view, the image alpha and label alpha doesn't appear to animate... although the debugger says the alpha is changing appropriately, nothing appears on screen.
How can I get it to animate appropriately when play is tapped after returning from the WebView?
Here's the play button:
touch += 1
//define animated layers
imgLayer = firstImg.layer
labelLayer = firstLbl.layer
if touch == 1 {
print("touch = \(touch)")
self.firstImg.alpha = 0
self.firstLbl.text = ""
animateStart()
player.play()
self.playOut.setBackgroundImage(UIImage(named: "pause.png"), for: UIControlState.normal)
} else {
touch = 2
}
//toggle pause
if touch == 2 {
print("touch = \(touch)")
pause = !pause
if pause {
animatePause()
player.pause()
} else {
animateResume()
player.play()
}
}
}
and the animate start block:
func animateStart() {
print("animation started")
UIView.animate(withDuration: 4, animations: {
self.firstImg.alpha = 1
print("firstImg alpha = \(self.firstImg.alpha)")
self.firstImg.image = UIImage(named:"peter.JPG")
self.view.layoutIfNeeded()
}, completion: { finished in
if finished {
self.animateSecond()
}
})
}
and here's how I dismiss the VC on segue
func dismissVC() {
touch = 0
self.playOut.setBackgroundImage(UIImage(named: "playbtn.png"), for: UIControlState.normal)
player.stop()
initAudio()
self.dismiss(animated: true, completion: nil)
}

Move UISlider knob to middle while double tap on it

I want to move the UISlider knob to the middle, or 0.0 value in my case, when the double tap event is occurred on it.
Knob is moving to 0.0 on double tap but immediately it resume to the old position/value.
I have tried the below code. Any help would be much appreciated.
override func viewDidLoad() {
super.viewDidLoad()
let eqSlider = UISlider(frame:CGRectMake(0, 0, 160, 40))
eqSlider.minimumValue = -12.0
eqSlider.maximumValue = 12.0
eqSlider.value = 0.0
self.view.addSubview(eqSlider)
// detecting double tap on slider method
eqSlider.addTarget(self, action: #selector(EQViewController.doubleTappedSlider(_:event:)), forControlEvents: .TouchDownRepeat)
}
func doubleTappedSlider(sender: UISlider, event: UIEvent) {
if let firstTouchObj = event.allTouches()?.first {
if 2 == firstTouchObj.tapCount {
sender.value = 0.0
}
}
}
The problem is that the two taps in your double tap will also be acted upon by the slider itself, so after you set its position manually, the user is setting it straight back.
To avoid this, you can add the following line after you set the .value:
sender.cancelTracking(with: nil)
This will "cancel any ongoing tracking" as stated in the documentation.

Draggable UIButton/Elements in Swift?

this is my first question! I was just wondering, in Swift (specifically Swift 2, although that may go without saying!), how you create a button that the user can drag around. So for example, if it is a UIButton, the user can tap and hold it, and when they move their finger, the UIButton moves with it, and when they release it, it remains in the position that the user left it. Potentially there could be a snapping system but this is unimportant for now.
I've searched StackOverflow and found some quite interesting things, however it's all for Objective-C, and although Swift is pretty similar in some respects, I can't figure out in the slightest as to how to implement this in Swift.
It would be massively appreciated for a project that I am working on!
Thank you very much!
You can implement UIPanGestureRecognizer on your UIButton.
Wherever you create your button (viewDidLoad if using outlets):
let pan = UIPanGestureRecognizer(target: self, action: "panButton:")
button.addGestureRecognizer(pan)
This creates a new pan gesture recognizer and adds it to the button. Now, you'll want to implement the pan's action. First, you need to store the center of the button to be able to reset it when you finish panning. Add this as a view controller property:
var buttonCenter = CGPointZero
Then you implement the pan action. Note that you can use gesture recognizer states to determine when the pan starts and ends:
func panButton(pan: UIPanGestureRecognizer) {
if pan.state == .Began {
buttonCenter = button.center // store old button center
} else if pan.state == .Ended || pan.state == .Failed || pan.state == .Cancelled {
button.center = buttonCenter // restore button center
} else {
let location = pan.locationInView(view) // get pan location
button.center = location // set button to where finger is
}
}
Swift 4 & 5 Version of accepted answer:
var buttonCenter: CGPoint = .zero
viewDidLoad() {
super.viewDidLoad()
let pan = UIPanGestureRecognizer(target: self, action: #selector(YourViewController.panButton(pan:)))
button.addGestureRecognizer(pan)
}
#objc func panButton(pan: UIPanGestureRecognizer) {
if pan.state == .began {
buttonCenter = button.center // store old button center
} else if pan.state == .ended || pan.state == .failed || pan.state == .cancelled {
button.center = buttonCenter // restore button center
} else {
let location = pan.location(in: view) // get pan location
button.center = location // set button to where finger is
}
}
Basically, you want to implement a touch gesture recognizer and set the button's center to the center of your press when you tap/move said button.
Here's how you'll want to do that: https://stackoverflow.com/a/31487087/5700898
Also, really cool idea!

Sprite moves two places after being paused and then unpaused

I am having trouble figuring out the solution to this and am starting to get very frustrated with it.
I have a pause button and an unpause button in my game scene to allow the player to pause the game, which is the following code
else if (node == pauseButton) {
pauseButton.removeFromParent()
addChild(unpauseButton)
addChild(restartButton)
self.runAction (SKAction.runBlock(self.pauseGame))
}
func pauseGame(){
pauseButton.hidden = true
unpauseButton.hidden = false
scene!.view!.paused = true // to pause the game
}
Problem
The problem is that when I pause the game then unpause the game my player sprite seems to move two spaces forward automatically
I also have a tap and swipe gesture that allows me to move the player up left right and down when I tap anywhere on the screen.
func tapUp(){
let amountToMove:CGFloat = levelUnitHeight
let move:SKAction = SKAction.moveByX(0, y: amountToMove, duration: 0.1)
menubutton.hidden = true
settingsButton.hidden = true
highscoreLabel.hidden = true
pauseButton.hidden = false
thePlayer.runAction(move)
}
func swipedRight(){
let amountToMove:CGFloat = levelUnitHeight
let move:SKAction = SKAction.moveByX(amountToMove, y: 0, duration: 0.1)
thePlayer.runAction(move) // links the action with the players
}
As the member above me said the player is not really moving 2 spaces.
Also you should maybe change your strategy when pausing your game, because pausing the scene.view makes it very hard to add SpriteKit elements afterwards.
I think a better way is to create a worldNode in your GameScene and add all the sprites that need to be paused to that worldNode. It basically gives you more flexibility pausing a node rather than the whole scene.
First create a world node property
let worldNode = SKNode()
and add it to the scene in ViewDidLoad
addChild(worldNode)
Than add all the sprites you need paused to the worldNode
worldNode.addChild(sprite1)
worldNode.addChild(sprite2)
...
Create a global enum for your game states
enum GameState {
case Playing
case Paused
case GameOver
static var current = GameState.Playing
}
Than make a pause and resume func in your game scene
func pause() {
GameState.current = .Paused
// show pause menu etc
}
func resume() {
GameState.current = .Playing
self.physicsWorld.speed = 1
worldNode.paused = false
}
And finally add the actual pause code to your updateMethod. This way it will not resume the game even if spriteKit itself tries to resume (e.g. reopened app, dismissed alert etc)
override func update(currentTime: CFTimeInterval) {
if GameState.current == .Paused {
self.physicsWorld.speed = 0
worldNode.paused = true
}
}
In regards to your tapGesture recogniser, in the method that gets called after a tap you can add this before the rest of the code
guard GameState.current != .Paused else { return }
....
Hope this helps

Resources