Swift: Init parameter is unresolved - ios

I'm trying to do something that seems straightforward, but I'm getting "use of unresolved identifier" instead of the variable I'm expecting.
In one class I'm instantiating an object:
let sprite = Hero(view: view)
View is a valid variable, and I can po it in this class.
In Hero, I have my init function setup as follows:
init(view: SKView) {
...
}
But when I try to po view from this initializer, I get the "use of unresolved identifier" error. Any idea why?
EDIT:
Here's all my relevant code:
GameScene.swift
import SpriteKit
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
let sprite = Hero(view: view)
self.addChild(sprite)
}
}
Hero.swift
import SpriteKit
class Hero: SKSpriteNode {
init(view: SKView) {
let texture = SKTexture(imageNamed:"Spaceship")
let color = SKColor(white: 0, alpha: 0)
let size = CGSizeMake(100, 100)
super.init(texture:texture, color:color, size:size)
self.xScale = 1.0
self.yScale = 1.0
self.position = CGPointMake((view.bounds.width / 2) - (size.width / 2), view.bounds.height - 200)
}
}
Everything about this code works, the spaceship will show up if I set the position to, let's say CGPointMake(200,200), only view is unresolved in Hero. I'm passing view so that Hero knows the bounds of the view it's being added to and can center itself.

So, I got the code to perform the way I was expecting. I ended up changing Hero's init definition to look like this:
init(addToView view: SKView)
And then initialized a new object by doing:
let hero = Hero(addToView: view)
Now, everything works as I expect, view is passed to init as a resolved variable. But what's funny is #connor's recommendation to use let sprite = Hero(view) should have worked when init was defined as init(view: SKView). "view:" in that case was a local parameter name and should not have been required in the function call. At least, that's what I'm lead to believe based on the Functions chapter of The Swift Programming Language book. I have no idea why Xcode complained about a missing "view:" in the function call, which would have been an external parameter name never listed in the init definition.

Related

SpriteKit Swift: SKView returning nil

I'm making a game using spritekit. I'm using a joystick in my game which is declared in a separate SKNode class called 'joystick'.
In this class, I add a UIGesturerecongiser to the view from the class. In the constructor method, one of the parameters is a SKView which is passed from the game scene.
Constructor Method in Joystick class:
init(colour: UIColor, position: CGPoint, skView: SKView) {
//constructor method
self.colour = colour;
self.parentposition = position;
self.view = skView;
super.init();
//setup properties
//user interaction is needed to allow touches to be detected
self.isUserInteractionEnabled = true;
//setup
setup();
}
In the games scene, I initialise the class like this:
class GameScene: SKScene {
func setupJoyStick() {
let joystick1 = Joystick(colour: UIColor.red, position: CGPoint(x: screenwidth / 3, y: screenwidth / 10 * 1.5), skView: self.view!)
self.addChild(joystick1)
}
}
Error:
When I run my app, I get an error because the 'self.view' return nil and because it is forced to unwrap, it causes a fatal error.
Where View is defined:
if let scene = GKScene(fileNamed: "GameScene") {
// Get the SKScene from the loaded GKScene
if let sceneNode = scene.rootNode as! GameScene? {
// Copy gameplay related content over to the scene
sceneNode.entities = scene.entities
sceneNode.graphs = scene.graphs
// Set the scale mode to scale to fit the window
sceneNode.scaleMode = .aspectFill
sceneNode.size = view.bounds.size
// Present the scene
if let view = self.view as! SKView? {
view.presentScene(sceneNode)
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
}
Additional Info:
I'm using:
Xcode 9.2
Swift 4
Sprite Kit
I'm testing it on:
Iphone 6s
Latest version of IOS (non-beta), latest public release.
Can someone please explain why this is happening and how to fix this problem.
Thanks in advance, any help would be appreciated.
Looks like you're trying to get view property of SKScene but it's nil. It's because you didn't presented SKScene.
Unfortunately I haven't worked with SpriteKit but you can find info about view property here and about SKScene here.

linking GameViewController.swift to GameScene.swift

I have created a UI elements on main.storyboard which i require to be hidden until the game is over and the once the player tap the screen to dismiss. Main.storyboard is linked to GameViewController therefor all my IBOutlets and IBActions are in there and all my game code is in GameScene. How can i link the view controller to the scene for that the popup image and buttons only appear when it is game over. Would greatly appreciate some help, I have been stuck on this for quite some time now.
This seems to be quite a common problem people have with SpriteKit games so lets go through the difference between SpriteKit games and UIKit apps.
When you make a regular UIKit app, e.g. YouTube, Facebook, you would use ViewControllers, CollectionViews, Views etc for each screen/menu that you see (Home screen, Channel screen, Subscription channel screen etc). So you would use UIKit APIs for this such as UIButtons, UIImageViews, UILabels, UIViews, UICollectionViews etc. To do this visually we would use storyboards.
In SpriteKit games on the other hand it works differently. You work with SKScenes for each screen that you see (MenuScene, SettingsScene, GameScene, GameOverScene etc) and only have 1 ViewController (GameViewController). That GameViewController, which has a SKView in it, will present all your SKScenes.
So we should add our UI directly in the relevant SKScenes using SpriteKit APIs such as SKLabelNodes, SKSpriteNodes, SKNodes etc. To do this visually we would use the SpriteKit scene level editor and not storyboards.
So the general logic would be to load your 1st SKScene as usual from the GameViewController and than do the rest from within the relevant SKScenes. Your GameViewController should basically have next to no code in it beyond the default code. You can also transition from 1 scene to another scene very easily (GameScene -> GameOverScene).
If you use GameViewController for your UI it will get messy really quickly if you have multiple SKScenes because UI will be added to GameViewController and therefore all SKScenes. So you would have to remove/show UI when you transition between scenes and it would be madness.
To add a label in SpriteKit it would be something like this
class GameScene: SKScene {
lazy var scoreLabel: SKLabelNode = {
let label = SKLabelNode(fontNamed: "HelveticaNeue")
label.text = "SomeText"
label.fontSize = 22
label.fontColor = .yellow
label.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
return label
}()
override func didMove(to view: SKView) {
addChild(scoreLabel)
}
}
To make buttons you essentially create a SKSpriteNode and give it a name and then look for it in touchesBegan or touchesEnded and run an SKAction on it for animation and some code after.
enum ButtonName: String {
case play
case share
}
class GameScene: SKScene {
lazy var shareButton: SKSpriteNode = {
let button = SKSpriteNode(imageNamed: "ShareButton")
button.name = ButtonName.share.rawValue
button.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
return button
}()
override func didMove(to view: SKView) {
addChild(shareButton)
}
/// Touches began
override open func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
let node = atPoint(location)
if let nodeName = node.name {
switch nodeName {
case ButtonName.play.rawValue:
// run some SKAction animation and some code
case ButtonName.share.rawValue:
let action1 = SKAction.scale(to: 0.9, duration: 0.2)
let action2 = SKAction.scale(to: 1, duration: 0.2)
let action3 = SKAction.run { [weak self] in
self?.openShareMenu(value: "\(self!.score)", image: nil) // image is nil in this example, if you use a image just create a UIImage and pass it into the method
}
let sequence = SKAction.sequence([action1, action2, action3])
node.run(sequence)
default:
break
}
}
}
}
}
To make this even easier I would create a button helper class, for a simple example have a look at this
https://nathandemick.com/2014/09/buttons-sprite-kit-using-swift/
You can also check out Apple's sample game DemoBots for a more feature rich example.
This way you can have things such as animations etc in the helper class and don't have to repeat code for each button.
For sharing, I would actually use UIActivityController instead of those older Social APIs which might become deprecated soon. This also allows you to share to multiple services via 1 UI and you will also only need 1 share button in your app. It could be a simple function like this in the SKScene you are calling it from.
func openShareMenu(value: String, image: UIImage?) {
guard let view = view else { return }
// Activity items
var activityItems = [AnyObject]()
// Text
let text = "Can you beat my score " + value
activityItems.append(text as AnyObject)
// Add image if valid
if let image = image {
activityItems.append(image)
}
// Activity controller
let activityController = UIActivityViewController(activityItems: activityItems, applicationActivities: nil)
// iPad settings
if Device.isPad {
activityController.popoverPresentationController?.sourceView = view
activityController.popoverPresentationController?.sourceRect = CGRect(x: view.bounds.midX, y: view.bounds.midY, width: 0, height: 0)
activityController.popoverPresentationController?.permittedArrowDirections = UIPopoverArrowDirection.init(rawValue: 0)
}
// Excluded activity types
activityController.excludedActivityTypes = [
UIActivityType.airDrop,
UIActivityType.print,
UIActivityType.assignToContact,
UIActivityType.addToReadingList,
]
// Present
view.window?.rootViewController?.present(activityController, animated: true)
}
and then call it like so when the correct button was pressed (see above example)
openShareMenu(value: "\(self.score)", image: SOMEUIIMAGE)
Hope this helps
create reference of GameViewController in GameScene class like this way
class GameScene: SKScene, SKPhysicsContactDelegate {
var referenceOfGameViewController : GameViewController!
}
in GameViewController pass the reference like this way
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
if let scene = GameScene(fileNamed: "GameScene") {
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
scene.referenceOfGameViewController = self
// Present the scene
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
}
by using this line you can pass reference to GameScene class
scene.referenceOfGameViewController = self
Now In GameScene class you can access all the variable of GameViewController like this way
referenceOfGameViewController.fbButton.hidden = false
referenceOfGameViewController.gameOverPopUP.hidden = false

Creating a button using SKLabelNode as a parent in Swift SpriteKit

I'm trying to create a subclass of SKLabelNode for a button in SpriteKit. I tried to create a button using SKLabelNode as a parent, so I can use everything I know about labels while creating my buttons (font, text size, text color, position, etc).
I've looked into Swift Spritekit Adding Button Programaticly and I'm using the basis of what that is saying, but rather I'm making a subclass instead of a variable, and I'm creating the button using a label's code. The subclass has the added function that will allow it to be tapped and trigger an action.
class StartScene: SKScene {
class myButton: SKLabelNode {
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if myButton.containsPoint(location) {
// Action code here
}
}
}
}
override func didMoveToView(view: SKView) {
let startGameBtn = myButton(fontNamed: "Copperplate-Light")
startGameBtn.text = "Start Game"
startGameBtn.fontColor = SKColor.blackColor()
startGameBtn.fontSize = 42
startGameBtn.position = CGPointMake(frame.size.width/2, frame.size.height * 0.5)
addChild(startGameBtn)
}
}
My error is in: if myButton.containsPoint(location)
The error reads: Cannot invoke 'containsPoint' with an argument list of type '(CGPoint)'
I know it has to do something with my subclass, but I have no idea what it is specifically.
I also tried putting parentheses around myButton.containsPoint(location) like so:
if (myButton.containsPoint(location))
But then the error reads: 'CGPoint' is not convertible to 'SKNode'
Thanks
I have an alternative solution that would work the same way.
Change the following code
if myButton.containsPoint(location) {
To this and it should compile and have the same functionality
if self.position == location {

removeFromSuperview doesn't work for UIView's added to SKScene

I am adding a UIView to the view of an SKScene. Later, when I wish to remove that UIView form its superview, using the standard method of uiview.removeFromSuperview does not seem to work. How should I be accomplishing this instead? Here is how I add the UIView:
func addContainerView() {
let containerRect = CGRectMake(400, 24, 600, 720)
smallerView = UIView(frame: containerRect)
smallerView.backgroundColor = UIColor.redColor()
self.view.addSubview(smallerView)
}
Here is how I am attempting to remove it:
func removeContainerView() {
smallerView.removeFromSuperview()
}
This all takes place within the SKScene class, so here 'self' refers to that scene.
Any thoughts?
First of all I wonder which version of swift your are using.
self.view is optional in 1.2
hence your should type: self.view?.addSubview() if you are targeting swift 1.2
I have tried in swift 1.2 to make a simple app
class GameScene: SKScene {
let subview = UIView()
override func didMoveToView(view: SKView) {
subview.frame = CGRectMake(0, 0, 100, 100)
subview.backgroundColor = SKColor.orangeColor()
self.view?.addSubview(subview)
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
removeContainerView()
}
func removeContainerView() {
subview.removeFromSuperview()
}
}
The above code works very well.
I can think of a couple of reasons your view doesn't get removed
Are you sure that removeContainerView is actually called. Try to make a break point to see if it's called
If you have set up your SKView in code something might have been setup wrong.
Your subview is being deallocated or something
To fully debug your problem we need to see some more code.
What we need is:
Declaration of your subview
All functions that call removeContainerView()
Even better would be to pastebin your SKScene class.

UIDynamics not working in Swift

I'm attempting to convert the Ray Wenderlich's tutorial on UIDynamics to swift code, but I'm stuck on the very first part.
http://www.raywenderlich.com/50197/uikit-dynamics-tutorial
Here's what I'm doing:
created a square UIView
added a UIDynamicAnimator to the main view
added a UIGravityBehavior and initialized it with the square
added gravity behavior to the animator
It all compiles and runs fine but the square doesn't move. Can anybody see what I'm doing wrong?
https://github.com/oisinlavery/UIDynamics-Swift
You've created your animator as a local variable to your go method, so it disappears as soon as that method is finished. It needs to be an instance variable on your ViewController class so that it will stick around and do the work of animating the square:
class ViewController: UIViewController {
var animator: UIDynamicAnimator?
#IBAction func go(sender : UIButton) {
var square = UIView(frame: CGRectMake(100, 100, 100, 100))
square.backgroundColor = UIColor.blackColor()
self.view.addSubview(square)
self.animator = UIDynamicAnimator(referenceView: self.view)
var grav = UIGravityBehavior(items:[square])
self.animator!.addBehavior(grav)
}
}

Resources