How to override SKScene init(fileNamed: fileNamed) - swift

I have a SKScene class in which I need to implement a custom initializer that override the initializer init(fileNamed: fileNamed) of the SKScene superclass SKNode, in order to do some proprietary initializations but keep the possibility offered by init(fileNamed: fileNamed) to load the scene from the interface builder.
I have some trouble to find the right syntax. I've tried the following:
class Try: SKScene
{
override init(fileNamed: String)
{
super.init(fileNamed: fileNamed)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
But it returns the error : Initializer does not override a designated initializer from its superclass
It is however a designated initializer from a superclass, not the immediate superclass which is SKEffectNode, but the one above which is SKNode. How can I override the initializer in this case ?
J.

From the SWIFT docs:
Conversely, if you write a subclass initializer that matches a superclass convenience initializer, that superclass convenience initializer can never be called directly by your subclass, as per the rules described above in Initializer Delegation for Class Types. Therefore, your subclass is not (strictly speaking) providing an override of the superclass initializer. As a result, you do not write the override modifier when providing a matching implementation of a superclass convenience initializer.
If I understand this right this code should work:
convenience init?(fileNamed filename: String)
{
self.init(fileNamed: filename)
}

Related

Swift 4.1 - Subclass UIImage

I get Overriding non-#objc declarations from extensions is not supported error when subclass UIImage with custom init after upgrading to Swift 4.1
class Foo: UIImage {
init(bar: String) { }
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// Overriding non-#objc declarations from extensions is not supported
required convenience init(imageLiteralResourceName name: String) {
fatalError("init(imageLiteralResourceName:) has not been implemented")
}
}
Thanks for your help
extension UIImage {
/// Creates an instance initialized with the given resource name.
///
/// Do not call this initializer directly. Instead, initialize a variable or
/// constant using an image literal.
required public convenience init(imageLiteralResourceName name: String)
}
This init method is declared in the extension of the UIImage class.
The error pretty much says that if a function is declared in the extension than it can't be overridden in this way
class Foo: UIImage {
}
extension Foo {
convenience init(bar :String) {
self.init()
}
}
var temp = Foo(bar: "Hello")
You could try to achieve the desired result in this way.
The problem seems to be caused by the init(bar:) designed initializer, if you convert it to a convenience one, then the class will compile:
class Foo: UIImage {
convenience init(bar: String) { super.init() }
// no longer need to override the required initializers
}
Seems that once you add a designated initializer (aka a non-convenience one), Swift will also enforce overrides for all required initializers from the base class. And for UIImage we have one that lives in an extension (not sure how it got there, likely was auto-generated, as myself wasn't able to add a required initializer in an extension). And you run into the compiler error in discussion.

Creating subclass of NSStackView

I try to create a subclass os NSStackView, for example to give it a special color.
To instantiate it, I have to call a superclasses designated init, if I understood right.
How can I detect, which are the designated initializers os NSStackView. Looking at https://developer.apple.com/documentation/appkit/nsstackview the only initializer I find is:
init(views: [NSView])
but the following code doesn't compile with "Must call a designated initializer of the superclass 'NSStackView'"
class CStackView: NSStackView
{ init(views: [NSView], withColor color: NSColor)
{ super.init(views: views)
// do something wich the color
}
required init?(coder decoder: NSCoder)
{ fatalError("init(coder:) has not been implemented")
}
}
init(coder:) seems to be a designated initializer, but I have want to init the CStackView with it´s subviews, not having a coder.
init(views:) is a convenience initializer.
NSStackView doesn't declare any designated initializers, so it inherits them from its superclass, NSView, which has init(frame:) and init(coder:)
You can either have your initializer call super.init(frame:) or declare your initializer as a convenience initializer and then it could call self.init(views:).
With #Dan s help, that´s it.
class C3StackView: NSStackView
{ convenience init(views: [NSView], withColor color: NSColor)
{ self.init(views: views)
// do something wich the color
}
required convenience init?(coder decoder: NSCoder)
{ fatalError("init(coder:) has not been implemented")
}
}

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

Adding NSCoding as an Extension

I'd like to extend a framework class (I don't want to edit the source code directly), and make it conform to NSCoding.
Basically, here's a simplification of the situation I'm in :
/* Can't be edited. */
class Car: NSObject {
var color: String?
}
/* Can be edited */
extension Car: NSCoding {
init(coder aDecoder: NSCoder) {
}
func encodeWithCoder(aCoder: NSCoder) {
}
}
The issue is init(coder aDecoder: NSCoder) is, as per the header file, a designated initializer (isn't this weird though ? shouldn't it be a convenience initializer ?). However, the documentation says extension can't add a new designated initializer.
My English isn't perfect and maybe I missed something out... Or is it really impossible ?
Like the documentation says, extensions can't add new designated initializers. What if there were private properties that need initialization? It would be impossible to properly initialize the type. You can add convenience initializers in an extension because by their nature, they must call a designated initializer.
Also, init(coder aDecoder: NSCoder) is specified to be a designated initializer because it is a whole different route to creating an instance. Take UIViewController for instance, it can be created using plain code or it can be created from a XIB file.
In the end, it is not possible to add an extension that implements NSCoding.
Perhaps you can create a wrapper class that contains this class and have it implement NSCoding.