【iOS】UIButtonにグラデーション、サブタイトル、左右アイコンを付ける

UIButtonにグラデーション、サブタイトル、左右アイコンを付ける例を紹介。![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272677/acb042b7-066b-a445-56fb-ad2b3d26cee2.png)
※ このコードにアイコンは付属しません

# コード
```swift
import UIKit

class WideButton: UIButton {
    var stackView = UIStackView(frame: .zero)
    var gradientLayer = CAGradientLayer()
    var startColor = UIColor(red: 41/255, green: 171/255, blue: 227/255, alpha: 1)
    var endColor = UIColor(red: 41/255, green: 171/255, blue: 227/255, alpha: 1)
    var title = UILabel()
    var subtitle = UILabel()
    
    override func layoutSubviews() {
        super.layoutSubviews()
        self.layer.cornerRadius = self.frame.height / 2
        self.setTitleColor(UIColor.white, for: UIControl.State.normal)
        self.titleLabel?.font = UIFont.boldSystemFont(ofSize: 18)
        gradientLayer.colors = [startColor.cgColor, endColor.cgColor]
        title.adjustsFontSizeToFitWidth = true
        title.baselineAdjustment = .alignCenters
        setLabelScale()
        layoutLayerFrame()
    }

    convenience init() {
        self.init(frame: CGRect.zero)
    }
    
    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setUp()
    }
    
    public override init(frame: CGRect) {
        super.init(frame: frame)
        setUp()
    }

    func setUp() {
        self.setTitle("", for: UIControl.State.normal)
        stackView.alignment = .firstBaseline
        stackView.distribution = .equalSpacing
        stackView.spacing = 8
        stackView.translatesAutoresizingMaskIntoConstraints = false
        stackView.isUserInteractionEnabled = false
        self.addSubview(stackView)
        self.centerYAnchor.constraint(equalTo: stackView.centerYAnchor).isActive = true
        self.centerXAnchor.constraint(equalTo: stackView.centerXAnchor).isActive = true
        title.font = UIFont.boldSystemFont(ofSize: 18)
        title.textColor = UIColor.white
        title.isHidden = true
        subtitle.font = UIFont.boldSystemFont(ofSize: 15)
        subtitle.textColor = UIColor.white
        subtitle.isHidden = true
        stackView.addArrangedSubview(title)
        stackView.addArrangedSubview(subtitle)
    }

    func setTitle(_ text: String) {
        title.text = text
        title.isHidden = false
        setLabelScale()
    }

    func setSubTitle(_ text: String) {
        subtitle.text = text
        subtitle.isHidden = false
        setLabelScale()
    }
    
    func setGradientColors(_ start: UIColor, _ end: UIColor) {
        startColor = start
        endColor = end
        gradientLayer.colors = [startColor.cgColor, endColor.cgColor]
    }
    
    func setLeftIcon(_ imageName: String) {
        if let iconImage =  UIImage(named: imageName) {
            let iconImageView = UIImageView(image: iconImage)
            let iconWidth = iconImage.size.width
            let iconHeight = iconImage.size.height
            iconImageView.frame = CGRect(x: 20, y: (self.frame.height - iconHeight) / 2, width: iconWidth, height: iconHeight)
            iconImageView.autoresizingMask = [.flexibleRightMargin]
            self.addSubview(iconImageView)
        }
    }
    
    func setRightIcon(_ imageName: String) {
        if let iconImage =  UIImage(named: imageName) {
            let iconImageView = UIImageView(image: iconImage)
            let iconWidth = iconImage.size.width
            let iconHeight = iconImage.size.height
            iconImageView.frame = CGRect(x: self.frame.width - iconWidth - 20, y: (self.frame.height - iconHeight) / 2, width: iconWidth, height: iconHeight)
            iconImageView.autoresizingMask = [.flexibleLeftMargin]
            self.addSubview(iconImageView)
        }
    }

    private func layoutLayerFrame() {
        gradientLayer.frame = self.bounds
        gradientLayer.cornerRadius = self.layer.cornerRadius
        gradientLayer.startPoint = CGPoint(x: 0, y: 0.5)
        gradientLayer.endPoint = CGPoint(x: 1, y: 0.5)
        gradientLayer.removeFromSuperlayer()
        self.layer.insertSublayer(gradientLayer, at: 0)
    }

    private func setLabelScale() {
        let labelWidth = title.frame.width + subtitle.frame.width
        var scale: CGFloat = 1
        if labelWidth > 0 { scale = (frame.width - 100) / labelWidth }
        if scale < 1 {
            stackView.transform = CGAffineTransform.identity.scaledBy(x: scale, y: scale)
        } else {
            stackView.transform = CGAffineTransform.identity.scaledBy(x: 1, y: 1)
        }
    }
}
```

# 使い方
以下のように使えます。

```swift:ViewController
@IBOutlet weak var gradientButton: WideButton! {
    didSet {
            gradientButton.setTitle("タイトルです")
            gradientButton.setSubTitle("サブタイトル")
            gradientButton.setLeftIcon("checkIcon")
            gradientButton.setRightIcon("nextIcon")
            gradientButton.setGradientColors(.cyan, .red)
    }
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272677/1e3c30ca-8e3a-e38d-873c-2bb713d82b42.png)
アイコンの呼び出しには`UIImage(named: String)`を使っています。
設定したい画像をAssetCatalogに用意してください。