Swift Unable to Override Variables in Subclass - swift

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)

Related

Overriding a Swift { get set } property to a { get } only property. Making a variable a constant

Is it possible to override a variable to a constant?
Basically this is the best I could come up with:
class TextStyle: UITextLabel {
private let _textStyle: FontStyle
override public var textStyle: FontStyle {
get { return _textStyle }
set {
#warning ("you cant set")
if newValue != _textStyle {
self.textStyle = _textStyle
}
}
}
init(frame: CGRect, textStyle: FontStyle) {
self._textStyle = textStyle
super.init(frame: frame)
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
If its not possible to override a var to a constant i want user to be notified on compile time that they cant set the property. The #warning doesn't work, its just the best i could come up with. I believe there is a possible way using protocols to do this, which is more complex. Similar to codable, encodable and decodable.

Swift: How to add new property to UIImageView class?

I'm afraid I'm relatively new to Swift, but have looked around as best I could and haven't been able to figure out how to do this relatively simple task!
I would like to add a new property called "angle" to the class UIImageView, such that you could use "image.angle". Here's what I've got, having attempted to follow the method of a tutorial I used (note that the required init? part was suggested by Xcode and I am not too sure of what it does):
class selection_image: UIImageView {
var angle = Double()
init(angle: Double) {
self.angle = angle
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Thank you very much for any help!!
class selection_image: UIImageView {
var angle = Double()
// your new Init
required convenience init(angle: Double) {
self.init(frame: CGRect.zero)
self.angle = angle
}
override init(frame: CGRect) {
super.init(frame: CGRect.zero)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Using Swift 4.2
#jasperthedog. You can add a property to a given class using AssociatedObjects of the Runtime using an extension as follows:
In this example, I add an optional viewAlreadyAppeared: Bool? property to UIViewController. And with this, I avoid creating subclasses of UIViewController
extension UIViewController {
private struct CustomProperties {
static var viewAlreadyAppeared: Bool? = nil
}
var viewAlreadyAppeared: Bool? {
get {
return objc_getAssociatedObject(self, &CustomProperties.viewAlreadyAppeared) as? Bool
}
set {
if let unwrappedValue = newValue {
objc_setAssociatedObject(self, &CustomProperties.viewAlreadyAppeared, unwrappedValue as Bool?, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
}

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

Annotating Swift function declarations that support conformance to a protocol?

Is there a standard mechanism for annotating function declarations in Swift to indicate that they are present because a class conforms to some protocol?
For instance, this declaration might be present because a class conforms to NSCoding. (Marking it with override would result in a syntax error, so it's not the kind of annotation I am looking for.) Ideally I am looking for a code-level annotation (e.g. override instead of /*! ... */).
// ... annotation such as "conform to NSCoding", if possible
func encodeWithCoder(encoder: NSCoder) {
// ...
}
You can use extension. for example:
protocol SomeProtocol {
func doIt() -> Int
}
class ConcreteClass {
....
}
extension ConcreteClass: SomeProtocol {
func doIt() -> Int {
// ...
return 1
}
}
But you cannot define required initializer in extension, for example:
// THIS DOES NOT WORK!!!
class Foo: NSObject {
}
extension Foo: NSCoding {
required convenience init(coder aDecoder: NSCoder) {
self.init()
}
func encodeWithCoder(aCoder: NSCoder) {
// ...
}
}
emits an error:
error: 'required' initializer must be declared directly in class 'Foo' (not in an extension)
required convenience init(coder aDecoder: NSCoder) {
~~~~~~~~ ^
In this case, you should use // MARK: comments:
class Foo: NSObject {
// ...
// MARK: NSCoding
required init(coder aDecoder: NSCoder) {
super.init()
}
func encodeWithCoder(aCoder: NSCoder) {
// ...
}
}

Simple Swift class does not compile

My simple class, ClassWithOneArray, produces this error:
Bitcast requires both operands to be pointer or neither %19 =
bitcast i64 %18 to %objc_object*, !dbg !470 LLVM ERROR: Broken
function found, compilation aborted! Command
/Applications/Xcode6-Beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift
failed with exit code 1
However, my class, ClassWithOneInt, does not. Why?
class ClassWithOneInt {
var myInt = Int()
init(myInt: Int) {
self.myInt = Int()
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(myInt, forKey: "myInt")
}
init(coder aDecoder: NSCoder) {
self.myInt = aDecoder.decodeObjectForKey("myInt") as Int
}
}
class ClassWithOneArray {
var myArray = String[]()
init(myArray: String[]) {
self.myArray = String[]()
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(myArray, forKey: "myArray")
}
init(coder aDecoder: NSCoder) {
self.myArray = aDecoder.decodeObjectForKey("myArray") as String[]
}
}
As I point out in comments, your example seems to compile fine on beta 2, although it still won't work for a couple of reasons, for encoderWithCoder to be of any use, ClassWithOneArray needs to:
declare conformance with NSCoding,
implement NSCoding,
inherit from NSObject or implement NSObjectProtocol, and,
use a non-mangled name.
All told, that means:
#objc(ClassWithOneArray)
class ClassWithOneArray:NSObject, NSCoding {
var myArray: String[]
init(myArray: String[]) {
self.myArray = myArray
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(myArray, forKey: "myArray")
}
init(coder aDecoder: NSCoder) {
self.myArray = aDecoder.decodeObjectForKey("myArray") as String[]
}
}
Also it seems as if the simple methods of testing archiving aren't available in the playground, probably because the classes don't get properly registered.
let foo = ClassWithOneArray(myArray:["A"])
let data = NSKeyedArchiver.archivedDataWithRootObject(foo)
let unarchiver = NSKeyedUnarchiver(forReadingWithData:data)
unarchiver.setClass(ClassWithOneArray.self, forClassName: "ClassWithOneArray")
let bar = unarchiver.decodeObjectForKey("root") as ClassWithOneArray
It looks like your syntax is a bit off for what you're trying to accomplish - something like this should work:
class ClassWithOneInt {
var myInt: Int
init(myInt: Int) {
self.myInt = myInt
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(myInt, forKey: "myInt")
}
init(coder aDecoder: NSCoder) {
self.myInt = aDecoder.decodeObjectForKey("myInt") as Int
}
}
class ClassWithOneArray {
var myArray: String[]
init(myArray: String[]) {
self.myArray = myArray
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(myArray, forKey: "myArray")
}
init(coder aDecoder: NSCoder) {
self.myArray = aDecoder.decodeObjectForKey("myArray") as String[]
}
}
In my experience simply declaring the Protocol "NSCoding" to your class should do the trick. Hope this helps someone.