Swift Call an override method by subclass from superclass - swift

I have two classes in swift. I have an action method that I want to override in many subclasses that will do a different action. I wanted this to sort of function like an abstract class in Java, where I could pass a bunch of different subclasses of a certain class, but they would be referred to from the superclass so that one method could be called for all of them even though they are different subclasses with different overridden versions of that one method. Here is what I mean.
class1: SKSpriteNode{
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func action(){ print("this should never be called") }
}
class2: class1 {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func action() {
self.physicsBody?.affectedByGravity = true
}
}
I would want to do something like this. A node with name someName would be defined as a class2 in the SKScene editor.
class GameScene...
let node = self.childNode(withName: "someName") as? class1
noe.action()
However, I never get the output I want. I always get the call of class1 even when I want it to be overridden as a class2 and turn on gravity for that node. I would want to call the action method for many subclasses of class1 no matter the subclass, so just changing the class1 method to do what I want will not work.
Thank you
EDIT: Here are my actual classes:
So I have some classes that are nodes that will be triggered when the user passes the trigger node. It has a stored value for the name of the target node that is then retrieved from the main SKScene, its parent. Then I want to have a specific TargetNode, DropNode that will make it fall from the sky, but I want to be able to trigger any subclass of TargetNode by calling the TriggerAction method, but it never seems to perform the code of the subclass, the DropNode, but instead always does nothing/ whatever code I put in the action method of the TargetNode class. It never overrides the method.
//SUPERCLASS:
import Foundation
class SKTargetNode: SKGlowNode {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func action(){
print("i don't want this to be called")
}
}
//SUBCLASS:
import Foundation
class SKDropNode: SKTargetNode {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func action() {
self.physicsBody?.affectedByGravity = true
}
}
//Just a class that triggers any TargetNode
import Foundation
import SpriteKit
class SKTriggerNode: SKSpriteNode {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func triggerAction(_ scene: GameScene){
let nodeName = self.userData?.value(forKey: "TargetNode") as? String ?? ""
let node = scene.childNode(withName: "//\(nodeName)") as? SKTargetNode
node?.action()
}
}

Related

Swift Unable to Override Variables in Subclass

I am working on a macOS project in Swift, and I've been having a lot of trouble with overriding variables in a few classes I've made. In classTwo Xcode is presenting the error Cannot override with a stored property 'texture' on the line;
override var texture: SKTexture?
This is a bit of the code I'm using.
public class classOne: SKSpriteNode {
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// Other functions
}
And the second class;
class classTwo: classOne {
override var texture: SKTexture? // Cannot override with a stored property 'texture'
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// Some other functions
}
I do plan to actually define a specific texture for classTwo, but for now I couldn't even make this work.. Thanks in advance, and I'd appreciate any help on solving this issue!
EDIT
#Knight0fDragon was right, a better approach is to use super.texture
class classTwo: classOne {
override var texture:SKTexture? {
get {
return super.texture
}
set {
super.texture = newValue
}
}
}
In order to override the property you need to initialise it. In other words, giving it an initial value. If you don't want, here is a workaround:
class classTwo: classOne {
var _texture:SKTexture
override public var texture: SKTexture? {
get {
return _texture
}
set {
_texture = newValue!
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// Some other functions
}
Try this
override var texture: SKTexture? {
get {
return SKTexture()
}
set {
}
}
obviously returning something a bit more useful.
In your subclass you need to do this.
override var texture:SKTexture? {
get {
return super.texture
}
set {
super.texture = newValue
}
}
From all my reading, I do not think overriding is the approach you want, you want to create a convenience init
class classTwo: classOne {
convenience init()
{
let texture = SKTexture(imageNamed:"myImage")
self.init(texture,.white,texure.size)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder:aDecoder)
}
}
Then to use it, you just need to do classTwo()
(Note, this has not been tested, so I may have typos, the general concept is here though)

Instantiate class with NSCODER

I face a problem when trying to instantiate a class(SettingsView) that uses NSCoder, in another class(Swipe). The SettingsView class is already an instance of an objective -C class(FXBlurView)
I get the following error "Missing argument for parameter 'coder' in call". I realized that I have to call some parameters, but I do not know which.
SettingsView :
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
self.tintColor = UIColor.clearColor()
}
Swipe :
var settings = SettingsView()//error occurs here
i already tried to use
init () {
super.init (tint: ...)
}
But it seems to be not functionnal.
Ok , i succeed to do it just by adding :
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
var settings = SettingsView(coder: aDecoder)
}
into my "Swipe" view controller.

Calling a function from other Swift class

I am trying to access a function from my Class1.swift to Class2.swift
Here's my function in Class1.swift:
class ExampleFormViewController: FormViewController {
......
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
load_data()
self.loadForm()
}
func get_facility(company: Int!, territory: Int!){
}
}
And here's how I call it in my Class2.swift:
ExampleFormViewController().get_facility(ExampleFormViewController().val_company[row], territory: ExampleFormViewController().val_territory[row])
The error I get in this line is: Missing argument for parameter 'coder' in call
Your init method for the class requires a coder parameter.You need an NSCoder to pass in, put in a variable called theCoder and then change your call to this:
ExampleFormViewController().get_facility(ExampleFormViewController(theCoder).val_company[row], territory: ExampleFormViewController(theCoder).val_territory[row])

Correctly init NSCoder in sub class when init NSCoder is convenience method in base class in Swift

Here's my code:
import Foundation
class Person: NSObject, NSCoding {
var name: String
init(name: String) {
self.name = name
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(name, forKey: "name")
}
required convenience init?(coder aDecoder: NSCoder) {
let name = aDecoder.decodeObjectForKey("name") as! String
self.init(name: name)
}
}
class Martin: Person {
init() {
self.init(name: "Martin")
}
required convenience init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
let p = Martin()
print(p.name)
For some reason I always end-up in a catch-22 situation, the only way i see making this work is to explicitly initialize all properties in required convenience init?(coder aDecoder: NSCoder) to able to remove the convenience and do super.init(coder: aDecoder) in Martin
I read about the init rules in Swift, still don't understand why Martin can't inherit the convenience init from Person in this case.
Because the rules state that
A designated initializer must call a designated initializer from its immediate superclass.
A convenience initializer must call another initializer from the same class.
A convenience initializer must ultimately call a designated initializer.

How do you get the frame.width of GameScene in a custom class?

I'm having trouble trying to get the frame.width of GameScene into a custom class because I need the scene width for positioning nodes in my custom class init.
HudController.swift snippet
import SpriteKit
class HudController:SKNode {
var width:CGFloat
override init(){
width = GameScene().frame.width //I was hoping to add the scene width here
}
}
The following code crashes on me and I've tried heaps of other solutions with no success.
Can someone please help me with this issue?
Thanks!
UPDATED CODE ===
GameScene.swift
import SpriteKit
class GameScene: SKScene {
var hud = HudController(gameScene: self)
// set as global because I'm using it to also call hud functions in my game
}
HudController.swift
import SpriteKit
class HudController:SKNode {
var width:CGFloat
init(gameScene:SKScene){
width = gameScene.size.width
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Every node in SpriteKit can retrieve the scene where it lives using the scene property.
let node = SKNode()
node.scene
The scene property returns a SKScene?, the value is an optional because if the current node does not belong to a scene, of course there is not scene to retrieve.
Now, you could be tempted to use the self.scene property inside the initializer of your HudController.
class HudController: SKNode {
var width:CGFloat
override init() {
super.init() // error: property 'self.width' not initialized at super.init call
width = self.scene!.frame.width
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
But it will now work because you cannot invoke super.init before having initialized the width property.
Fine, then you could try this.
class HudController: SKNode {
var width:CGFloat
override init() {
width = self.scene!.frame.width // error: use of 'self' in property access 'scene' before super.init initializes self
super.init()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Again compiler error because you are using self before having called super.init().
And finally there is another problem, when the initializer is executed, the node it is being created has not a parent yet, so the self.scene property does return nil.
How to solve this?
Solution
First of all declare you initializer to receive a GameScene.
class HudController: SKNode {
var width: CGFloat
init(gameScene:SKScene) {
width = gameScene.frame.width
super.init()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Now, when you create this object (I hope you are doing it from the scene or from a node already added to the scene otherwise we are in trouble) you simply write
class Foo : SKNode {
func foo() {
guard let scene = self.scene else { return }
let controller = HudController(gameScene: scene)
}
}
On the other hand if you are creating HudController from you scene you simply write:
class MyScene : SKScene {
override func didMoveToView(view: SKView) {
super.didMoveToView(view)
let controller = HudController(gameScene: self)
}
}
That's it.
Update
If you want an instance property of HudController inside your GameScene this is the code.
class HudController: SKNode {
var width: CGFloat
init(gameScene:SKScene) {
width = gameScene.frame.width
super.init() // <-- do not forget this
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class GameScene: SKScene {
var hud : HudController?
override func didMoveToView(view: SKView) {
super.didMoveToView(view)
hud = HudController(gameScene: self)
}
}