Overriding convenience init - swift

Trying to subclass an NSTextView:
class MYTextView : NSTextView {
init(frame frameRect: NSRect) {
super.init(frame: frameRect)
setup()
}
}
I get this error: Must call a designated initializer of the superclass 'NSTextView' on this line: super.init(frame: frameRect).
According to the docs Convenience initializers must call another initializer available in the same class.. See 'Initializer Chaining' below:
https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/Initialization.html#//apple_ref/doc/uid/TP40014097-CH18-XID_286
But for NSTextViews the only designated inits i get are super.init(frame:, textContainer:) & super.init(coder: coder) & super.inti(). init(frame:) does some setup which I'd rather not implement myself.
Is there some way to use a super class's convenience initializer?

Override the designated initialisers:
class MyTextView : NSTextView {
init(frame frameRect: NSRect, textContainer aTextContainer: NSTextContainer!) {
super.init(frame: frameRect, textContainer: aTextContainer)
setup();
}
func setup() {
...
}
}
var textView = MyTextView(frame: NSRect())
Since all the designated initialisers are overridden, all convenience will be automatically inherited.
There are two other designated initialzers to override:
init() {
}
and
init(coder:) {
}

I was just tripped up by this exact problem as well. I still Swift initialization with inheritance tricky, so definitely possible I'm not understanding something else here.
The accepted answer seems to suggest overriding init(frame: textContainer:), init, and init(coder:) will make init(frame:) accessible, but is does not for me.
The only way I was able to get things working as I wanted was with this:
override init(frame frameRect: NSRect, textContainer container: NSTextContainer?) {
super.init(frame: frameRect, textContainer: container)
setup()
}
override init(frame frameRect: NSRect) {
// this will end up calling init(frame:textContainer:)
super.init(frame: frameRect)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

Related

Live Interface Builder update with #IBDesignable for custom NSSlider control

I have problems in getting my custom NSSlider control updated live within Xcode's Interface Builder.
I have implemented #IBDesignable and prepareForInterfaceBuilder as shown in many other posts and tutorials. My little test just removes the knob from the slider control.
Here is the code I am using at the moment:
import Cocoa
#IBDesignable
class ColorSlider2: NSSlider {
override func setNeedsDisplay(_ invalidRect: NSRect) {
super.setNeedsDisplay(invalidRect)
}
override func awakeFromNib() {
super.awakeFromNib()
setupView()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setupView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
}
private func setupView() {
if ((self.cell?.isKind(of: ColorSlider2Cell.self)) == false) {
let cell = ColorSlider2Cell()
self.cell = cell
}
self.alphaValue = 0.5
self.floatValue = 0.4
}
}
class ColorSlider2Cell: NSSliderCell {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init() {
super.init()
}
override func drawKnob(_ knobRect: NSRect) {
return
}
}
The preview in Interface Builder is neither removing the knob nor updating the floatValue:
Do you have any idea why this is the case?

How to Pass extra parameters to a custom UIView class for initialization in swift

I'm trying to write a class that is of type UIView, but on initialization I want it to take an extra parameter, but I can't figure out how to get around the UIView needing its params instead. Any help is much appreciated!
class MenuBar: UIView {
let homeController: HomeController
init(controller: HomeController){
homeController = controller
super.init()
}
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
In the ViewController I'm initializing it like this:
let menuBar: MenuBar = {
let mb = MenuBar(controller: self)
return mb
}()
Try this.
class MenuBar: UIView {
let homeController: HomeController
required init(controller: HomeController){
homeController = controller
super.init(frame: CGRect.zero)
// Can't call super.init() here because it's a convenience initializer not a desginated initializer
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
From my experience this is what works best if you want to have custom initialiser for UIView:
class CustomView : UIView {
private var customProperty: CustomClass
required init(customProperty: CustomClass) {
super.init(frame: .zero)
self.customProperty = customProperty
self.setup()
}
required override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setup()
}
fileprivate func setup() {
//Here all custom code for initialisation (common for all creation methods)
}
}
This approach allows you to keep common initialisation code regardless of method of creating the view (both storyboard and code)
That's about creating UIView properly.
Additionally I would recommend to avoid passing UIViewController to UIView - I think you are trying to solve some problem in a wrong way.
Much better ways to communicate between those two is to use delegate or closure - but that's a bit off-topic - maybe you can create another question about why you want to pass it like this.

Must call a designated initializer error

import UIKit
class RightAnswerButtonClass: UIButton {
var rightAnswer: Bool
init() {
super.init()
rightAnswer = false
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I am trying to create a custom class button with the variable "rightanswer" stored as a bool. However, when I try to build, I recieve the error "Must call a designated initializer"
It's compile time error where super class designated initialisation not implement.
As subclass of UIButton must implement(override) init(frame:CGRect)("Designated Initialiser for UIButton") like as below,
> override init(frame: CGRect) {
super.init(frame: frame)
rightAnswer = true
}

Can I subclass UIView more concisely in Swift?

When subclassing UIView in Swift, you are required to have init functions
override init(frame: CGRect)
{
super.init(frame:frame)
initMyStuff()
}
required init?(coder aDecoder: NSCoder)
{
super.init(coder:aDecoder)
initMyStuff()
}
override func awakeFromNib()
{
super.awakeFromNib()
initMyStuff()
}
internal func initMyStuff()
{
// Init class variables and setup constraints here
}
Surely there is a better way to do this. At the very least, what would a better naming convention be for that initMyStuff function? Maybe there is a way extend UIView to have a single custom initializer function I can override in my custom classes?
You can create a convenience initializer like so :)
convenience init(someValue: String) {
self.init(frame : CGRectZero)
.....
}

Custom UITextField, init with no arguments

I have a custom UITextField and I'm trying to declare it like:
let textField = StandardTextField() // pretty much like UITextField()
My custom text field looks like:
class StandardTextField: UITextField {
init(frame: CGRect, size: CGFloat) {
super.init(frame: frame)
// some initialization
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
This is giving me an error because TextField has no initializer with no arguments. I tried adding:
init() {
super.init()
}
But this isn't possible since super.init() isn't the designated initializer. How can I achieve this?
You are probably looking for a convenience initialiser. But you will need to define a default frame size for your UITextField. Try like this:
class StandardTextField: UITextField {
convenience init() {
self.init(frame: CGRect(x: 0, y: 0, width: 200, height: 50))
// some initialisation for init with no arguments
}
override init(frame: CGRect) {
super.init(frame: frame)
// some initialisation for init with frame
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
Well unless I'm blatantly missing something, you've declared your class as class Textfield: UITextField and based on how you're trying to create your object, your class should be defined as class StandardField: UITextField and the class doesn't need any initializers as Leo said.
class StandardTextField: UITextField {
//no initializers
}