Creating a custom initalizer for SKScene that overrides convenience init?(fileNamed:) - swift

I'm trying to create a convenience initializer that overrides the convenience init? (fileNamed:) initializer in SKScene so that I can pass some initial values to the scene while also unarchiving the .sks file. The problem is that when I try to do this, it seems that the subclass of SKScene (GameScene) is unable to see the convenience init? (fileNamed:) of the superclass. Here are some of my attempts:
Class GameScene : SKScene {
var stage : Int?
override init(size: CGSize) {
super.init(size: size)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
convenience init?(fileNamed: String, stage: Int) {
self.init(fileNamed: fileNamed) // Compiler error-- Argument labels '(filenamed:)' do not match any available overloads
self.stage = stage
}
Another attempt I found suggested as a workaround:
Class GameScene : SKScene {
var stage : Int?
override init(size: CGSize) {
super.init(size: size)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
convenience init?(fileNamed: String) {
self.init(fileNamed: fileNamed) // Error at run time: EXC_BAD_ACCESS (code=2, address=0x16fc1bff0)
}
convenience init?(fileNamed: String, stage: Int) {
self.init(fileNamed: fileNamed)
self.stage = stage
}
The debugger reveals an endless loop of GameScene.init(fileNamed : String) -> GameScene?
How do I accomplish this? I need to move on with my life!! (and this project...)

Couldn't it be as simple as this?
if let gameScene = GameScene(fileNamed: "GameScene") {
self.gameScene = gameScene
self.gameScene.stage = 1
self.gameScene.setupBasedOnStage()
self.gameScene.scaleMode = .aspectFill
self.gameScene.gameSceneDelegate = self.menuSceneDelegate as! GameSceneDelegate!
self.view?.presentScene(self.gameScene, transition: SKTransition.reveal(with: .down, duration: 1.0))
}
You are able to set the stage property before revealing the page, and if you needed to you can call a setup function to load info/graphics based on the stage.
I know it's not as elegant as what you are trying to do, but maybe sometimes the easiest answer is the best?

Swift has rules around convenience initializers:
Rule 1: A designated initializer must call a designated initializer
from its immediate superclass.
Rule 2: A convenience initializer must call another initializer from
the same class.
Rule 3: A convenience initializer must ultimately call a designated
initializer.
The initializer public convenience init?(fileNamed filename: String) is itself a convenience initilizer on SKNode, so attempting to call it from your own convenience initializer breaks Rule 2.
If you see in the Xcode quick tips, the only initializer available to call from your convenience initializer would be required init?(coder aDecoder: NSCoder) which satisfies Rule 3.

Related

How can I subclass UISegmentedControl having custom designated initializer?

Seems like a trivial issue but I am not able to make this compile.
Neither in playgrounds nor in normal swift ios projects.
(Please note I am not using Storyboards that's why I don't need / care about the init?(coder) part..it;s jsut it has to be include otherwise the complier complains about it.)
class SegmentedControl: UISegmentedControl {
let configuration: [String]
required init(configuration: [String]) {
self.configuration = configuration
super.init(items: configuration)
}
required init?(coder aDecoder: NSCoder) { fatalError() }
}
let x = SegmentedControl(configuration: ["a","b"])
It is complaining about not having the deisignated initializer implemented.
Fatal error: Use of unimplemented initializer 'init(frame:)' for class
'__lldb_expr_167.SegmentedControl'
I don't understand what is going on here. Isn't the designated initializer the init(items:) for UISegmentedControl? I am calling it in my subclass designated initializer.
Solution:
class SegmentedControl: UISegmentedControl {
var configuration = [String]()
required init(configuration: [String]) {
super.init(items: configuration)
self.configuration = configuration
}
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) { fatalError() }
}
let x = SegmentedControl(configuration: ["a","b"])

Make a class of type SKNode in swift

Im trying to make a class in swift that will be of type sknode that will also contain a parameter holding a position. I keep getting an error trying to create the class saying "initializer does not override a designated initializer from its super class" I'm not sure what I'm doing wrong, or how the class is supposed to be created.
import Foundation
import SpriteKit
class hud:SKNode {
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override init(position:CGPoint){
}
}
You are getting the "initializer does not override a designated initializer from its super class" error since you have marked your init(position: CGPoint) initializer as override.
SKNode has no such initializer so there is nothing to override.
Simply removing the override will rid you of the error, and your Hud class will have an initializer taking a CGPoint as a parameter.
init(position: CGPoint) {...}
It's worth noting that SKNode already has a property called position of type CGPoint, so it's possible that SKNode already have the capabilities you are after. Otherwise you'll likely want to call your property something else (such as myPosition) to avoid clashing with the existing property.
Remove the override keyword and don't forget to call the original initializer in your init method:
class hud:SKNode {
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
init(position:CGPoint){
super.init()
}
}

Initialization of class hierarchy now fails in Swift 1.2

Assuming the following class structure:
class A : SKSpriteNode
{
let foo : CGFloat
required init?(coder aDecoder: NSCoder) { ... }
init(foo: CGFloat)
{
self.foo = foo
super.init(
texture: SKTexture(imageNamed: "test"),
color: UIColor.blackColor(),
size: CGSizeMake(42, 42))
}
}
class B : A
{
required init?(coder aDecoder: NSCoder) { ... }
init()
{
super.init(foo: 42)
}
}
With A and B being in different modules, shows the following compiler error in B:
Initializer 'init()' overrides Objective-C method 'init' from superclass 'SKSpriteNode'
However, when changing init() in B to override init(), the following error occurs:
Initializer does not override a designated initializer from its superclass
Which is only solvable by adding init() in A which in turn calls the init of the base class.
Observations:
The above code did work in Swift 1.1 (i.e. without override).
It does work, when A and B are in the same module.
It does work, when overriding init() in A and using override init() in B.
The release notes state:
Imported C structs now have a default initializer in Swift, which initializes all of the struct's fields to zero.
But IMO this is not related, since SKSpriteNode is not a struct.
Is this a compiler bug or did I miss something else in the release notes?
Update Apple fixed this in Xcode 6.3 Beta 2.

Extending SCNNode in Swift

I would like to extend SCNNode (if possible) in Swift. Here's my attempt:
class MyNode : SCNNode {
override init(geometry: SCNGeometry) -> SCNNode {
return super.init(geometry)
}
/* Xcode required this */
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
The -> operator is being tagged with an error "Consecutive declarations on a line must be separated by ;".
I know this is not right but if I humour Xcode it "fixes" it as follows:
override init(geometry: SCNGeometry); -> SCNNode {
Which is nonsense and then Xcode complains "Expected declaration".
I don't quite understand the SCNNode implementation - if I look at it in Xcode it's all declared as abstract (in comments) and offers no implementation. The docs do not suggest anything extends from SCNNode, you instantiate it directly, so I am assuming I should be able to extend it.
You can simply do:
class MyNode : SCNNode {
/* Xcode required this */
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Which will inherit the designated initialiser from the base class.
If you want to specify your own initialiser that takes a geometry, you must call a designated initialiser of the base class, so I'd say this would be what you'd want:
class MyNode : SCNNode {
init(geometry: SCNGeometry) {
super.init()
self.geometry = geometry
}
/* Xcode required this */
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
The initialiser you're seeing in the SCNNode.h code is an initialiser translated (automatically, I believe) from the Objective C method nodeWithGeometry. I believe this is a convenience initialiser, even though it's not specifically marked as such, which I'd personally say is a bug/limitation of the automatic header file translation. The documentation specifically says:
For consistency and simplicity, Objective-C factory methods get mapped as convenience initializers in Swift.
Don't be confused by the word abstract in the comments; this is the HeaderDoc tag for a short description of a method, i.e. "abstract" is used in the sense of an abridgement or summary.

Subclassing SCNScene in Swift - override init

I'm getting some compiler errors in Xcode 6 using Swift which I have a hard time wrapping my head around. I'm trying to create a scene by subclassing SCNScene, but keep getting errors on the initialisers. The basic structure of my code is:
class SpaceScene: SCNScene {
override init(named: String) {
super.init(named: named)
}
}
This results in an error on line 2 with the message "Initializer does not override a designated initializer from its superclass", although SCNScene clearly has such an initialiser. I think i'm missing something basic - any insights?
On XCode 6.1, the following should do it:
class SpaceScene : SCNScene {
override init() {
super.init()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}