Alexander Schutte
Alexander Schutte

Reputation: 881

How can I init a UIButton subclass?

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?

Upvotes: 88

Views: 61990

Answers (4)

dimpiax
dimpiax

Reputation: 12677

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
}

Upvotes: 8

33-B01
33-B01

Reputation: 2066

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)

Upvotes: 4

Nate Cook
Nate Cook

Reputation: 93276

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

Upvotes: 12

Imanou Petit
Imanou Petit

Reputation: 92419

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
    }

}

Upvotes: 275

Related Questions