// // RadioButton.swift // VDS // // Created by Matt Bruce on 6/5/23. // import Foundation import UIKit import Combine import VDSCoreTokens /// Radio buttons are single-select components through which a customer indicates a choice. /// They must always be paired with one or more ``RadioButtonItem`` within a ``RadioButtonGroup``. /// Use radio buttons to display choices like delivery method. @objc(VDSRadioButton) open class RadioButton: SelectorBase { //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- required public init() { super.init(frame: .zero) } public override init(frame: CGRect) { super.init(frame: .zero) } public required init?(coder: NSCoder) { super.init(coder: coder) } //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- private var selectorSize = CGSize(width: 10, height: 10) //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. open override func setup() { super.setup() backgroundColorConfiguration.setSurfaceColors(VDSColor.feedbackErrorBackgroundOnlight, VDSColor.feedbackErrorBackgroundOndark, forState: .error) backgroundColorConfiguration.setSurfaceColors(.clear, .clear, forState: [.error, .disabled]) borderColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .selected) borderColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .highlighted) borderColorConfiguration.setSurfaceColors(VDSFormControlsColor.borderOnlight, VDSFormControlsColor.borderOndark, forState: .normal) borderColorConfiguration.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled) borderColorConfiguration.setSurfaceColors(VDSColor.feedbackErrorOnlight, VDSColor.feedbackErrorOndark, forState: .error) borderColorConfiguration.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: [.selected, .disabled]) borderColorConfiguration.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: [.error, .disabled]) selectorColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .selected) selectorColorConfiguration.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: [.selected, .disabled]) } /// This will change the state of the Selector and execute the actionBlock if provided. open override func toggle() { guard !isSelected else { return } //removed error if showError && isSelected == false { showError.toggle() } isSelected.toggle() sendActions(for: .valueChanged) } open override func layoutSubviews() { super.layoutSubviews() //get the colors let backgroundColor = backgroundColorConfiguration.getColor(self) let borderColor = borderColorConfiguration.getColor(self) let selectorColor = selectorColorConfiguration.getColor(self) if let shapeLayer = shapeLayer, let sublayers = layer.sublayers, sublayers.contains(shapeLayer) { shapeLayer.removeFromSuperlayer() self.shapeLayer = nil } let bounds = bounds self.backgroundColor = backgroundColor layer.borderColor = borderColor.cgColor layer.cornerRadius = bounds.width * 0.5 layer.borderWidth = VDSFormControls.borderWidth if shapeLayer == nil { let selectedBounds = selectorSize let bezierPath = UIBezierPath(ovalIn: CGRect(x: (bounds.width - selectedBounds.width) / 2, y: (bounds.height - selectedBounds.height) / 2, width: selectorSize.width, height: selectorSize.height)) let shapeLayer = CAShapeLayer() self.shapeLayer = shapeLayer shapeLayer.frame = bounds layer.addSublayer(shapeLayer) shapeLayer.fillColor = selectorColor.cgColor shapeLayer.path = bezierPath.cgPath } } } // MARK: AppleGuidelinesTouchable extension RadioButton: AppleGuidelinesTouchable { /// Overrides to ensure that the touch point meets a minimum of the minimumTappableArea. override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool { Self.acceptablyOutsideBounds(point: point, bounds: bounds) } }