How can I subclass UISegmentedControl having custom designated initializer? - swift

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

Related

Swift UIView subclass init failing

I created a UIView subclass:
class RA_Circle: UIView {
let elipseWidth:CGFloat
let elipseHeight:CGFloat
init(elipseWidth: CGFloat, elipseHeight:CGFloat) {
self.elipseWidth = elipseWidth
self.elipseHeight = elipseHeight
super.init(frame: CGRect.zero)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
But the compiler is giving me this error on the required init:
Property 'self.elipseWidth' not initialized at super.init call
I was following this SO Q&A:
How to properly init passed property in subclass of UIView?
In your class definition you're declaring your variable without any default values, and the required initializer is not providing any values before calling super init.
There are a number of ways to deal with this, here's a few...
One way is to provide default values in the property declarations:
var elipseWidth: CGFloat = 0.0
var elipseHeight: CGFloat = 0.0
The second is to make your properties optional:
var elipseWidth: CGFloat?
var elipseHeight: CGFloat?
A third is to provide default values in the required initializer:
required init?(coder aDecoder: NSCoder) {
elipseWidth = 14.0
elipseHeight = 14.0
super.init(coder: aDecoder)
}
You have to change the let variable declarations to var and optional. In this case, you would initialize them in awakeFromNib() or at some later time.

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

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.

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.

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) {
// ...
}
}