// // ViewProtocol.swift // VDS // // Created by Matt Bruce on 7/22/22. // import Foundation import UIKit import Combine public protocol ViewProtocol: AnyObject, Initable, Resettable, Enabling, Surfaceable { /// Set of Subscribers for any Publishers for this Control. var subscribers: Set { get set } /// Key of whether or not updateView() is called in setNeedsUpdate() var shouldUpdateView: Bool { get set } /// Key of whether or not updateAccessibility() is called in setNeedsUpdate() var shouldUpdateAccessibility: Bool { get set } /// Used for setting an implementation for the default Accessible Action var accessibilityAction: ((Self) -> Void)? { get set } /// Executed on initialization for this View. func initialSetup() /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. func setup() /// Used to make changes to the View based off a change events or from local properties. func updateView() /// Used to update any Accessibility properties. func updateAccessibility() } extension ViewProtocol { /// Called when there are changes in a View based off a change events or from local properties. public func setNeedsUpdate() { if shouldUpdateView { shouldUpdateView = false updateView() if shouldUpdateAccessibility { updateAccessibility() } shouldUpdateView = true } } } extension ViewProtocol where Self: UIView { /// Helper method for removing a superview and updating Self. public func removeFromSuperview(_ view: UIView){ if view.superview != nil { view.removeFromSuperview() setNeedsDisplay() } } } extension ViewProtocol where Self: UIControl { /// Helper method to assign a completion block to a specific UIControl Event using Combine and stored in the subscribers. public func addEvent(event: UIControl.Event, block: @escaping (Self)->()) { publisher(for: event) .sink(receiveValue: { c in block(c) }).store(in: &subscribers) } } public protocol AccessibilityUpdatable { // Basic accessibility var bridge_isAccessibilityElementBlock: AXBoolReturnBlock? { get set } var bridge_accessibilityLabelBlock: AXStringReturnBlock? { get set } var bridge_accessibilityValueBlock: AXStringReturnBlock? { get set } var bridge_accessibilityHintBlock: AXStringReturnBlock? { get set } var bridge_accessibilityActivateBlock: AXBoolReturnBlock? { get set } } extension NSObject: AccessibilityUpdatable { static var isAccessibilityElementBlockKey: UInt8 = 0 static var activateBlockKey: UInt8 = 1 static var valueBlockKey: UInt8 = 2 static var hintBlockKey: UInt8 = 3 static var labelBlockKey: UInt8 = 4 public var bridge_isAccessibilityElementBlock: AXBoolReturnBlock? { get { return objc_getAssociatedObject(self, &NSObject.isAccessibilityElementBlockKey) as? AXBoolReturnBlock } set { objc_setAssociatedObject(self, &NSObject.isAccessibilityElementBlockKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) // if #available(iOS 17, *) { // self.isAccessibilityElementBlock = newValue // } } } public var bridge_accessibilityActivateBlock: AXBoolReturnBlock? { get { return objc_getAssociatedObject(self, &NSObject.activateBlockKey) as? AXBoolReturnBlock } set { objc_setAssociatedObject(self, &NSObject.activateBlockKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) // if #available(iOS 17, *) { // self.accessibilityActivateBlock = newValue // } } } public var bridge_accessibilityValueBlock: AXStringReturnBlock? { get { return objc_getAssociatedObject(self, &NSObject.valueBlockKey) as? AXStringReturnBlock } set { objc_setAssociatedObject(self, &NSObject.valueBlockKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) // if #available(iOS 17, *) { // self.accessibilityValueBlock = newValue // } } } public var bridge_accessibilityHintBlock: AXStringReturnBlock? { get { return objc_getAssociatedObject(self, &NSObject.hintBlockKey) as? AXStringReturnBlock } set { objc_setAssociatedObject(self, &NSObject.hintBlockKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) // if #available(iOS 17, *) { // self.accessibilityHintBlock = newValue // } } } public var bridge_accessibilityLabelBlock: AXStringReturnBlock? { get { return objc_getAssociatedObject(self, &NSObject.labelBlockKey) as? AXStringReturnBlock } set { objc_setAssociatedObject(self, &NSObject.labelBlockKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) // if #available(iOS 17, *) { // self.accessibilityLabelBlock = newValue // } } } }