diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 3e96eeeb..01cb6e65 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -92,6 +92,7 @@ EAC9258F2911C9DE00091998 /* EntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC9258B2911C9DE00091998 /* EntryField.swift */; }; EAD8D2C128BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD8D2C028BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift */; }; EAF1FE9929D4850E00101452 /* Clickable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF1FE9829D4850E00101452 /* Clickable.swift */; }; + EAF1FE9B29DB1A6000101452 /* Changeable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF1FE9A29DB1A6000101452 /* Changeable.swift */; }; EAF7F0952899861000B287F5 /* Checkbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0932899861000B287F5 /* Checkbox.swift */; }; EAF7F09A2899B17200B287F5 /* CATransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0992899B17200B287F5 /* CATransaction.swift */; }; EAF7F09E289AAEC000B287F5 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F09D289AAEC000B287F5 /* Constants.swift */; }; @@ -208,6 +209,7 @@ EAC9258B2911C9DE00091998 /* EntryField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EntryField.swift; sourceTree = ""; }; EAD8D2C028BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIGestureRecognizer+Publisher.swift"; sourceTree = ""; }; EAF1FE9829D4850E00101452 /* Clickable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Clickable.swift; sourceTree = ""; }; + EAF1FE9A29DB1A6000101452 /* Changeable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Changeable.swift; sourceTree = ""; }; EAF7F0932899861000B287F5 /* Checkbox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Checkbox.swift; sourceTree = ""; }; EAF7F0992899B17200B287F5 /* CATransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CATransaction.swift; sourceTree = ""; }; EAF7F09D289AAEC000B287F5 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; @@ -412,6 +414,7 @@ isa = PBXGroup; children = ( EA4DB2FC28D3D0CA00103EE3 /* AnyEquatable.swift */, + EAF1FE9A29DB1A6000101452 /* Changeable.swift */, EAF1FE9829D4850E00101452 /* Clickable.swift */, EAA5EEDF28F49DB3003B3210 /* Colorable.swift */, EA3361A9288B25E40071C351 /* Disabling.swift */, @@ -818,6 +821,7 @@ EA3361BD288B2C760071C351 /* TypeAlias.swift in Sources */, EAB1D2CF28ABEF2B00DAE764 /* Typography.swift in Sources */, EAF7F09A2899B17200B287F5 /* CATransaction.swift in Sources */, + EAF1FE9B29DB1A6000101452 /* Changeable.swift in Sources */, EAF7F0A2289AFB3900B287F5 /* Errorable.swift in Sources */, EA985C7D297DAED300F2FF2E /* Primitive.swift in Sources */, EAF1FE9929D4850E00101452 /* Clickable.swift in Sources */, diff --git a/VDS/Classes/SelectorGroupHandlerBase.swift b/VDS/Classes/SelectorGroupHandlerBase.swift index 3b67019d..5fee8d6c 100644 --- a/VDS/Classes/SelectorGroupHandlerBase.swift +++ b/VDS/Classes/SelectorGroupHandlerBase.swift @@ -7,14 +7,23 @@ import Foundation import UIKit +import Combine -public class SelectorGroupHandlerBase: Control { +public class SelectorGroupHandlerBase: Control, Changeable { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- public var selectorViews: [HandlerType] = [] + public var onChangeSubscriber: AnyCancellable? { + willSet { + if let onChangeSubscriber { + onChangeSubscriber.cancel() + } + } + } + //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- diff --git a/VDS/Components/Checkbox/Checkbox.swift b/VDS/Components/Checkbox/Checkbox.swift index cfff406e..f0d690cd 100644 --- a/VDS/Components/Checkbox/Checkbox.swift +++ b/VDS/Components/Checkbox/Checkbox.swift @@ -7,13 +7,13 @@ import Foundation import UIKit +import Combine import VDSColorTokens import VDSFormControlsTokens -import Combine /// Checkboxes are a multi-select component through which a customer indicates a choice. If a binary choice, the component is a checkbox. If the choice has multiple options, the component is a ``CheckboxGroup``. @objc(VDSCheckboxBase) -open class Checkbox: Control, Errorable { +open class Checkbox: Control, Errorable, Changeable { //-------------------------------------------------- // MARK: - Initializers @@ -63,6 +63,14 @@ open class Checkbox: Control, Errorable { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- + public var onChangeSubscriber: AnyCancellable? { + willSet { + if let onChangeSubscriber { + onChangeSubscriber.cancel() + } + } + } + open var label = Label().with { $0.setContentCompressionResistancePriority(.required, for: .vertical) $0.textPosition = .left diff --git a/VDS/Components/RadioBox/RadioBox.swift b/VDS/Components/RadioBox/RadioBox.swift index b71c0342..f5f8d9b9 100644 --- a/VDS/Components/RadioBox/RadioBox.swift +++ b/VDS/Components/RadioBox/RadioBox.swift @@ -7,12 +7,12 @@ import Foundation import UIKit +import Combine import VDSColorTokens import VDSFormControlsTokens -import Combine @objc(VDSRadioBox) -open class RadioBox: Control { +open class RadioBox: Control, Changeable { //-------------------------------------------------- // MARK: - Initializers @@ -56,6 +56,14 @@ open class RadioBox: Control { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- + public var onChangeSubscriber: AnyCancellable? { + willSet { + if let onChangeSubscriber { + onChangeSubscriber.cancel() + } + } + } + open var textLabel = Label().with { $0.setContentCompressionResistancePriority(.required, for: .vertical) $0.textPosition = .left diff --git a/VDS/Components/RadioButton/RadioButton.swift b/VDS/Components/RadioButton/RadioButton.swift index 76d29b46..b3b35f3e 100644 --- a/VDS/Components/RadioButton/RadioButton.swift +++ b/VDS/Components/RadioButton/RadioButton.swift @@ -7,11 +7,12 @@ import Foundation import UIKit +import Combine import VDSColorTokens import VDSFormControlsTokens @objc(VDSRadioButton) -open class RadioButton: Control, Errorable { +open class RadioButton: Control, Errorable, Changeable { //-------------------------------------------------- // MARK: - Initializers @@ -61,6 +62,14 @@ open class RadioButton: Control, Errorable { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- + public var onChangeSubscriber: AnyCancellable? { + willSet { + if let onChangeSubscriber { + onChangeSubscriber.cancel() + } + } + } + open var label = Label().with { $0.setContentCompressionResistancePriority(.required, for: .vertical) $0.textPosition = .left diff --git a/VDS/Components/TextFields/EntryField/EntryField.swift b/VDS/Components/TextFields/EntryField/EntryField.swift index a773ac43..20f221cb 100644 --- a/VDS/Components/TextFields/EntryField/EntryField.swift +++ b/VDS/Components/TextFields/EntryField/EntryField.swift @@ -12,7 +12,7 @@ import VDSFormControlsTokens import Combine @objc(VDSEntryField) -open class EntryField: Control { +open class EntryField: Control, Changeable { //-------------------------------------------------- // MARK: - Enums //-------------------------------------------------- @@ -98,6 +98,14 @@ open class EntryField: Control { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- + public var onChangeSubscriber: AnyCancellable? { + willSet { + if let onChangeSubscriber { + onChangeSubscriber.cancel() + } + } + } + open var titleLabel = Label().with { $0.setContentCompressionResistancePriority(.required, for: .vertical) $0.attributes = [] diff --git a/VDS/Components/Toggle/Toggle.swift b/VDS/Components/Toggle/Toggle.swift index 05349779..0f6bc797 100644 --- a/VDS/Components/Toggle/Toggle.swift +++ b/VDS/Components/Toggle/Toggle.swift @@ -18,7 +18,7 @@ import Combine Knob: The circular indicator that slides on the container. */ @objc(VDSToggle) -open class Toggle: Control { +open class Toggle: Control, Changeable { //-------------------------------------------------- // MARK: - Enums //-------------------------------------------------- @@ -107,6 +107,14 @@ open class Toggle: Control { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- + public var onChangeSubscriber: AnyCancellable? { + willSet { + if let onChangeSubscriber { + onChangeSubscriber.cancel() + } + } + } + open var label = Label().with { $0.setContentCompressionResistancePriority(.required, for: .vertical) } diff --git a/VDS/Protocols/Changeable.swift b/VDS/Protocols/Changeable.swift new file mode 100644 index 00000000..850c9cf2 --- /dev/null +++ b/VDS/Protocols/Changeable.swift @@ -0,0 +1,31 @@ +// +// Changeable.swift +// VDS +// +// Created by Matt Bruce on 4/3/23. +// + +import Foundation +import UIKit +import Combine + +public protocol Changeable: Handlerable where Self: UIControl { + var onChangeSubscriber: AnyCancellable? { get set } +} + +extension Changeable { + public var onChange: ((Self) -> ())? { + get { return nil } + set { + if let newValue { + onChangeSubscriber = publisher(for: .valueChanged) + .sink { c in + newValue(c) + } + } else { + onChangeSubscriber?.cancel() + onChangeSubscriber = nil + } + } + } +} diff --git a/VDS/Protocols/Clickable.swift b/VDS/Protocols/Clickable.swift index 4ffecf2b..a202850a 100644 --- a/VDS/Protocols/Clickable.swift +++ b/VDS/Protocols/Clickable.swift @@ -15,13 +15,6 @@ public protocol Clickable: Handlerable where Self: UIControl { } extension Clickable { - public func addEvent(event: UIControl.Event, block: @escaping (Self)->()) { - publisher(for: event) - .sink(receiveValue: { c in - block(c) - }).store(in: &subscribers) - } - public var onClick: ((Self) -> ())? { get { return nil } set { diff --git a/VDS/Protocols/Handlerable.swift b/VDS/Protocols/Handlerable.swift index f9ad8770..41ef01b5 100644 --- a/VDS/Protocols/Handlerable.swift +++ b/VDS/Protocols/Handlerable.swift @@ -34,3 +34,12 @@ extension Handlerable where Self: UIView { subject.send() } } + +extension Handlerable where Self: UIControl { + public func addEvent(event: UIControl.Event, block: @escaping (Self)->()) { + publisher(for: event) + .sink(receiveValue: { c in + block(c) + }).store(in: &subscribers) + } +}