Episode #394

Making a Custom Subscribe Button

Series: Making a Podcast App From Scratch

16 minutes
Published on June 13, 2019

This video is only available to subscribers. Get access to this video and 572 others.

In this episode we customize our call-to-action Subscribe button. Using @IBDesignable and Interface Builder we can preview how it looks in the various button states without having to recompile and run in the simulator every time.

Custom Subscribe Button

In this episode, we will create a custom @IBDesignable button, this time for the subscribe button. We will set some custom properties and override intrinsicContentSize. We will also work on the highlighted and selected states of the subscribe button.

Creating a custom UIButton

We will start by creating a custom UIButton to define our settings. To view the custom button in Interface Builder, we will mark the class as @IBDesignable.


import UIKit

@IBDesignable
class SubscribeButton : UIButton {

    override func awakeFromNib() {
        super.awakeFromNib()
        commonSetup()
    }

    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        commonSetup()
    }
}   

Now we will set properties to customize our button. We will set cornerRadius and borderWidth, to see the effect of these setting we will set masksToBounds. We will also define intrinsicContentSize to give our button a lot of padding.


private func commonSetup() {
    tintColor = Theme.Colors.purple
    layer.cornerRadius = 12
    layer.borderWidth = 3
    layer.borderColor = borderColor.cgColor
    layer.masksToBounds = true
}

override var intrinsicContentSize: CGSize {
    return CGSize(width: 170, height: 46)
}

Setting the highlighted state of a button

Now we will set the highlighted color for subscribe button.


let highlightColor = Theme.Colors.purple.withAlphaComponent(0.85)
setBackgroundImage(UIImage.with(color: highlightColor), for: .highlighted)

To have a background color for the selected state on a button, we will create a 1px background image using UIGraphicsImageRenderer and use that image for the .highlighted state of the button.


import UIKit

extension UIImage {
    static func with(color: UIColor) -> UIImage {
        let size = CGSize(width: 1, height: 1)
        let renderer = UIGraphicsImageRenderer(size: size)
        return renderer.image { context in
            color.setFill()
            context.fill(CGRect(origin: .zero, size: size))
        }
    }
}

Setting the selected state of a button

To set the selected state of the subscribe button, we need to have a few settings listed below.

1) We need to add purple bright color to struct Colors in the color section of Theme.


static var purpleBright = UIColor(red: 0.63, green: 0.06, blue: 1.00, alpha: 1.00)

2) Next, we will add check icon with rendering mode set as "Template Image". Note that we're declaring the bundle specifically using the class instead of using Bundle.main or just UIImage(named:..) without passing a bundle. We do this because our code will run in Xcode as well (which will have a different main bundle). By passing the bundle explicitly our code works in both design time and run time.


let selectedColor = Theme.Colors.purpleBright
setBackgroundImage(UIImage.with(color: selectedColor), for: .selected)
let bundle = Bundle(for: SubscribeButton.self)
let checkIcon = UIImage(named: "icon-check", in: bundle, compatibleWith: nil)
setImage(checkIcon, for: .selected)
setTitleColor(.white, for: .selected)

3) To have a similar color for border and button during its selected state, we will set borderColor.


override var isSelected: Bool {
    didSet {
        layer.borderColor = borderColor.cgColor
    }
}

private var borderColor: UIColor {
    return isSelected ? Theme.Colors.purpleBright : Theme.Colors.purple
}

4) To reposition the check icon, we will set imageEdgeInsets of the button.


imageEdgeInsets.left = -20

This episode uses Xcode 10.2.1, Swift 5.0.