IOS w/ SpriteKit How to edit a pre-existing label? - ios

I made a label in my LaunchScreen.sks file, named it touchLabel, and I can't seem to find a method for accessing it anywhere.
So how do I set a "let" equal to the label that I created in my .sks file, I know how to create one I just can't figure out how to access one I already created.
I figured the code would be something along the lines of:
let touchLabel = SKLabelNode(named: "touchLabel")
but "named:" is not an available overload for that function.
I remembered to set the name of the label in the .sks file to touchLabel and also remembered to set the parent to LaunchScreen.swift
Also Im really sorry if this is super obvious I am new to swift and I've looked for a solution for at least an hour, but I have only found pages on how to create labels from the .swift file. Not how to access labels that have already been created.

You can use childNode(withName:) to find elements in the sks file.
var touchLabel: SKLabelNode!
override func didMove(to view: SKView) {
guard let touchLabel = childNode(withName: "touchLabel") as? SKLabelNode else {
fatalError("touchLabel node not loaded")
}
self.touchLabel = touchLabel
}

Related

Why is SKSpriteNode(fileNamed:) generating nil in iOS using Swift?

Why is init(fileNamed:) of SKSpriteNode generating a nil?
I've tried the following code. I show only the code that is related to the problem:
let road = SKSpriteNode(fileNamed: "road.png")
override func didMove(to view: SKView) {
print("road", road as Any) // road nil
if let road = self.road {
road.position = view.center
road.physicsBody = SKPhysicsBody(rectangleOf: road.size)
print(road.physicsBody?.isDynamic as Any, "!")
road.physicsBody?.pinned = true
addChild(road)
}
}
I get a nil regardless of whether the image file is a regular png or an animated png file.
You should use SKSpriteNode(imageNamed:) instead of SKSpriteNode(fileNamed:).
SKSpriteNode(imageNamed:) - loads image as texture for you and creates a node.
SKSpriteNode(fileNamed:) is actually init method from SKNode, as it says in official doc:
init?(fileNamed: String)
Creates a new node by loading an archive file from the game’s main bundle.
So there are two different methods (constructors), from two different classes, and even though one inherit from another they should not be confused.

Cocos2d iPHone SDK ccPhysicsCollisionPreSolve crashing while accessing object properties

I am working on Cocos2d iphone SDK and stuck with an issues. Check my code here.
Obstacle Class
#objc class Obstacle: CCNode {
weak var __pipe: CCSprite!
var ignoreCollision:Bool = false
override init!() {
super.init()
//NSLog("init plain")
userInteractionEnabled = true
ignoreCollision = false
}
func didLoadFromCCB() {
...
}
}
The main scene where I have placed collision delegate methods. The method is called once the player object collides with obstacle object.
func ccPhysicsCollisionPreSolve(pair: CCPhysicsCollisionPair!, hero: Player!, platform: Obstacle!) -> ObjCBool {
if !isGameOn {
NSLog("PLATFORM: Game finished")
return false
}
if platform.ignoreCollision {
platform.ignoreCollision = !platform.ignoreCollision
// For score updates
hudLayer.updatePlatform(++scorePlatforms)
}
return true
}
Now here, I am just trying to use simple Bool property from platform object and what I get is a crash. My app crashes on the if... condition statement where I am using that property. I am unable to get what is with this as I am simply using a property from object.
I checked the object and found platform shows me of type Some instead ob Obstacle. I have tried using
var p: Obstacle = platform as Obstacle
and replaced all platform with p but yet I am facing the crash. I thought the type now shows me some random hex number which might be the issue.
Can anyone help me here as I am unable to find out how I should access property from this platform object in ccPhysicsCollisionPreSolve method?
Sorry guys for the trouble but it was my mistake. I was understanding the same incorrectly.
The Obstacle class represents the platform as well as its background layer having tripple height of the device screen. But my ball collides only with that __pipe sprite in Obstacle class and I am referring the whole Obstacle class which is wrong.
I used platform.parent!.ignoreCollision and problem is solved. :)
This little miss costed me 3-4 days of R&D and work extra.

Swift: Fatal error: unexpectedly found nil while unwrapping an Optional value (SpriteKit)

I've just started learning to program iOS games with SpriteKit and I'm a novice to programming (I have only programmed in Java and Swift before this). I started out by doing a tutorial I found online. I'm at the point where I am trying to add a "Game Over" scene, and I keep getting the error
"Thread 1:EXC-BAD_INSTRUCTION(code=EXC_I386_INVOP,subcode=0x0)"
when I am declaring the gameOverLabel constant. It compiles but crashes at run time as soon as the ball hits the bottom of the screen, which is supposed to trigger the 'Game Over' screen.
import SpriteKit
let gameOverLabelCategoryName = "gameOverLabel"
class GameOverScene: SKScene {
var gameWon: Bool = false {
didSet {
let gameOverLabel = childNodeWithName(gameOverLabelCategoryName) as! SKLabelNode
gameOverLabel.text = gameWon ? "Game Won" : "Game Over"
}
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
if let view = view {
let gameScene = GameScene.unarchiveFromFile("GameScene") as! GameScene
view.presentScene(gameScene)
}
}
}
Also if anyone has suggestions of places I can go to learn more about SpriteKit, articles or videos, it would be much appreciated as most things I have found have been in Objective-C and I am doing this in Swift.
Here is the tutorial I have been following..
childNodeWithName will return nil if a node with that name does not exist. Your code is not checking for this possibility (the as! assumes that it is both not nil and of the appropriate type) so this is causing the crash.
The tutorial asks you to create this 'gameOverLabel' node and name it before creating this code. Check that you did that correctly and that you did not misname it (it is case sensitive for example).
It looks like you are declaring your gameOverLabelCategoryName constant outside of the scope of the class which looks a bit odd, but should be fine. It will be scoped to internal (docs).
On line 14: "if let view = view {" you are declaring a constant named view which masks the original self.view instance. Perhaps that could be confusing the compiler and causing an internal crash?

SKSpriteNode subclass: method to remove all joints

I have created a subclass of SKSpriteNode. I connect instances of that class together with joints of type SKPhysicsJointLimit. I do this within my didEndContact(contact: SKPhysicsContact) in my GameScene:
var joint = SKPhysicsJointLimit.jointWithBodyA(contact.bodyA, bodyB: contact.bodyB, anchorA: pos1!, anchorB: pos2!)
self.physicsWorld.addJoint(joint)
This works well so far.
Then i come to the point where i want to release the node from the joint. According to the SKPhysicsBody docs there is a property called "joints" which is an array holding SKPhysicsJoint objects. I thought thats exactly what I need, but I am not able to iterate over an instance's joints and remove them from the physicsWorld. To do the job i added a method to my custom SKSpriteNode subclass.
func freeJoints(world: SKPhysicsWorld){
if let joints = self.physicsBody?.joints {
for joint in joints{
println("found a joint: \(joint)")
// example print:
//found a joint: <PKPhysicsJointRope: 0x7fbe39e95c50>
world.removeJoint(joint as SKPhysicsJoint)
}
}
}
Calling the method fails after the println() statement with the message "Swift dynamic cast failed". I would really appreciate your opinion in how to work with an SKPhysicsBody's joint property. More specifically: How to use (cast?) the items in the array to be able to remove them from a scene's SKPhysicsWorld.
I spent a little more time in investigating this. This is what I have come up with:
I decided to add an property to my SKSpriteNode subclass and manage the joints myself
var joints: [SKPhysicsJointLimit]
override init(){
...
self.joints = []
...
}
Everytime I add an joint to the scene's SKPHysicsWorld I also add it to the joints array of the SKNNode itself. Whilst iterating the SKPHysicsBody's joints-Array failed (see question) at the point I wanted to cast it to SKPhysicsJoint, removing items from the physics world works as intended when iterating the array of SKPhysicsJointLimit items:
func freeJoints(world: SKPhysicsWorld){
for item in self.joints{
println("removing item from physics world \(item)")
world.removeJoint(item)
}
self.joints.removeAll(keepCapacity: false)
}
}
This seems not to be the most elegant way to do the job, since there already is a framework managed array that promises to be same thing. But I was unable to utilize it and this works for now.

Accessing an IBOutlet from another class

I am new to Swift/iOS, so please bear with me:
I am trying to access a function in one class from another class, and update an UIImage name.
Within my viewcontroller class I have
class Documents: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet var UpdateImage: UIImageView
override func viewDidLoad() {
super.viewDidLoad()
UpdateImage()
}
func UpdateImage() {
UpdateImage.image = UIImage(named: "NewImage")
}
}
Everything works, the Image gets updated to "NewImage"
Question: I can access the UpdateImage func from another class, but why is it generating an error when trying to change the image in the Documents class?
class GetChanges {
var success = { operation:AFHTTPRequestOperation!, response:AnyObject!) -> Void in
var MakeChange = Documents()
MakeChange.UpdateImage()
}
}
This generates an error on the "UpdateImage.image = UIImage(named: "NewImage")" in the Documents Class; "fatal error: unexpectedly found nil while unwrapping an Optional value"
When you call it within the class itself, it is operating on itself and it has already been created from a nib/storyboard. This means that UpdateImage exists.
When you call the method from another class, when you call this line:
var MakeChange = Documents()
You are creating a new instance of Documents. This is not initialized through the nib/storyboard, and thus it never populated the IBOutlet value UpdateImage. Because this value doesn't exist, it unexpectedly finds nil and throws an error.
You need to somehow retain a reference to the instance of Documents you're trying to display. I'd need more information to tell you how to do that.
Also, because you mentioned that you're new, I'd like to point out a few issues I notice with your code that is making it very difficult to read.
Capitalized names are reserved for Types variable names should (almost) never begin with a capital letter.
Variable names should reflect the object they represent. UpdateImage sounds like it is an image. It would be better to name this updateImageView
Functions should be lowercase as well. It is strange to see capitalization this way and makes the code a bit uncomfortable to read.
Good luck!
Read about View Contoller's lifecycle, it's very important knowledge for iOS developer.
As Logan said:
You are creating a new instance of Documents. This is not initialized through the nib/storyboard, and thus it never populated the IBOutlet value UpdateImage
This means that after call init for ViewController (i.e. Documents()) nib isn't loaded. You can use outlets of viewController in another code only after viewDidLoad stage. Apple docs:
The nib file you specify is not loaded right away. It is loaded the first time the view controller's view is accessed. If you want to perform additional initialization after the nib file is loaded, override the viewDidLoad() method and perform your tasks there.
You can remove MakeChange.UpdateImage(), because it will be called in viewDidLoad. Or, if you want pass specific image name to view controller:
class Documents: UIViewController, UITableViewDataSource,
UITableViewDelegate {
#IBOutlet var UpdateImage: UIImageView
var imageName: String?
override func viewDidLoad() {
super.viewDidLoad()
updateImageView()
}
func updateImageView() {
if let imageName = imageName {
UpdateImage.image = UIImage(named: imageName)
}
}
}
After that, you can use
let documentsViewController = Documents
documentsViewController.imageName = "newImage"
When you load documentsViewController, newImage will be presented

Resources