Sunday, September 6, 2015

Custom button to include both text and image as well as changing background color when selected

Update - September 29, 2016 - Xcode 8.0 and Swift 3.0
Original post - September 6, 2015 - Xcode 6.4 and Swift 1.2

This post is going to create a button with the following features:

1. A button including an image an text.
2. A rounded border around the button.
3. Change the background color when selected.

The result of the button created is:

When the button is selected:

Firstly, create a file called MyClass.swift to change the button background color:

Xcode 8.0 (Swift 3.0)

import UIKit

class MyClass: NSObject {
    
    class myButton : UIButton {
        override var isHighlighted: Bool {
            didSet {
                if (isHighlighted) {
                    self.backgroundColor = UIColor.blue
                } else {
                    self.backgroundColor = UIColor.white
                }
            }
        }
    }

}

=======

Xcode 6.4 (Swift 1.2)

import UIKit

class MyClass: NSObject {
    
    class myButton : UIButton {
        override var highlighted: Bool {
            didSet {
                if (highlighted) {
                    self.backgroundColor = UIColor.blueColor()
                } else {
                    self.backgroundColor = UIColor.whiteColor()
                }
            }
        }
    }

}

Then edit ViewController.swift as below:

Xcode 8.0 (Swift 3.0)

import UIKit

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let imageSize : CGFloat = 100
        let gap : CGFloat = 10
        let borderSize : CGFloat = 10
        let textHeight : CGFloat = 20
        let buttonWidth : CGFloat = borderSize * 2 + gap * 2 + imageSize
        let buttonHeight : CGFloat = borderSize * 2 + gap * 3 + imageSize + textHeight
        let imageOrigin : CGFloat = borderSize + gap
        let textTop : CGFloat = imageOrigin + imageSize + gap
        let textBottom : CGFloat = borderSize + gap
        let imageBottom : CGFloat = textBottom + textHeight + gap
        
        let myButton = MyClass.myButton()
        myButton.frame = CGRect(x: 0, y: 0, width: buttonWidth, height: buttonHeight)
        myButton.center = view.center
        myButton.addTarget(self, action: #selector(btnPressed), for: UIControlEvents.touchUpInside)
        
        //Border
        myButton.layer.borderColor = UIColor.blue.cgColor
        myButton.layer.borderWidth = borderSize
        myButton.layer.cornerRadius = 20
        
        //Image
        let myImage = UIImage(named: "telephone_blue.png")
        myButton.setImage(myImage, for: UIControlState.normal)
        myButton.setImage(UIImage(named: "telephone_white.png"), for: UIControlState.highlighted)
        myButton.imageEdgeInsets = UIEdgeInsets(top: imageOrigin, left: imageOrigin, bottom: imageBottom, right: imageOrigin)
        
        //Text
        myButton.setTitle("Telephone", for: UIControlState.normal)
        myButton.setTitleColor(UIColor.blue, for: UIControlState.normal)
        myButton.setTitleColor(UIColor.white, for: UIControlState.highlighted)
        myButton.titleEdgeInsets = UIEdgeInsets(top: textTop, left: -myImage!.size.width, bottom: textBottom, right: 0.0)
        
        view.addSubview(myButton)
    }
    
    func btnPressed() {
        print("button pressed!")
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

}

=======

Xcode 6.4 (Swift 1.2)

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let orignX : CGFloat = 70
        let orignY : CGFloat = 70
        let imageSize : CGFloat = 100
        let gap : CGFloat = 10
        let borderSize : CGFloat = 10
        let textHeight : CGFloat = 20
        let buttonWidth : CGFloat = borderSize * 2 + gap * 2 + imageSize
        let buttonHeight : CGFloat = borderSize * 2 + gap * 3 + imageSize + textHeight
        let imageOrigin : CGFloat = borderSize + gap
        let textTop : CGFloat = imageOrigin + imageSize + gap
        let textBottom : CGFloat = borderSize + gap
        let imageBottom : CGFloat = textBottom + textHeight + gap
        
        let myButton = MyClass.myButton()
        myButton.frame = CGRectMake(orignX, orignY, buttonWidth, buttonHeight)
        myButton.addTarget(self, action: "btnPressed:", forControlEvents: UIControlEvents.TouchUpInside)
        
        //Border
        myButton.layer.borderColor = UIColor.blueColor().CGColor
        myButton.layer.borderWidth = borderSize
        myButton.layer.cornerRadius = 20

        //Image
        let myImage = UIImage(named: "telephone_blue.png")
        myButton.setImage(myImage, forState: UIControlState.Normal)
        myButton.setImage(UIImage(named: "telephone_white.png"), forState: UIControlState.Highlighted)
        myButton.imageEdgeInsets = UIEdgeInsets(top: imageOrigin, left: imageOrigin, bottom: imageBottom, right: imageOrigin)
        
        //Text
        myButton.setTitle("Telephone", forState: UIControlState.Normal)
        myButton.setTitleColor(UIColor.blueColor(), forState: UIControlState.Normal)
        myButton.setTitleColor(UIColor.whiteColor(), forState: UIControlState.Highlighted)
        myButton.titleEdgeInsets = UIEdgeInsets(top: textTop, left: -myImage!.size.width, bottom: textBottom, right: 0.0)

        view.addSubview(myButton)
    }
    
    func btnPressed(sender: UIButton!) {
        println("button pressed!")
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}

3 comments:

  1. Thanks, this is very helpful, although I still don't understand why -myImage!.size.width, I would appreciate any help on that concept.

    ReplyDelete
    Replies
    1. Hi, It's great to know this solution is helpful to you. Unfortunately I've forgot how I worked out the UIEdgeInsets(... -myImage!.size.width...) code one year ago. Sorry I can't help you more!

      Delete
  2. Man, make custom controls and also use IBDesignable, not code in the viewdidload

    ReplyDelete