How can I init a UIButton subclass?

SwiftUibutton

Swift Problem Overview


I tried to add a double value to a subclass of UIButton in Swift. I tried all kind of inits and get and set options, but I couldn’t get it to work.

So I started with this:

class CVSTButton : UIButton {
    var cvstPosition: Double

    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")

        super.init(coder: aDecoder)
    }
}

Then I tried:

class CVSTButton : UIButton {
    var cvstPosition: Double {
        get {
            return self.cvstPosition
        }
        set {
            self.cvstPosition = newValue
        }

    }

    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
        super.init(coder: aDecoder)
   }
}

What’s is wrong here?

Swift Solutions


Solution 1 - Swift

With Swift 3, according to your needs, you may choose one of the seven following code snippets to solve your problem.


1. Create your UIButton subclass with a custom initializer

This solution allows you to create instances of your UIButton subclass with the appropriate value for your property. With this solution, you can only create instances of your UIButton subclass programmatically.

import UIKit

class CustomButton: UIButton {
    
    var myValue: Int
    
    required init(value: Int = 0) {
        // set myValue before super.init is called
        self.myValue = value
        
        super.init(frame: .zero)
        
        // set other operations after super.init, if required
        backgroundColor = .red
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
}

Usage:

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let button = CustomButton(value: 0)
        // let button = CustomButton() // also works
        button.setTitle("Hello", for: .normal)
        
        // auto layout
        button.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(button)
        button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        button.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
        
        print(button.myValue) // prints 0
    }
    
}

2. Create your UIButton subclass with a convenience initializer

This solution allows you to create instances of your UIButton subclass with the appropriate value for your property. With this solution, you can only create instances of your UIButton subclass programmatically.

import UIKit

class CustomButton: UIButton {
    
    var myValue: Int
    
    convenience init(squareOf value: Int) {
        self.init(value: value * value)
    }

    required init(value: Int = 0) {
        // set myValue before super.init is called
        self.myValue = value
        
        super.init(frame: .zero)
        
        // set other operations after super.init, if required
        backgroundColor = .red
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
}

Usage:

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let button = CustomButton(squareOf: 10)
        // let button = CustomButton(value: 100) // also works
        button.setTitle("Hello", for: .normal)
        
        // auto layout
        button.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(button)
        button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        button.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
        
        print(button.myValue) // prints 100
    }
    
}

3. Create your UIButton subclass with init(frame: CGRect) initializer

With this solution, you can only create instances of your UIButton subclass programmatically.

import UIKit

class CustomButton: UIButton {
    
    var myValue: Int
    
    override init(frame: CGRect) {
        // set myValue before super.init is called
        self.myValue = 0
        
        super.init(frame: frame)
        
        // set other operations after super.init, if required
        backgroundColor = .red
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
}

Usage:

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let button = CustomButton(frame: .zero)
        //let button = CustomButton() // also works
        button.setTitle("Hello", for: .normal)
        
        // auto layout
        button.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(button)
        button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        button.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
        
        print(button.myValue) // prints 0
    }
    
}

4. Create your UIButton subclass with init?(coder aDecoder: NSCoder) initializer

With this solution, you can create instances of your UIButton subclass from Storyboard.

import UIKit

class CustomButton: UIButton {
    
    var myValue: Int
    
    required init?(coder aDecoder: NSCoder) {
        // set myValue before super.init is called
        self.myValue = 0
        
        super.init(coder: aDecoder)
        
        // set other operations after super.init, if required
        backgroundColor = .red
    }
    
}

Usage:

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var button: CustomButton!
    
    override func viewDidLoad() {
        super.viewDidLoad()
              
        print(button.myValue) // prints 0
    }
    
}

5. Create your UIButton subclass with init(frame: CGRect) and init?(coder aDecoder: NSCoder) initializers

With this solution, you can create instances of your UIButton subclass programmatically or from Storyboard.

import UIKit

class CustomButton: UIButton {
    
    var myValue: Int
    
    override init(frame: CGRect) {
        // set myValue before super.init is called
        self.myValue = 0
        
        super.init(frame: frame)
        
        // set other operations after super.init, if required
        backgroundColor = .red
    }
    
    required init?(coder aDecoder: NSCoder) {
        // set myValue before super.init is called
        self.myValue = 0
        
        super.init(coder: aDecoder)
        
        // set other operations after super.init if required
        backgroundColor = .red
    }
    
}

6. Create your UIButton subclass with a default property value for your property

As an alternative to the previous solutions, you can assign an initial value to your property outside of the initializers.

import UIKit

class CustomButton: UIButton {
    
    var myValue: Int = 0
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        // set other operations after super.init, if required
        backgroundColor = .red
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        
        // set other operations after super.init if required
        backgroundColor = .red
    }
    
}

7. Create your UIButton subclass with your property having an optional type

If you don't want to / can't set a default value to your property when your button is created, you must set your property type as an optional.

import UIKit

class CustomButton: UIButton {
    
    var myValue: Int? = nil
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        // set other operations after super.init, if required
        backgroundColor = .red
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        
        // set other operations after super.init if required
        backgroundColor = .red
    }
    
}

Solution 2 - Swift

You need two things there -- (1) cvstPosition needs an initial value, either in the declaration or in the init before you call super.init(). (2) That call to fatalError is put in so you don't forget to implement the initializer -- it’s basically an on-purpose crash. Delete!

Setting the initial value in the declaration, there isn’t any need for an init:

class CVSTButton : UIButton {
    var cvstPosition: Double = 0
}

Or setting the initial value in the initializer:

class CVSTButton : UIButton {
    var cvstPosition: Double

    required init(coder aDecoder: NSCoder) {
        cvstPosition = 0

        super.init(coder: aDecoder)
    }
}

Solution 3 - Swift

Swift >= 2.2:

Since this version subclassing the UIButton, makes your button to have .custom type.

Swift 2:
convenience init(type buttonType: UIButtonType) {
    super.init(frame: CGRectZero)

    // this button be automatically .Custom
}
Swift:
override class func buttonWithType(buttonType: UIButtonType) -> AnyObject {
    let button = super.buttonWithType(buttonType) as! UIButton
    // your default code
    return button
}

Solution 4 - Swift

Note: I'm using Swift 3 in Xcode 8.3.3

This is a simple and easy workaround I've been using when needing to add custom properties and methods to a UIButton:

class CVSTButton: UIButton {

    var cvstPosition: Double

    static func button(withCVSTPosition cvstPosition: Double) -> CVSTButton {

        let button = CVSTButton(type: .detailDisclosure) // You may adjust the initializer used to suit your needs.

        button.cvstPosition = cvstPosition // Then you can simply set the the properties (which are passed as arguments to the factor/class method)

        return button

    }

}

To use it:

let cvstButton = CVSTButton.button(withCVSTPosition: 2.0)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAlexander SchutteView Question on Stackoverflow
Solution 1 - SwiftImanou PetitView Answer on Stackoverflow
Solution 2 - SwiftNate CookView Answer on Stackoverflow
Solution 3 - SwiftdimpiaxView Answer on Stackoverflow
Solution 4 - SwiftfocornerView Answer on Stackoverflow