2

I'm trying to subclass UIButton with my own button class called FlipsSendButton. The code for this class is listed below. In it you can see that I am calling setImage to attempt to set the button image. However, after compiling, everything works correctly EXCEPT setting the image view.

Am I missing some steps in my code to set the image, or perhaps calling the optional and unwrap operators incorrectly?

import Foundation

class FlipSendButton : UIButton {

    let bgColor : UIColor

    init(frame aFrame: CGRect, bgColor aColor: UIColor) {
        bgColor = aColor;

        super.init(frame: aFrame)

        addTarget(self, action: Selector("buttonTouched:"), forControlEvents: .TouchUpInside)
        backgroundColor = aColor;

        setImage(UIImage(named: "flips.png") as UIImage?, forState: .Normal)
    }

    required init(coder aDecoder: NSCoder) {
        bgColor = UIColor.lightGrayColor()
        super.init(coder: aDecoder)
    }

    func buttonTouched(sender: UIButton) {
        if backgroundColor == UIColor.lightGrayColor() {
            backgroundColor = bgColor
        } else {
            backgroundColor = UIColor.lightGrayColor()
        }
    }
}

Also, it is worth mentioning that the image flips.png is in my images.xassets folder, if that makes a difference.

liushuaikobe
  • 2,152
  • 1
  • 23
  • 26
Noah Labhart
  • 157
  • 4
  • 18

2 Answers2

9

You forgot the buttonType property. Besides, you'd better not subclass the UIButton(check out this for the reason).

Instead, You should set the buttonType property to UIButtonTypeCustom when you init the button, some sample code:

var myButton = UIButton.buttonWithType(.UIButtonTypeCustom)
myButton.setImage(UIImage(named: "flips.png") as UIImage?, forState: .Normal)

myButton.addTarget(self, action: Selector("buttonTouched:"), forControlEvents: .TouchUpInside)

func buttonTouched(sender: UIButton) {
    if sender.backgroundColor == UIColor.lightGrayColor() {
        sender.backgroundColor = bgColor
    } else {
        sender.backgroundColor = UIColor.lightGrayColor()
    }
}

-- EDIT --

If you want to reuse the style configuration, a better way is to define your own factory method of UIButton. Something like:

import Foundation
import UIKit
import ObjectiveC

private var bgColorKey: UInt8 = 0

extension UIButton {

    var bgColor: UIColor {
        get {
            return objc_getAssociatedObject(self, &bgColorKey) as! UIColor
        }
        set (neewColor) {
            objc_setAssociatedObject(self, &bgColorKey, neewColor, .OBJC_ASSOCIATION_RETAIN)
        }
    }

    class func buttonWithFrame(f: CGRect, backgroundColor color: UIColor) -> UIButton {
        let button = UIButton(type: .Custom)
        button.frame = f
        button.backgroundColor = color;
        button.bgColor = color
        button.setImage(UIImage(named: "flips.png") as UIImage?, forState: .Normal)
        button.addTarget(button, action: "buttonTouched:", forControlEvents: .TouchUpInside)
        return button
    }

    func buttonTouched(sender: UIButton) {
        if sender.backgroundColor == UIColor.lightGrayColor() {
            sender.backgroundColor = sender.bgColor
        } else {
            sender.backgroundColor = UIColor.lightGrayColor()
        }
    }
}

Then you can use the button like:

var btn = UIButton.buttonWithFrame(CGRectMake(50, 50, 100, 50), backgroundColor: UIColor.greenColor())
self.view.addSubview(btn)
Community
  • 1
  • 1
liushuaikobe
  • 2,152
  • 1
  • 23
  • 26
  • Thanks for your reply. I get the following error: Cannot assign to buttonType in self. – Noah Labhart Jun 16 '15 at 02:13
  • Gotcha. That is what I finding out, but was hoping I could get around it (since I already coded the thing). Thanks for your help. – Noah Labhart Jun 16 '15 at 02:19
  • @NoahLabhart I edited my answer and added a workaround with that you needn't change your code a lot but has the same result. see it please. Perhaps the most appropriate way :) – liushuaikobe Jun 16 '15 at 02:55
  • Thanks, this looks good. I think this would work, but I reverted to UIImageViews. If those don't work, I will be backing up and trying this out. – Noah Labhart Jun 16 '15 at 14:50
  • Thanks, your answer made me realize: As long as "someone else" sets the image, it works. So i tried a different approach with setting the UIButton's image from an inline class (see answer below) – Nir Golan Jul 23 '15 at 17:48
  • this part helped me. myButton.setImage(UIImage(named: "flips.png") as UIImage?, forState: .Normal). thank you – Adrian P Jul 15 '16 at 20:01
2

I found this frustrating as well. A fully functional UIButton subclass, that it's only flaw is setImage:. The easier solution I've found is writing a tiny helper class to set the image from outside:

class ButtonImageHelper {

    func setImage(image: UIImage?, forButton:UIButton) {
            forButton.setImage(image, forState: UIControlState.Normal)
    }   
}

This class can be positioned inside your UIButton subclass.
To call it:
ButtonImageHelper().setImage(yourImage, forButton:self)

Nir Golan
  • 1,336
  • 10
  • 24