// // SelectorBase.swift // VDS // // Created by Matt Bruce on 6/5/23. // import Foundation import UIKit import Combine import VDSCoreTokens public protocol SelectorControlable: Control, Changeable { /// Whether not to show the error. var showError: Bool { get set } /// Size of the SelectorView. var size: CGSize { get set } /// Configuration for the Background Color based on Control State. var backgroundColorConfiguration: ControlColorConfiguration { get set } /// Configuration for the Border Color based on Control State. var borderColorConfiguration: ControlColorConfiguration { get set } /// Configuration for the Selector Color based on Control State. var selectorColorConfiguration: ControlColorConfiguration { get set } } /// Base Class used to build out a Selector control. @objcMembers @objc(VDSSelectorBase) open class SelectorBase: Control, SelectorControlable, ParentViewProtocol { //-------------------------------------------------- // 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: - Public Properties //-------------------------------------------------- open var children: [any ViewProtocol] { [selectorView] } open var onChangeSubscriber: AnyCancellable? open var size = CGSize(width: 20, height: 20) { didSet { setNeedsUpdate() } } var _showError: Bool = false /// Whether not to show the error. open var showError: Bool { get { _showError } set { if !isSelected && _showError != newValue { _showError = newValue setNeedsUpdate() } } } /// Override UIControl state to add the .error state if showError is true. open override var state: UIControl.State { get { var state = super.state if showError { state.insert(.error) } else { state.remove(.error) } return state } } open var backgroundColorConfiguration = ControlColorConfiguration() { didSet { setNeedsUpdate() } } open var borderColorConfiguration = ControlColorConfiguration() { didSet { setNeedsUpdate() } } open var selectorColorConfiguration = ControlColorConfiguration() { didSet { setNeedsUpdate() } } //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- private var selectorView = View() //-------------------------------------------------- // MARK: - Constraints //-------------------------------------------------- internal var shapeLayer: CAShapeLayer? //-------------------------------------------------- // 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() isAccessibilityElement = true accessibilityTraits = .button let layoutGuide = UILayoutGuide() addLayoutGuide(layoutGuide) layoutGuide .pinTop(0) .pinLeading(0) .pinTrailing(0, .defaultHigh) .pinBottom(0, .defaultHigh) .width(size.width) .height(size.height) } open override func setDefaults() { super.setDefaults() showError = false onClick = { control in control.toggle() } onChange = nil bridge_accessibilityLabelBlock = { [weak self] in guard let self else { return "" } return "\(Self.self)\(showError ? ", error" : "")" } bridge_accessibilityHintBlock = { [weak self] in guard let self else { return "" } return !isEnabled ? "" : "Double tap to activate." } } open override func updateView() { super.updateView() setNeedsLayout() layoutIfNeeded() } /// This will change the state of the Selector and execute the actionBlock if provided. open func toggle() { } open override func reset() { super.reset() onChange = nil } open override func accessibilityActivate() -> Bool { guard isEnabled, isUserInteractionEnabled else { return false } guard isEnabled, isUserInteractionEnabled else { return false } var value = true // if #available(iOS 17, *) { // if let block = accessibilityAction { // block(self) // // } else if let block = accessibilityActivateBlock { // value = block() // // } else if let block = bridge_accessibilityActivateBlock { // value = block() // // } else { // toggle() // } // } else { if let block = accessibilityAction { block(self) } else if let block = bridge_accessibilityActivateBlock { value = block() } else { toggle() } // } return value } }