Subclassing SCNScene in Swift - override init - swift

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)
}
}

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"])

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

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.

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()
}
}

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.

Override multiple overloaded init() methods in Swift

I'm attempting to create a custom NSTextFieldCell subclass in Swift (Xcode Beta 5), with the following code:
class CustomHighlightTextFieldCell : NSTextFieldCell {
required init(coder aCoder: NSCoder!) {
super.init(coder: aCoder)
}
init(imageCell anImage: NSImage!) {
super.init(imageCell: anImage)
}
init(textCell aString: String!) {
super.init(textCell: aString)
}
}
However, I receive compilation errors on the 2nd and 3rd init() declarations:
/Users/Craig/projects/.../CustomHighlightTextFieldCell:8:40: Invalid redeclaration of 'init(imageCell:)'
/Users/Craig/projects/.../CustomHighlightTextFieldCell.swift:17:5: 'init(imageCell:)' previously declared here
/Users/Craig/projects/.../CustomHighlightTextFieldCell:7:39: Invalid redeclaration of 'init(textCell:)'
/Users/Craig/projects/.../CustomHighlightTextFieldCell.swift:21:5: 'init(textCell:)' previously declared here
While there are some strange compiler bugs here (I'm getting the usual "SourceKitService Terminated, Editor functionality temporarily limited." message), it seems like I'm missing something in my method overriding - but I can't tell what.
I was under the assumption the named parameters, or at least the parameter types, would indicate that there are three different init() methods here, but apparently I'm missing a key piece of the puzzle.
Edit: If I add override to the 2nd and 3rd init() methods, I have a separate issue:
required init(coder aCoder: NSCoder!) {
super.init(coder: aCoder)
}
override init(imageCell anImage: NSImage!) {
super.init(imageCell: anImage)
}
override init(textCell aString: String!) {
super.init(textCell: aString)
}
This new issue (simply invoked by adding the two override keywords) seems to almost contradict the original.
/Users/Craig/projects/.../CustomHighlightTextFieldCell.swift:17:14: Initializer does not override a designated initializer from its superclass
/Users/Craig/projects/.../CustomHighlightTextFieldCell.swift:21:14: Initializer does not override a designated initializer from its superclass
It would seem that your declarations (with override) should be sufficient, however, they seem to need the #objc declarations as well. This works:
class CustomHighlightTextFieldCell : NSTextFieldCell {
required init(coder aCoder: NSCoder!) {
super.init(coder: aCoder)
}
#objc(initImageCell:)
override init(imageCell anImage: NSImage!) {
super.init(imageCell: anImage)
}
#objc(initTextCell:)
override init(textCell aString: String!) {
super.init(textCell: aString)
}
}
As you see, you have the corresponding super.init(xxx) in the functions which means you are overriding the functions. So just add the override keyword.
override init(imageCell anImage: NSImage!) {
super.init(imageCell: anImage)
}
override init(textCell aString: String!) {
super.init(textCell: aString)
}