// // Control.swift // VDS // // Created by Matt Bruce on 7/22/22. // import Foundation import UIKit import Combine /// Base Class use to build Controls. @objc(VDSControl) open class Control: UIControl, ViewProtocol, UserInfoable, Clickable { //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- required public init() { super.init(frame: .zero) initialSetup() } public override init(frame: CGRect) { super.init(frame: .zero) initialSetup() } public required init?(coder: NSCoder) { super.init(coder: coder) initialSetup() } //-------------------------------------------------- // MARK: - Combine Properties //-------------------------------------------------- open var subscribers = Set() open var onClickSubscriber: AnyCancellable? //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- private var initialSetupPerformed = false //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- open var shouldUpdateView: Bool = true open var userInfo = [String: Primitive]() open var surface: Surface = .light { didSet { setNeedsUpdate() } } /// Whether the Control is selected or not. open override var isSelected: Bool { didSet { setNeedsUpdate() } } /// State of animating isHighlight. public var isHighlighting = false /// Whether the Control should handle the isHighlighted state. open var shouldHighlight: Bool { isHighlighting == false && onClickSubscriber != nil } /// Whether the Control is highlighted or not. open override var isHighlighted: Bool { didSet { if shouldHighlight { isHighlighting = true setNeedsUpdate() isHighlighting = false } } } /// Whether the Control is enabled or not. open override var isEnabled: Bool { didSet { setNeedsUpdate() } } //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- open func initialSetup() { if !initialSetupPerformed { initialSetupPerformed = true setup() setNeedsUpdate() } } open func setup() { backgroundColor = .clear translatesAutoresizingMaskIntoConstraints = false insetsLayoutMarginsFromSafeArea = false } open func updateView() { } open func updateAccessibility() { if isSelected { accessibilityTraits.insert(.selected) } else { accessibilityTraits.remove(.selected) } if isEnabled { accessibilityTraits.remove(.notEnabled) } else { accessibilityTraits.insert(.notEnabled) } } open func reset() { backgroundColor = .clear surface = .light isEnabled = true onClick = nil userInfo.removeAll() } //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- open override func layoutSubviews() { super.layoutSubviews() setNeedsUpdate() } //-------------------------------------------------- // MARK: - Accessibility //-------------------------------------------------- open var accessibilityAction: ((Control) -> Void)? private var _isAccessibilityElement: Bool = false open override var isAccessibilityElement: Bool { get { var block: AXBoolReturnBlock? // if #available(iOS 17, *) { // block = isAccessibilityElementBlock // } if block == nil { block = bridge_isAccessibilityElementBlock } if let block { return block() } else { return _isAccessibilityElement } } set { _isAccessibilityElement = newValue } } private var _accessibilityLabel: String? open override var accessibilityLabel: String? { get { var block: AXStringReturnBlock? // if #available(iOS 17, *) { // block = accessibilityLabelBlock // } if block == nil { block = bridge_accessibilityLabelBlock } if let block { return block() } else { return _accessibilityLabel } } set { _accessibilityLabel = newValue } } private var _accessibilityHint: String? open override var accessibilityHint: String? { get { var block: AXStringReturnBlock? // if #available(iOS 17, *) { // block = accessibilityHintBlock // } if block == nil { block = bridge_accessibilityHintBlock } if let block { return block() } else { return _accessibilityHint } } set { _accessibilityHint = newValue } } private var _accessibilityValue: String? open override var accessibilityValue: String? { get { var block: AXStringReturnBlock? // if #available(iOS 17, *) { // block = accessibilityHintBlock // } if block == nil { block = bridge_accessibilityValueBlock } if let block{ return block() } else { return _accessibilityValue } } set { _accessibilityValue = newValue } } open override func accessibilityActivate() -> Bool { 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 { if let block = accessibilityAction { block(self) } else if let block = bridge_accessibilityActivateBlock { value = block() } // } sendActions(for: .touchUpInside) return value } }