検証環境:
Xcode 11.3
Swift 5.1.3
UIStackView の backgroundColor プロパティに色を設定しても描画されません。
(※追記: iOS 14 から UIStackView の backgroundColor で背景色を付けられるようになりました。)
そこで UIStackView の layer に CAShapeLayer を追加してそこに色を設定します。
UIStackView のサブクラスとして実装します。
IBInspectable
なcolor
プロパティにStoryboard上で色を設定することで確認できるようにしています。
import UIKit @IBDesignable class MyStackView: UIStackView { @IBInspectable var color: UIColor? override var backgroundColor: UIColor? { get { return color } set { color = newValue setNeedsLayout() } } private lazy var backgroundLayer: CAShapeLayer = { let layer = CAShapeLayer() self.layer.insertSublayer(layer, at: 0) return layer }() override func layoutSubviews() { super.layoutSubviews() CATransaction.begin() CATransaction.setDisableActions(true) // CALayerの暗黙的アニメーションは不要なのでオフにする backgroundLayer.path = UIBezierPath(rect: bounds).cgPath backgroundLayer.fillColor = backgroundColor?.cgColor CATransaction.commit() } }
さらに UIStackView をタップ中は背景色が少し暗めの色にハイライトするように改良します。
少し暗めの色に変化させるための leap メソッドはこちらの記事で定義したものを使っています。
import UIKit @IBDesignable class MyStackView: UIStackView { @IBInspectable var color: UIColor? override var backgroundColor: UIColor? { get { guard let color = color else { return nil } return isTracking ? color.lerp(to: .gray, progress: 0.3) : color } set { color = newValue setNeedsLayout() } } private var isTracking: Bool = false { didSet { setNeedsLayout() } } private lazy var backgroundLayer: CAShapeLayer = { let layer = CAShapeLayer() self.layer.insertSublayer(layer, at: 0) return layer }() override func layoutSubviews() { super.layoutSubviews() CATransaction.begin() CATransaction.setDisableActions(true) backgroundLayer.path = UIBezierPath(rect: bounds).cgPath backgroundLayer.fillColor = backgroundColor?.cgColor CATransaction.commit() } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { isTracking = true } override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { isTracking = false } override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) { isTracking = false } }