Swift access methods of touched Node - ios

Im dragging a SKSpriteNode around with the following method
override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
for touch in touches {
let location = touch.locationInNode(self)
var touchedNode = nodeAtPoint(location)
if touchedNode.isKindOfClass(Card) {
touchedNode.position = location
}
}
}
the if statement is confirming that it;s pif a specific class, how can I now call methods and access properties this node?

You can use this syntax:
if let cardNode = touchedNode as Card {
cardNode.position = location
// cardNode is of type card. You can access all its
// methods and properties
}

Related

How to detect precisely when a SKShapeNode is touched?

I'm working with Swift and SpriteKit.
I have the following situation :
Here, each of the "triangles" is a SKShapenode.
My problem is that I would like to detect when someone touches the screen which triangle is being touched.
I assume that the hitbox of all these triangles are rectangles so my function returns me all the hitboxes touched while I only want to know which one is actually touched.
Is there any way to have a hitbox that perfectly match the shape instead of a rectangle ?
Here's my current code :
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)
{
let touch = touches.first
let touchPosition = touch!.locationInNode(self)
let touchedNodes = self.nodesAtPoint(touchPosition)
print(touchedNodes) //this should return only one "triangle" named node
for touchedNode in touchedNodes
{
if let name = touchedNode.name
{
if name == "triangle"
{
let triangle = touchedNode as! SKShapeNode
// stuff here
}
}
}
}
You could try to use CGPathContainsPoint with a SKShapeNode instead of nodesAtPoint, which is more appropriate:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)
{
let touch = touches.first
let touchPosition = touch!.locationInNode(self)
self.enumerateChildNodesWithName("triangle") { node, _ in
// do something with node
if node is SKShapeNode {
if let p = (node as! SKShapeNode).path {
if CGPathContainsPoint(p, nil, touchPosition, false) {
print("you have touched triangle: \(node.name)")
let triangle = node as! SKShapeNode
// stuff here
}
}
}
}
}
This would be the easiest way of doing it.
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)
{
for touch in touches {
let location = touch.locationInNode(self)
if theSpriteNode.containsPoint(location) {
//Do Whatever
}
}
}
The way I do this with Swift 4:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else {
return
}
let touchPosition = touch.location(in: self)
let touchedNodes = nodes(at: touchPosition)
for node in touchedNodes {
if let mynode = node as? SKShapeNode, node.name == "triangle" {
//stuff here
mynode.fillColor = .orange //...
}
}
}

Touch location inside an object

I when I try using the following line of code:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let tap = touch as UITouch
let location = tap.locationInView(self.view)
print(location(
}
I can not get the location of the touch inside a picker view. Is there a way to do it?
this is how i do it..First you need to assign your object a name and then in touchesBegan you need to specify that the location of the touch is the same with the object
let location = (touch as! UITouch).locationInNode(self)
if let name = self.nodeAtPoint(location).name {
if name == "objectName" {
// do your code here
}

Detect Touches on SKNode in Array

I have the following function which spawns squares and adds them to an array of squares. This adds new squares indefinitely until the function is stopped. The array of squares is declared in the SKScene like so: var rsArray = [RedSquare]().
func spawnRedSquares() {
if !self.gameOver {
let rs = RedSquare()
var rsSpawnRange = self.frame.size.width/2
rs.position = CGPointMake(rsSpawnRange, CGRectGetMaxY(self.frame) + rs.sprite.size.height * 2)
rs.zPosition = 3
self.addChild(rs)
self.rsArray.append(rs)
let spawn = SKAction.runBlock(self.spawnRedSquares)
let delay = SKAction.waitForDuration(NSTimeInterval(timeBetweenRedSquares))
let spawnThenDelay = SKAction.sequence([delay, spawn])
self.runAction(spawnThenDelay)
}
}
I'm trying to use the touchesBegan() function to detect when a specific square in the array is tapped and then access the properties of the square. I can't figure out how to determine which square is being touched. How would I go about doing this?
Give each of the squares you spawn a unique name and check that name in touchesBegan. You can use a counter and do
rs.name = "square\(counter++)"
In touchesBegan you can retrieve the name of the touched node and check it against the names of the nodes in the array.
First you have to give the rs node a name. For example
rs.name = "RedSquare"
Then you can use the nodeAtPoint function to find the node at a particular touch point. If the node is a RedSquare, you can modify it.
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch in touches {
let touchPoint = touch.locationInNode(self)
let node = self.nodeAtPoint(touchPoint)
if node.name == "RedSquare" {
// Modify node
}
}
}
I was able to answer my own question by experimenting, and decided that I would post the answer in case anyone else had a similar problem. The code that I needed inside the touchesBegan() function was the following:
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
let rsCurrent = self.nodeAtPoint(location)
for RedSquare in rsArray {
let rsBody = RedSquare.sprite.physicsBody
if rsBody == rsCurrent.physicsBody? {
//Action when RedSquare is touched
}
}
}
}

(Swift + Spritekit) - remove node and its data entirely

I'm working a little game for iOS.
I've a SKSpriteNode in my scene - when I remove it with "removeFromParent" and touch the area it was last in, I still get the function.
My code is as following:
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if tapToPlayNode.containsPoint(location){
tapToPlayNode.removeFromParent()
startNewGame()
}
}
}
func startNewGame(){
//Starts a new game with resetted values and characters in position
println("Ready.. set.. GO!")
//Shows the ui (value 1)
toggleUiWithValue(1)
}
In other words, I get "Ready.. set.. GO!" output when I touch the area even after it was deleted.
Any clues?
Bests,
Your tapToPlayNode is still retained by self and is removed from it's parent.
You should make it optional var tapToPlayNode:SKSpriteNode?and nil it after remove it from it's parent like this:
if let playNode = self.tapToPlayNode {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if playNode.containsPoint(location) {
playNode.removeFromParent()
startNewGame()
self.tapToPlayNode = nil // il it here!
break
}
}
}
You can also avoid to keep a reference of your tapToPlayNode and give it a name when initializing it like this:
node.name = #"tapToPlayNodeName"
// Add node to scene and do not keep a var to hold it!
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
// Retrieve the ode here
let tapToPlayNode = container.childNodeWithName("tapToPlayNodeName")!
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if tapToPlayNode.containsPoint(location){
tapToPlayNode.removeFromParent()
startNewGame()
}
}
}

touchesMoved when two nodes across swift

I had a problem with movning some nodes around but I found a way (finally) but then I faced a problem which is : when two nodes gets across the touch will move from one node to the other ! but what I want is when I touch a node I will move it until I move my finger from the screen
here is my code :
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
var location = touch.locationInNode(self)
let node = nodeAtPoint(location)
if node == firstCard{
firstCard.position = location
}else if node == secondCard{
secondCard.position = location
println("Second Card Location")
println(secondCard.position)
}else{
println("Test")
}
}
}
You need to keep track of the SKNode the finger touched in touchesBegan
First thing to remember is the the same UITouch object is returned for each finger in touchesMoved with different locations. So we can keep track of the each touch in the node using a touchTracker dictionary.
var touchTracker : [UITouch : SKNode] = [:]
Also give a name to each card, so that we can keep track of the nodes that need to be moved. For example.
card1.name = "card"
card2.name = "card"
In touchesBegan, we will add the node that is under the touch to the touchTracker.
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch in touches {
let location = touch.locationInNode(self)
let node = self.nodeAtPoint(location)
if (node.name == "card") {
touchTracker[touch as UITouch] = node
}
}
}
When a touch is moved inside touchesMoved, we will get the node back from the touchTracker and update its position.
override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
for touch in touches {
let location = touch.locationInNode(self)
let node = touchTracker[touch as UITouch]
node?.position = location
}
}
In touchesEnded, we update the final position again, and remove the touch key-value pair from touchTracker.
override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
for touch in touches {
let location = touch.locationInNode(self)
let node = touchTracker[touch as UITouch]
node?.position = location
touchTracker.removeValueForKey(touch as UITouch)
}
}

Resources