SwiftでON・OFFの切り替えをする円形ボタンを作る
環境: Swift3
こんな感じの円形ボタンのカスタムビューを作る。
ボタンというよりUISwitch的なON・OFFの状態切り替えをさせたい。
UIControlを継承して、状態はisSelected
プロパティで保持している。
import UIKit import PlaygroundSupport final class CircleView: UIControl { var didTouchUpInsideHandler: (() -> Void)? let normalColor = UIColor(hex: 0x59acff) let selectedColor = UIColor(hex: 0xFF6E86) var circleColor: UIColor { return self.isSelected ? self.selectedColor : self.normalColor } // タッチの反応を円内のみとする override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { return self.circleShapeLayer.path?.contains(point) ?? false } // タッチ時、離れた時に呼ばれる override var isHighlighted: Bool { didSet { guard oldValue != self.isHighlighted else { return } self.circleShapeLayer.fillColor = self.isHighlighted ? self.circleColor.darkColor().cgColor : self.circleColor.cgColor if self.isHighlighted { UIView.animate( withDuration: 0.05, delay: 0, options: [.allowUserInteraction, .beginFromCurrentState], animations: { [weak self] in self?.transform = CGAffineTransform(scaleX: 1.1, y: 1.1) }) } else { UIView.animate( withDuration: 1, delay: 0, usingSpringWithDamping: 0.15, initialSpringVelocity: 10, options: [.allowUserInteraction, .beginFromCurrentState], animations: { [weak self] in self?.transform = CGAffineTransform.identity }) } } } // 円の描画 override func layoutSublayers(of layer: CALayer) { super.layoutSublayers(of: layer) if self.circleShapeLayer.superlayer == nil { self.layer.insertSublayer(self.circleShapeLayer, at: 0) } } // 円のlayer作成 lazy var circleShapeLayer: CAShapeLayer = { [unowned self] in let path = UIBezierPath(ovalIn: self.bounds) let shapeLayer = CAShapeLayer() shapeLayer.fillColor = self.circleColor.cgColor shapeLayer.path = path.cgPath return shapeLayer }() // タッチを離した時 override func endTracking(_ touch: UITouch?, with event: UIEvent?) { super.endTracking(touch, with: event) // 円内で離した場合のみに反応させる if let point = touch?.location(in: self), let path = self.circleShapeLayer.path, path.contains(point) { self.isSelected = !self.isSelected self.didTouchUpInsideHandler?() } } } extension UIColor { convenience init(hex: UInt32, alpha: CGFloat = 1.0) { let mask = 0x000000FF let r = Int(hex >> 16) & mask let g = Int(hex >> 8) & mask let b = Int(hex) & mask let red = CGFloat(r) / 255 let green = CGFloat(g) / 255 let blue = CGFloat(b) / 255 self.init(red:red, green:green, blue:blue, alpha: alpha) } // 暗めの色にする func darkColor(brightnessRatio: CGFloat = 0.8) -> UIColor { var hue: CGFloat = 0 var saturation: CGFloat = 0 var brightness: CGFloat = 0 var alpha: CGFloat = 0 if self.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha) { return UIColor(hue: hue, saturation: saturation, brightness: brightness * brightnessRatio, alpha: alpha) } else { return self } } } let baseView = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200)) baseView.backgroundColor = .white let view = CircleView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) view.center = baseView.center baseView.addSubview(view) PlaygroundPage.current.liveView = baseView