How to change button images from another class? - swift

I´m new to coding and I want to know how to change the buton image in the menu class from the setting class.
Here is the menu class:
class Mainmenu: SKScene{
var ratingButton = SKSpriteNode(imageNamed: "RatingButton1")
}
In my setting class I want to change this image to "RatingButton2" just by clicking a button.
Here is the setting class:
class Settings: SKScene {
override func didMove(to view: SKView) {
self.backgroundColor = SKColor.white
let DCButton = SKSpriteNode(imageNamed: "ChangeButton")
DCButton.position = CGPoint(x: self.size.width * 0.2, y: self.size.height * 0.8)
DCButton.setScale(0.53)
self.addChild(DCButton)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches{
let locationUser = touch.location(in: self)
if atPoint(locationUser) == DCButton {
//Change the button image here
}
}
}
}

Swift 3+ Use Notificationcenter to update your button image.
// Define identifier
let notificationName = Notification.Name("NotificationIdentifier")
// Register to receive notification in Mainmenu
NotificationCenter.default.addObserver(self, selector: #selector(receivedNotification), name: notificationName, object: nil)
// Post notification into Settings write this line into "touchesBegan" method
NotificationCenter.default.post(name: notificationName, object: nil)
// Stop listening notification if required.
NotificationCenter.default.removeObserver(self, name: notificationName, object: nil)
Notification Handler
func receivedNotification(notification: Notification){
// Set your button image here...
}

I would not recommend using NSNotificationCenter for this task, it adds a lot of overhead for something that will be rarely used.
Now unfortunately I do not know how your layout is structured, so I am not sure what answer is best for you.
If Menu and Settings are both on the scene at the same time, then you can search the scene to find the button you are looking for:
class Mainmenu: SKScene{
var ratingButton = SKSpriteNode(imageNamed: "RatingButton1")
ratingButton.name = "ratingButton"
}
class Settings: SKScene {
override func didMove(to view: SKView) {
self.backgroundColor = SKColor.white
let DCButton = SKSpriteNode(imageNamed: "ChangeButton")
DCButton.position = CGPoint(x: self.size.width * 0.2, y: self.size.height * 0.8)
DCButton.setScale(0.53)
self.addChild(DCButton)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches{
let locationUser = touch.location(in: self)
if atPoint(locationUser) == DCButton {
let button = scene.childNode(withName:"//ratingButton") as! SKSpriteNode
button.texture = SKTexture(imageNamed:"RatingButton2")
}
}
}
}
If it is not on the scene and you are presenting it, then you want to use userData to assign what your button is before hand
let menu = Mainmenu(...)
menu.userData = ["ratingButton":SKTexture(imageNamed:textureName)] //texture name is either Ratingbutton1 or RatingButton2 depending on state
self.view.presentScene(menu)
Then in the viewDidLoad, just read the userData
func viewDidLoad(...)
{
ratingButton.texture = userData?["ratingButton"]
}

Related

Changing Scenes In Spritekit, Detecting Touches on Buttons

I am attempting to change from a home scene to a game scene in Spritekit. When I run it in the simulator, there is no transition to the GameScene. I also added a print command to see if the touches on the play button were even being registered, and they were not. Here's the code. Thanks!
import SpriteKit
class HomeScene: SKScene {
var playButton: SKSpriteNode?
var gameScene: SKScene!
override func didMove(to view: SKView) {
playButton = self.childNode(withName: "startButton") as? SKSpriteNode
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let pos = touch.location(in: self)
let node = self.atPoint(pos)
if node == playButton {
let transition = SKTransition.fade(withDuration: 1)
gameScene = SKScene(fileNamed: "GameScene")
gameScene.scaleMode = .aspectFit
self.view?.presentScene(gameScene, transition: transition)
print("touch")
}
}
}
}
The touches parameter inside of your touchesBegan(_: with:) method is of type Set<UITouch>, i.e. a set of touches. As stated in the Apple Documentation for Sets, a set is used when the order of the items in the collection are not a concern. Therefore, you cannot assume that the first item in touches is the first touch in the set. Knowing this, I recommend refactoring your code inside of HomeScene.swift to the following:
import SpriteKit
class HomeScene: SKScene {
private var playButton: SKSpriteNode?
override func sceneDidLoad() {
self.playButton = self.childNode(withName: "//starButton") as? SKSpriteNode
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches {
self.touchDown(atPoint: t.location(in: self))
}
}
func touchDown(atPoint pos: CGPoint) {
let nodes = self.nodes(at: pos)
if let button = playButton {
if nodes.contains(button) {
let gameScene = SKScene(fileNamed: "GameScene")
self.view?.presentScene(gameScene)
}
}
}
The touchDown(atPoint:) method does all the heavy lifting and touchesBegan(_: with:) is freed up to listen for touch events. Another thing to note when calling childNode(withName:):
Adding / before the name of the file starts searching for nodes beginning at the root of your file hierarchy.
Adding // before the name of the file starts searching for nodes beginning at the root of your file hierarchy, then recursively down the hierarchy.
Adding * before the name of the file acts as a character wildcard and allows you to search for file names that are wholly or partially unknown.
I think you should use the node's name instead of the node itself for comparison in the touchesBegan... try something like this:
import SpriteKit
class HomeScene: SKScene {
var playButton: SKSpriteNode?
var gameScene: SKScene!
override func didMove(to view: SKView) {
playButton = self.childNode(withName: "startButton") as? SKSpriteNode
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let pos = touch.location(in: self)
let node = self.atPoint(pos)
if node.name == "startButton" {
let transition = SKTransition.fade(withDuration: 1)
gameScene = SKScene(fileNamed: "GameScene")
gameScene.scaleMode = .aspectFit
self.view?.presentScene(gameScene, transition: transition)
print("touch")
}
}
}
}
Hope it helps! :)

Button is still working even when removeFromParent() in Swift SpriteKit is called

In the code below if I press the removeButton the button would disappear but it's still working. Maybe I should make the if statement false but I don't know how.
class GameScene : SKScene {
var button : SKSpriteNode!
var removeButton : SKSpriteNode!
var image : SKSpriteNode!
override func didMove(to view: SKView) {
createButton()
createRemoveButton()
}
func createButton() {
button = SKSpriteNode(imageNamed: "button")
button.position = CGPoint(x: -300,y: 0)
self.addChild(button)
}
func createRemoveButton() {
removeButton = SKSpriteNode(imageNamed: "removeButton")
removeButton.position = CGPoint(x: 300,y: 0)
self.addChild(removeButton)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
let touchLocation = touch!.location(in: self)
if button.contains(touchLocation) {
image = SKSpriteNode(imageNamed: "image")
image.position = CGPoint(x: 0,y: 300)
self.addChild(image)
}
if removeButton.contains(touchLocation) {
button.removeFromParent()
}
}
override func update(_ currentTime: TimeInterval) {
}
}
You need to understand ARC (Automatic Reference Counting) and what it takes to retain an instance.
In your case you are retaining button, which means it does not delete from memory.
Now when you remove the button from the parent, the button's frame is still exactly the same, the only thing different is button.parent is now nil
When you call button.contains(touchLocation), this is going to pass because you are not concerned with whether or not button is on the scene, all you are doing is checking if the touchLocation is within the frame of the button.
The quickest fix is to check the parent:
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
let touchLocation = touch!.location(in: self)
if button.parent == self && button.contains(touchLocation) {
image = SKSpriteNode(imageNamed: "image")
image.position = CGPoint(x: 0,y: 300)
self.addChild(image)
}
if removeButton.contains(touchLocation) {
button.removeFromParent()
}
}
In reality though, you need to learn how to better manage your resources. I would recommend trying to find tutorials about how ARC works.
It is normal because you dont use the good button. You use button instead of removeButton.
You should do:
if removeButton.contains(touchLocation) {
removeButton.removeFromParent()
}
Now it should work correctly

SpriteKit: How to identify the previous scene the user is coming from?

I have a GameOver scene with a button to a Settings scene. (Code shown below) I want to add a button on the MainMenu scene to do the same, send the user to the Settings scene but I want just one “Back” button on the Settings scene. I'm assuming this can be accomplished with an IF statement. If the user comes from A scene move to B else if the user comes from X scene move to Y scene.
How can I tell in SpriteKit which scene is the user coming from?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches {
let pointOfTouch = touch.location(in: self)
let nodeUserTapped = atPoint(pointOfTouch)
if nodeUserTapped.name == "BackButton" {
let sceneToMoveTo = SKScene(fileNamed: "GameOverScene")
let gameTransition = SKTransition.fade(withDuration: 0.5)
self.view!.presentScene(sceneToMoveTo!, transition: gameTransition)
}
}
}
In this case you can adopt various solutions to achieve what you wish.
I want to show you just 2 example, a rapid and simple way and a more custom way.
1: - You can use the userData instance property of your next scene to store the current scene, something like:
sceneToMoveTo.userData = NSMutableDictionary()
sceneToMoveTo.userData?.setObject("MenuScene", forKey: "previousScene" as NSCopying)
and when you are in the next scene you can ask to this NSMutableDictionary who is the previous scene as:
guard let previousValue = self.userData?.value(forKey: "previousScene") else { return }
switch previousValue as! String {
case "MenuScene":
//do whatever you want if the previous scene was MenuScene
default:
break
}
2: - You can build an enumeration that contain a list of the scenes where you want to move.
After that, each scene contain a simple var to store the previous scene: it's default is .None but you should fill it when you call the new scene. When you arrive to the SettingsScene and press your back button you can ask to this previousScene var who is the previous scene and decide, through a switch statement, where to go.
// GAMEOVER SCENE
import SpriteKit
enum Scenes : Int {
case MainMenu = 0
case Settings = 1
case GameOver = 2
case None = 3
}
class GameOver: SKScene {
var previousScene:Scenes = .None
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches {
let pointOfTouch = touch.location(in: self)
let nodeUserTapped = atPoint(pointOfTouch)
if nodeUserTapped.name == "settingsBtn" {
if let sceneToMoveTo = SKScene(fileNamed: "SettingsScene") {
let gameTransition = SKTransition.fade(withDuration: 0.5)
(sceneToMoveTo as! SettingsScene).previousScene = .GameOver
self.view?.presentScene(sceneToMoveTo, transition: gameTransition)
}
}
}
}
}
// SETTINGS SCENE
import SpriteKit
class SettingsScene: SKScene {
var previousScene:Scenes = .None
override func didMove(to view: SKView) {
print("Current scene loaded: \(type(of:self))")
print("Previous scene was: \(previousScene)")
let label = SKLabelNode.init(text: "BackButton")
addChild(label)
label.position = CGPoint(x:self.frame.midX,y:self.frame.midY)
label.name = "backBtn"
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches {
let pointOfTouch = touch.location(in: self)
let nodeUserTapped = atPoint(pointOfTouch)
if nodeUserTapped.name == "backBtn" {
switch previousScene {
case .MainMenu: // go back to menu
if let sceneToMoveTo = SKScene(fileNamed: "MenuScene") {
let gameTransition = SKTransition.fade(withDuration: 0.5)
(sceneToMoveTo as! MenuScene).previousScene = .Settings
self.view?.presentScene(sceneToMoveTo, transition: gameTransition)
}
break
case .GameOver: // re-launch a new game
print("The game was ended")
if let sceneToMoveTo = SKScene(fileNamed: "GameScene") {
let gameTransition = SKTransition.fade(withDuration: 0.5)
(sceneToMoveTo as! GameScene).previousScene = .Settings
self.view?.presentScene(sceneToMoveTo, transition: gameTransition)
}
default:
break
}
}
}
}
}
//MENU SCENE
import SpriteKit
class MenuScene: SKScene {
var previousScene:Scenes = .None
override func didMove(to view: SKView) {
print("Current scene loaded: \(type(of:self))")
print("Previous scene was: \(previousScene)")
}
}
userData is your friend here, it is a dictionary that can be found on any type of SKNode. All you need to do is store the current scene you are in into the userData, and make the transition. This will keep your current scene in memory. Since your current scene is in memory and is not a part of the new scenes node tree, no update method will be called on it. Essentially you have a frozen scene state. Then when you are ready to return to the scene, just pull the instance out of the user data, and present it. Viola, you have transitioned back to your scene the moment that you have left it previously.
Basically, if you want the scene to resume where it left off, do the following:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches {
let pointOfTouch = touch.location(in: self)
let nodeUserTapped = atPoint(pointOfTouch)
if nodeUserTapped.name == "SettingButton" {
let sceneToMoveTo = SKScene(fileNamed: "SettingScene")
sceneToMoveTo.userData = ["previousScene":self.scene]
let gameTransition = SKTransition.fade(withDuration: 0.5)
self.view!.presentScene(sceneToMoveTo!, transition: gameTransition)
}
}
}
Then, at any point you want to return to the previous scene, just add the following.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches {
let pointOfTouch = touch.location(in: self)
let nodeUserTapped = atPoint(pointOfTouch)
if nodeUserTapped.name == "BackButton" {
if let userData = self.userData, let previousScene = userData["previousScene"] as? SKScene
{
let gameTransition = SKTransition.fade(withDuration: 0.5)
self.view!.presentScene(previousScene, transition: gameTransition)
}
}
}
}

Replay button doesn't work. (Swift, SpriteKit)

Once my game ends, it displays a button for replay, but I can't understand how to let Xcode know if there is a touch after the game has ended. My replay button's code is in didBeginContact Method and is as follows:
func didBeginContact(contact: SKPhysicsContact) {
if (.....) {
..........
} else {
replayButton = SKSpriteNode(imageNamed: "ReplayButton")
replayButton.position = CGPoint(x: size.width / 1.75, y: size.height / 2.5)
replayButton.name = "replayButton"
self.addChild(replayButton)
}
New Swift file:
class button: SKSprideNode {
let replayButton = button(imageNamed: "replayButton")
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
var touch = touches as! Set<UITouch>
var location = touch.first!.locationInNode(self)
var node = self.nodeAtPoint(location)
if (node.name == "replayButton") {
let myScene = EMode(size: self.size)
let reveal = SKTransition.fadeWithDuration(2.0)
self.scene!.view(myScene, transition: reveal) //error on this line
}
}
}
You need to set the replay button's user interaction enabled to true like this:
replayButton.userInteractionEnabled = true
Then you just need to listen for when the button or scene is touched. One way to do that is make a subclass of SKSpriteNode called Button or something, and override the touchesBegan method.
EXAMPLE:
ButtonClass
class ExampleButton: SKSpriteNode {
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
//do stuff here
}
}
Button Initialization
let button = ExampleButton(imageNamed: "ButtonImage")
button.userInteractionEnabled = true

Presenting UIViewController from SKScene?

So at the beginning I was having trouble implementing social media sharing into my project using Swift/Sprite-Kit. I believe I have now accomplished that but I just cannot trigger the methods, or as it seems everybody's struggle, I cannot present the view controller from my SKScene. So what I first did, was to create my buttons on my EndGame Scene. Then on my touchesEnded method I called the NSNotificationCenter. Something like this:
import SpriteKit
import UIKit
import Social
class EndGameScene: SKScene {
var facebookButton: SKNode! = nil
var twitterButton: SKNode! = nil
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(size: CGSize) {
super.init(size: size)
// FacebookButton
facebookButton = SKSpriteNode(imageNamed: "facebookButton")
facebookButton.position = CGPoint(x: self.size.width * 0.3 , y: self.size.height * 0.45);
addChild(facebookButton)
// TwitterButton
twitterButton = SKSpriteNode(imageNamed: "twitterButton")
twitterButton.position = CGPoint(x: self.size.width * 0.5 , y: self.size.height * 0.45);
addChild(twitterButton)
}
override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if facebookButton.containsPoint(location) {
NSLog("Button tapped")
NSNotificationCenter.defaultCenter().postNotificationName("FacebookID", object: nil)
}
if twitterButton.containsPoint(location) {
NSLog("Button tapped")
NSNotificationCenter.defaultCenter().postNotificationName("TwitterID", object: nil)
}
}
}
Then I created my SocialViewController with the methods that are supposed to be triggered with help of the NSNotificationCenter to show the sheets. This is the code I used for it:
import UIKit
import Social
class SocialViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "showTweetSheet", name: "TwitterID", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "showFacebookSheet", name: "FacebookID", object: nil)
}
func showTweetSheet() {
let tweetSheet = SLComposeViewController(forServiceType: SLServiceTypeTwitter)
tweetSheet.completionHandler = {
result in
switch result {
case SLComposeViewControllerResult.Cancelled:
break
case SLComposeViewControllerResult.Done:
break
}
}
tweetSheet.setInitialText("Test Twitter")
tweetSheet.addImage(UIImage(named: "TestImage.png"))
tweetSheet.addURL(NSURL(string: "http://twitter.com"))
self.presentViewController(tweetSheet, animated: false, completion: {
})
}
func showFacebookSheet() {
let facebookSheet = SLComposeViewController(forServiceType: SLServiceTypeFacebook)
facebookSheet.completionHandler = {
result in
switch result {
case SLComposeViewControllerResult.Cancelled:
break
case SLComposeViewControllerResult.Done:
break
}
}
facebookSheet.setInitialText("Test Facebook")
facebookSheet.addImage(UIImage(named: "TestImage.png"))
facebookSheet.addURL(NSURL(string: "http://facebook.com"))
self.presentViewController(facebookSheet, animated: false, completion: {
})
}
I literally have no idea on what I might be missing or doing wrong. I've read several posts on this and using the NSNotificationCenter is supposed to be the best way to accomplish this but seems is not working for me. So if there is anyone there that could help me out I would really appreciate it. Thanks in advance!
You can download the Facebook Sdk from https://github.com/facebook/facebook-android-sdk. And after create the key hash using your cmd prompt. For more details for create the key hash see the link https://developers.facebook.com/docs/android/getting-started