Generate Value for Class Property - swift

I'm trying to subclass UILabel and automatically generate a value for one of the properties, so that I can initialize with some values, and have others randomly generated.
For example, here I'd want to be able to create a new MyLabel, pass in a value for labelString and and have the value for labelNumber generated randomly.
class MyLabel: UILabel {
var labelString : String!
var labelNumber : Int!
init(frame: CGRect, labelString: String) {
self.labelString = labelString
self.labelNumber = createNumber()
super.init(frame: frame)
}
func createNumber() -> Int {
return Int(arc4random())
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I get the error "Use of 'self' in method call before super.init initializes self." Is there a way to fix this, or a better pattern to be using?
Many thanks for any help!

You can't call instance methods from init until everything has been initialized. One way to handle it is to make createNumber a static function which doesn't require the class to be initialized to be called:
class MyLabel: UILabel {
var labelString : String!
var labelNumber : Int!
init(frame: CGRect, labelString: String) {
self.labelString = labelString
self.labelNumber = MyLabel.createNumber()
super.init(frame: frame)
}
static func createNumber() -> Int {
return Int(arc4random())
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Another way to handle it is to move the initialization of labelNumber after the call to super.init. This works because labelNumber is an Optional and thus is initially given the value of nil which meets the requirement that all properties of the subclass MyLabel are initialized before calling the init of the superclass.
class MyLabel: UILabel {
var labelString : String!
var labelNumber : Int!
init(frame: CGRect, labelString: String) {
self.labelString = labelString
super.init(frame: frame)
self.labelNumber = createNumber()
}
func createNumber() -> Int {
return Int(arc4random())
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

Related

Correct calling init function in Apple Swift 5 subclass in Cocoa application

I cannot figure out the correct format for calling the init function of NSTextField in a subclass called HyperlinkTextField.
class HyperlinkTextField: NSTextField {
var url: String = ""
override func mouseDown(with event: NSEvent) {
NSWorkspace.shared.open(URL(string: url)!)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
isBezeled = false
}
init(string: String) {
super.init(string: string) // error: must call a designated initializer of the superclass 'NSTextField'
isBezeled = false
}
}
All classes inheriting from NSControl have two designated initializers
init?(coder: NSCoder)
init(frame: NSRect)
So you must call the latter
init(string: String) {
super.init(frame: .zero)
self.stringValue = string
isBezeled = false
}

Two classes to use the same function to create custom view - Swift

I am kind of new to Swift and I can't figure this out. I have two classes where I need to use the same function to set up a custom UIStackVIew (Rating Control that shows rating stars). Each class has a variable called value that needs to be passed inside the function. I don't want to be duplicating the same code of the setUpStackView function inside each class. I have the following code:
class Class1: UIStackView {
var variable1 = "value1"
override init(frame: CGRect){
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder){
super.init(coder: aDecoder)
}
setUpStackView(value: variable1)
}
class Class2: UIStackView {
var variable2 = "value2"
override init(frame: CGRect){
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder){
super.init(coder: aDecoder)
}
setUpStackView(value: variable2)
}
extension Class1 {
func setUpStackView(value: String){
//code to set UIStackView rating control and to use the variable value
}
}
How can I implement the extension for the Class2? I am stuck with this. Any help would be greatly appreciated!
One solution can be moved common code to protocol where you can abstract out:
protocol BaseStackView {
var variable :String { get set }
}
class Class1: UIStackView,BaseStackView {
var variable = "value1"
override init(frame: CGRect){
super.init(frame: frame)
self.setUpStackView(value: variable)
}
required init(coder: NSCoder) {
super.init(coder: coder)
self.setUpStackView(value: variable)
}
}
class Class2: UIStackView,BaseStackView {
var variable = "value2"
override init(frame: CGRect){
super.init(frame: frame)
self.setUpStackView(value: variable)
}
required init(coder: NSCoder) {
super.init(coder: coder)
self.setUpStackView(value: variable)
}
}
extension UIStackView {
func setUpStackView(value: String) {
//Your setup here
}
}
You have many options.
You can make Class2 inherit from Class1:
class Class2: Class1 {
var value = "value2" //You have access to Class1's value, so you can change it here
setUpStackView(value: value) //But there's a problem here
}
But you can't just call a function when you're in the middle of a class declaration.
But you can do this in the initializer:
class Class2: Class1 {
override init(frame: CGRect) {
super.init(frame: frame)
value = "value2"
setUpStackView(value: value)
}
required init(coder aDecoder: NSCoder){
super.init(coder: aDecoder)
value = "value2"
setUpStackView(value: value)
}
}
The above is kind of jank because you're referencing class properties within its own initializer.
Here's another solution:
You can make your setup function a function extension of UIStackView:
extension UIStackView {
func setUpStackView(value: String) {
//Your setup here
self.someProperty = value //self is referring to the stackview itself
}
}
Another option is to create a static function.
extension Class1 {
static func setUpStackView(stackVw: UIStackView, value: String) {
stackVw.someProperty = value
//Doing it like this still makes this function "belong" to Class1
//It also makes it so anyone can set up their stack view like
//this because they have to pass their stack view in here
}
}
Usage would be like this regardless of what class you're in and what inheritance hierarchy you have:
override init(frame: CGRect) {
super.init(frame: frame)
Class1.setUpStackVw(stackVw: self, value: self.variable)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
Class1.setUpStackVw(stackVw: self, value: self.variable)
}
Yet another option is to create a protocol from which both Class1 and Class2 conform to.
protocol StackVwWithSpecialSetUp where Self: UIStackView {
var value: String {get}
}
extension StackVwWithSpecialSetUp {
func setUpStackView()
{
self.someProperty = self.value
}
}
And then you'd have your class conform to it
class Class1Or2: UIStackView, StackVwWithSpecialSetUp {
var value: String = "blah" //Compiler will force you to implement this
override init(frame: CGRect) {
super.init(frame: frame)
//Now you can use this
setUpStackVw()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
//You can use this here too
setUpStackVw()
}
}
Perhaps the way I'd do it is to just eliminate the need for the value property altogether:
class Class1: UIStackView {
override init(frame: CGRect) {
super.init(frame: frame)
setUpStackVw()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setUpStackVw()
}
func setUpStackVw()
{
self.accessibilityHint = "value1"
}
}
//////////
class Class2: Class1 {
override func setUpStackVw()
{
self.someProperty = "value2"
}
}

init UIView with arguments?

My UIView looks like this :
override init (frame : CGRect)
{
super.init(frame : frame)
}
I would like to add an argument such as :
override init (frame : CGRect, number:Int)
What is the right way to do it so I can init and send arg in one line ?
You would define your class something like this:
class MyView: UIView {
let number: Int
init(frame: CGRect, number: Int) {
self.number = number
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
// Need to initialize the number property here. Do so appropriately.
super.init(coder: aDecoder)
}
}
The use of the number property is just an example. Do what you actually need.
And you would create an instance like this:
let mv = MyView(frame: .zero, number: 42)
Obviously you would pass a useful frame.
You can do something like this:
class CustomView: UIView{
var number: Int
init(frame : CGRect, number: Int) {
self.number = number
print("Number = \(number)")
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Then add it to the stack like so:
let viewToAdd = CustomView(frame: CGRect(x: 20, y: 72, width: 100 , height: 100), number: 1)
viewToAdd.backgroundColor = .red
self.view.addSubview(viewToAdd)

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
}

Subclass UICollectionViewCell in Swift 1.2

Given the following UICollectionViewCell subclass:
class MJCollectionViewCell: UICollectionViewCell {
var MJImageView: UIImageView!
var image = UIImage()
override init(frame: CGRect) {
super.init(frame: frame)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
// MARK: - Setters
func setImage(image: UIImage) {
self.MJImageView.image = image
}
}
I receive the following error on func setImage(image: UIImage):
Method 'setImage' redeclares Objective-C method 'setImage:'
You have to rename the methond setImage(image: UIImage), because there is already a method with this name declared in the Objective-C's UICollectionViewCell class.