diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index b2395e84..3e96eeeb 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -46,7 +46,6 @@ EA5E30532950DDA60082B959 /* TitleLockup.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5E30522950DDA60082B959 /* TitleLockup.swift */; }; EA5E3058295105A40082B959 /* Tilelet.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5E3057295105A40082B959 /* Tilelet.swift */; }; EA5E305A29510F8B0082B959 /* EnumSubset.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5E305929510F8B0082B959 /* EnumSubset.swift */; }; - EA89200228AECF2A006B9984 /* UIButton+Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89200128AECF2A006B9984 /* UIButton+Publisher.swift */; }; EA89200428AECF4B006B9984 /* UITextField+Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89200328AECF4B006B9984 /* UITextField+Publisher.swift */; }; EA89200628B526D6006B9984 /* CheckboxGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89200528B526D6006B9984 /* CheckboxGroup.swift */; }; EA89201328B568D8006B9984 /* RadioBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89201228B568D8006B9984 /* RadioBox.swift */; }; @@ -78,7 +77,6 @@ EAB1D29C28A5618900DAE764 /* RadioButtonGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D29B28A5618900DAE764 /* RadioButtonGroup.swift */; }; EAB1D2CD28ABE76100DAE764 /* Withable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2CC28ABE76000DAE764 /* Withable.swift */; }; EAB1D2CF28ABEF2B00DAE764 /* Typography.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2CE28ABEF2B00DAE764 /* Typography.swift */; }; - EAB1D2E628AE842000DAE764 /* Publisher+Bind.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2E328AE842000DAE764 /* Publisher+Bind.swift */; }; EAB1D2EA28AE84AA00DAE764 /* UIControlPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2E928AE84AA00DAE764 /* UIControlPublisher.swift */; }; EAB5FED429267EB300998C17 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB5FED329267EB300998C17 /* UIView.swift */; }; EAB5FEED2927E1B200998C17 /* ButtonGroupPositionLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB5FEEC2927E1B200998C17 /* ButtonGroupPositionLayout.swift */; }; @@ -93,6 +91,7 @@ EAC9258C2911C9DE00091998 /* InputField.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC925872911C9DE00091998 /* InputField.swift */; }; 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 */; }; 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 */; }; @@ -163,7 +162,6 @@ EA5E30522950DDA60082B959 /* TitleLockup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TitleLockup.swift; sourceTree = ""; }; EA5E3057295105A40082B959 /* Tilelet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tilelet.swift; sourceTree = ""; }; EA5E305929510F8B0082B959 /* EnumSubset.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnumSubset.swift; sourceTree = ""; }; - EA89200128AECF2A006B9984 /* UIButton+Publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIButton+Publisher.swift"; sourceTree = ""; }; EA89200328AECF4B006B9984 /* UITextField+Publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextField+Publisher.swift"; sourceTree = ""; }; EA89200528B526D6006B9984 /* CheckboxGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxGroup.swift; sourceTree = ""; }; EA89201228B568D8006B9984 /* RadioBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioBox.swift; sourceTree = ""; }; @@ -196,7 +194,6 @@ EAB1D29B28A5618900DAE764 /* RadioButtonGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButtonGroup.swift; sourceTree = ""; }; EAB1D2CC28ABE76000DAE764 /* Withable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Withable.swift; sourceTree = ""; }; EAB1D2CE28ABEF2B00DAE764 /* Typography.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Typography.swift; sourceTree = ""; }; - EAB1D2E328AE842000DAE764 /* Publisher+Bind.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Publisher+Bind.swift"; sourceTree = ""; }; EAB1D2E928AE84AA00DAE764 /* UIControlPublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIControlPublisher.swift; sourceTree = ""; }; EAB5FED329267EB300998C17 /* UIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = ""; }; EAB5FEEC2927E1B200998C17 /* ButtonGroupPositionLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonGroupPositionLayout.swift; sourceTree = ""; }; @@ -210,6 +207,7 @@ EAC925872911C9DE00091998 /* InputField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InputField.swift; sourceTree = ""; }; 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 = ""; }; 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 = ""; }; @@ -370,12 +368,12 @@ EA0FC2BE2912D18200DF80B4 /* Buttons */, EAF7F092289985E200B287F5 /* Checkbox */, EA985BF3296C609E00F2FF2E /* Icon */, - EA1F265F28B945070033E859 /* RadioSwatch */, EA3362412892EF700071C351 /* Label */, 44604AD529CE195300E62B51 /* Line */, 445BA07629C07ABA0036A7C5 /* Notification */, EA89200B28B530F0006B9984 /* RadioBox */, EAF7F11428A1470D00B287F5 /* RadioButton */, + EA1F265F28B945070033E859 /* RadioSwatch */, EAC925852911C9DE00091998 /* TextFields */, EA5E304A294CBDBB0082B959 /* TileContainer */, EA5E3056295105930082B959 /* Tilelet */, @@ -414,6 +412,7 @@ isa = PBXGroup; children = ( EA4DB2FC28D3D0CA00103EE3 /* AnyEquatable.swift */, + EAF1FE9829D4850E00101452 /* Clickable.swift */, EAA5EEDF28F49DB3003B3210 /* Colorable.swift */, EA3361A9288B25E40071C351 /* Disabling.swift */, EA5E305929510F8B0082B959 /* EnumSubset.swift */, @@ -571,8 +570,6 @@ EAB1D2E228AE842000DAE764 /* Publishers */ = { isa = PBXGroup; children = ( - EAB1D2E328AE842000DAE764 /* Publisher+Bind.swift */, - EA89200128AECF2A006B9984 /* UIButton+Publisher.swift */, EAB1D2E928AE84AA00DAE764 /* UIControlPublisher.swift */, EA89200328AECF4B006B9984 /* UITextField+Publisher.swift */, EAD8D2C028BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift */, @@ -823,6 +820,7 @@ EAF7F09A2899B17200B287F5 /* CATransaction.swift in Sources */, EAF7F0A2289AFB3900B287F5 /* Errorable.swift in Sources */, EA985C7D297DAED300F2FF2E /* Primitive.swift in Sources */, + EAF1FE9929D4850E00101452 /* Clickable.swift in Sources */, EAB5FEF829393A7200998C17 /* ButtonGroupConstants.swift in Sources */, EA3361AF288B26310071C351 /* FormFieldable.swift in Sources */, 44604AD729CE196600E62B51 /* Line.swift in Sources */, @@ -833,7 +831,6 @@ EA985C1D296CD13600F2FF2E /* BundleManager.swift in Sources */, EA1F266528B945070033E859 /* RadioSwatch.swift in Sources */, EA4DB18528CA967F00103EE3 /* SelectorGroupHandlerBase.swift in Sources */, - EA89200228AECF2A006B9984 /* UIButton+Publisher.swift in Sources */, EAF7F0AB289B13FD00B287F5 /* TextStyleLabelAttribute.swift in Sources */, EAB1D29C28A5618900DAE764 /* RadioButtonGroup.swift in Sources */, EA985BE629688F6A00F2FF2E /* TileletBadgeModel.swift in Sources */, @@ -842,7 +839,6 @@ EA5E30532950DDA60082B959 /* TitleLockup.swift in Sources */, EAA5EEB528ECBFB4003B3210 /* ImageLabelAttribute.swift in Sources */, EAB5FF0129424ACB00998C17 /* UIControl.swift in Sources */, - EAB1D2E628AE842000DAE764 /* Publisher+Bind.swift in Sources */, EA985BF5296C60C000F2FF2E /* Icon.swift in Sources */, EA3361AA288B25E40071C351 /* Disabling.swift in Sources */, EA3361B6288B2A410071C351 /* Control.swift in Sources */, diff --git a/VDS/Classes/Control.swift b/VDS/Classes/Control.swift index 9c8da789..13f50e7f 100644 --- a/VDS/Classes/Control.swift +++ b/VDS/Classes/Control.swift @@ -10,24 +10,21 @@ import UIKit import Combine @objc(VDSControl) -open class Control: UIControl, Handlerable, ViewProtocol, Resettable, UserInfoable { +open class Control: UIControl, Handlerable, ViewProtocol, Resettable, UserInfoable, Clickable { //-------------------------------------------------- // MARK: - Combine Properties //-------------------------------------------------- public var subject = PassthroughSubject() public var subscribers = Set() - open var onClickSubscriber: AnyCancellable? { + public var onClickSubscriber: AnyCancellable? { willSet { if let onClickSubscriber { onClickSubscriber.cancel() } } - didSet { - enabledHighlight = onClickSubscriber != nil - } } - + //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- @@ -41,12 +38,12 @@ open class Control: UIControl, Handlerable, ViewProtocol, Resettable, UserInfoab open override var isSelected: Bool { didSet { didChange() } } - internal var enabledHighlight: Bool = false + public var touchUpInsideCount: Int = 0 var isHighlightAnimating = false open override var isHighlighted: Bool { didSet { - if isHighlightAnimating == false && enabledHighlight { + if isHighlightAnimating == false && touchUpInsideCount > 0 { isHighlightAnimating = true UIView.animate(withDuration: 0.1, animations: { [weak self] in self?.updateView() @@ -109,7 +106,7 @@ open class Control: UIControl, Handlerable, ViewProtocol, Resettable, UserInfoab sendActions(for: .touchUpInside) return true } - + //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- diff --git a/VDS/Components/Buttons/Button/ButtonBase.swift b/VDS/Components/Buttons/Button/ButtonBase.swift index 61b628ce..41633216 100644 --- a/VDS/Components/Buttons/Button/ButtonBase.swift +++ b/VDS/Components/Buttons/Button/ButtonBase.swift @@ -18,7 +18,8 @@ public protocol Buttonable: UIControl, Surfaceable, Disabling { } @objc(VDSButtonBase) -open class ButtonBase: UIButton, Buttonable, Handlerable, ViewProtocol, Resettable, UserInfoable { +open class ButtonBase: UIButton, Buttonable, Handlerable, ViewProtocol, Resettable, UserInfoable, Clickable { + //-------------------------------------------------- // MARK: - Configuration Properties //-------------------------------------------------- @@ -29,15 +30,12 @@ open class ButtonBase: UIButton, Buttonable, Handlerable, ViewProtocol, Resettab //-------------------------------------------------- public var subject = PassthroughSubject() public var subscribers = Set() - open var onClickSubscriber: AnyCancellable? { + public var onClickSubscriber: AnyCancellable? { willSet { if let onClickSubscriber { onClickSubscriber.cancel() } } - didSet { - enabledHighlight = onClickSubscriber != nil - } } //-------------------------------------------------- @@ -60,13 +58,13 @@ open class ButtonBase: UIButton, Buttonable, Handlerable, ViewProtocol, Resettab open var userInfo = [String: Primitive]() - internal var enabledHighlight: Bool = false + public var touchUpInsideCount: Int = 0 internal var isHighlightAnimating = false open override var isHighlighted: Bool { didSet { - if isHighlightAnimating == false && enabledHighlight { + if isHighlightAnimating == false && touchUpInsideCount > 0 { isHighlightAnimating = true UIView.animate(withDuration: 0.1, animations: { [weak self] in self?.updateView() diff --git a/VDS/Components/Checkbox/Checkbox.swift b/VDS/Components/Checkbox/Checkbox.swift index fc78cb7f..cfff406e 100644 --- a/VDS/Components/Checkbox/Checkbox.swift +++ b/VDS/Components/Checkbox/Checkbox.swift @@ -12,22 +12,9 @@ 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(VDSCheckbox) -public class Checkbox: CheckboxBase{} - -@objc(VDSSoloCheckbox) -public class SoloCheckbox: CheckboxBase{ - public override func initialSetup() { - super.initialSetup() - onClickSubscriber = publisher(for: .touchUpInside) - .sink { control in - control.toggle() - } - } -} - @objc(VDSCheckboxBase) -open class CheckboxBase: Control, Errorable { +open class Checkbox: Control, Errorable { + //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- @@ -164,6 +151,13 @@ open class CheckboxBase: Control, Errorable { //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- + open override func initialSetup() { + super.initialSetup() + onClick = { control in + control.toggle() + } + } + open override func setup() { super.setup() diff --git a/VDS/Components/Checkbox/CheckboxGroup.swift b/VDS/Components/Checkbox/CheckboxGroup.swift index 6b5412f0..d3e601c8 100644 --- a/VDS/Components/Checkbox/CheckboxGroup.swift +++ b/VDS/Components/Checkbox/CheckboxGroup.swift @@ -9,31 +9,24 @@ import Foundation import UIKit @objc(VDSCheckboxGroup) -public class CheckboxGroup: CheckboxGroupBase { - public override func didSelect(_ selectedControl: Checkbox) { - selectedControl.toggle() - if selectedControl.isSelected, showError{ - showError.toggle() - } - valueChanged() - } -} - -public class CheckboxGroupBase: SelectorGroupHandlerBase { +public class CheckboxGroup: SelectorGroupHandlerBase { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - - public override var selectorViews: [HandlerType] { + public var selectedHandlers: [Checkbox]? { + let selected = selectorViews.filter{ $0.isSelected == true } + guard selected.count > 0 else { return nil } + return selected + } + + public override var selectorViews: [Checkbox] { didSet { for selector in selectorViews { if !mainStackView.arrangedSubviews.contains(selector) { - selector - .publisher(for: .touchUpInside) - .sink { [weak self] handler in - self?.didSelect(handler) - }.store(in: &subscribers) + selector.onClick = { [weak self] handler in + self?.didSelect(handler) + } mainStackView.addArrangedSubview(selector) } } @@ -79,11 +72,13 @@ public class CheckboxGroupBase: SelectorGroupHandlerB mainStackView.pinToSuperView() } - - public var selectedHandlers: [HandlerType]? { - let selected = selectorViews.filter{ $0.isSelected == true } - guard selected.count > 0 else { return nil } - return selected + + public override func didSelect(_ selectedControl: Checkbox) { + selectedControl.toggle() + if selectedControl.isSelected, showError{ + showError.toggle() + } + valueChanged() } } diff --git a/VDS/Components/Notification/Notification.swift b/VDS/Components/Notification/Notification.swift index 5a7f18bc..aea2be6d 100644 --- a/VDS/Components/Notification/Notification.swift +++ b/VDS/Components/Notification/Notification.swift @@ -8,6 +8,7 @@ import Foundation import UIKit import VDSColorTokens +import Combine @objc(VDSNotification) /// A VDS Component that will render a view with information @@ -100,7 +101,7 @@ public class Notification: View { } //Text - open var titleText: String? { didSet{didChange()}} + open var titleText: String = "" { didSet{didChange()}} open var subTitleText: String? { didSet{didChange()}} @@ -117,6 +118,20 @@ public class Notification: View { $0.use = .secondary } + open var onCloseClick: ((Notification)->())? { + didSet { + if let onCloseClick { + onCloseSubscriber = closeButton + .publisher(for: UITapGestureRecognizer()) + .sink { _ in + onCloseClick(self) + } + } else { + onCloseSubscriber = nil + } + } + } + internal var onCloseSubscriber: AnyCancellable? //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- @@ -183,13 +198,7 @@ public class Notification: View { mainStackView.addArrangedSubview(typeIcon) mainStackView.addArrangedSubview(labelButtonView) mainStackView.addArrangedSubview(closeButton) - - labelButtonView.addArrangedSubview(labelsView) - - closeButton.publisher(for: UITapGestureRecognizer()).sink { [weak self] _ in - self?.didClickOnCloseButton() - }.store(in: &subscribers) - + //labels titleLabel.textColorConfiguration = textColorConfig.eraseToAnyColorable() subTitleLabel.textColorConfiguration = textColorConfig.eraseToAnyColorable() @@ -231,7 +240,7 @@ public class Notification: View { titleLabel.surface = surface subTitleLabel.surface = surface - if let titleText { + if !titleText.isEmpty { titleLabel.text = titleText labelsView.addArrangedSubview(titleLabel) } else { @@ -252,22 +261,14 @@ public class Notification: View { if let primaryButtonModel { primaryButton.text = primaryButtonModel.text primaryButton.surface = surface - primaryButton.onClickSubscriber = primaryButton - .publisher(for: .touchUpInside) - .sink(receiveValue: { button in - primaryButtonModel.onClick(button) - }) + primaryButton.onClick = primaryButtonModel.onClick buttons.append(primaryButton) } if let secondaryButtonModel { secondaryButton.text = secondaryButtonModel.text secondaryButton.surface = surface - secondaryButton.onClickSubscriber = secondaryButton - .publisher(for: .touchUpInside) - .sink(receiveValue: { button in - secondaryButtonModel.onClick(button) - }) + secondaryButton.onClick = secondaryButtonModel.onClick buttons.append(secondaryButton) } @@ -286,9 +287,5 @@ public class Notification: View { .pinTrailing() } } - - func didClickOnCloseButton() { - print("Notification close button clicked!!!") - } } diff --git a/VDS/Components/RadioBox/RadioBox.swift b/VDS/Components/RadioBox/RadioBox.swift index b655b03b..b71c0342 100644 --- a/VDS/Components/RadioBox/RadioBox.swift +++ b/VDS/Components/RadioBox/RadioBox.swift @@ -12,22 +12,8 @@ import VDSFormControlsTokens import Combine @objc(VDSRadioBox) -public class RadioBox: RadioBoxBase{} - -@objc(VDSSoloRadioBox) -public class SoloRadioBox: RadioBoxBase{ +open class RadioBox: Control { - public override func initialSetup() { - super.initialSetup() - onClickSubscriber = publisher(for: .touchUpInside) - .sink { control in - control.toggle() - } - } -} - -@objc(VDSRadioBoxBase) -open class RadioBoxBase: Control{ //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- @@ -138,7 +124,13 @@ open class RadioBoxBase: Control{ //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- - + open override func initialSetup() { + super.initialSetup() + onClick = { control in + control.toggle() + } + } + open override func setup() { super.setup() diff --git a/VDS/Components/RadioBox/RadioBoxGroup.swift b/VDS/Components/RadioBox/RadioBoxGroup.swift index e53a87c7..19f0358a 100644 --- a/VDS/Components/RadioBox/RadioBoxGroup.swift +++ b/VDS/Components/RadioBox/RadioBoxGroup.swift @@ -9,30 +9,18 @@ import Foundation import UIKit @objc(VDSRadioBoxGroup) -public class RadioBoxGroup: RadioBoxGroupBase { - - public override func didSelect(_ selectedControl: RadioBox) { - let oldSelectedControl = selectorViews.filter { $0.isSelected == true }.first - oldSelectedControl?.toggle() - selectedControl.toggle() - valueChanged() - } -} - -public class RadioBoxGroupBase: SelectorGroupSelectedHandlerBase { +public class RadioBoxGroup: SelectorGroupSelectedHandlerBase { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - public override var selectorViews: [HandlerType] { + public override var selectorViews: [RadioBox] { didSet { for selector in selectorViews { if !mainStackView.arrangedSubviews.contains(selector) { - selector - .publisher(for: .touchUpInside) - .sink { [weak self] handler in - self?.didSelect(handler) - }.store(in: &subscribers) + selector.onClick = { [weak self] handler in + self?.didSelect(handler) + } mainStackView.addArrangedSubview(selector) } } @@ -84,4 +72,11 @@ public class RadioBoxGroupBase: SelectorGroupSelected } }.store(in: &subscribers) } + + open override func didSelect(_ selectedControl: RadioBox) { + let oldSelectedControl = selectorViews.filter { $0.isSelected == true }.first + oldSelectedControl?.toggle() + selectedControl.toggle() + valueChanged() + } } diff --git a/VDS/Components/RadioButton/RadioButton.swift b/VDS/Components/RadioButton/RadioButton.swift index edc2fdc0..76d29b46 100644 --- a/VDS/Components/RadioButton/RadioButton.swift +++ b/VDS/Components/RadioButton/RadioButton.swift @@ -11,30 +11,8 @@ import VDSColorTokens import VDSFormControlsTokens @objc(VDSRadioButton) -public class RadioButton: RadioButtonBase { - //for groups allows "toggle" - open override func toggle() { - //removed error - if showError && isSelected == false { - showError.toggle() - } - isSelected.toggle() - } -} - -@objc(VDSSoloRadioButton) -public class SoloRadioButton: RadioButtonBase { - public override func initialSetup() { - super.initialSetup() - onClickSubscriber = publisher(for: .touchUpInside) - .sink { control in - control.toggle() - } - } -} - -@objc(VDSRadioButtonBase) -open class RadioButtonBase: Control, Errorable { +open class RadioButton: Control, Errorable { + //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- @@ -158,7 +136,13 @@ open class RadioButtonBase: Control, Errorable { //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- - + open override func initialSetup() { + super.initialSetup() + onClick = { control in + control.toggle() + } + } + open override func setup() { super.setup() diff --git a/VDS/Components/RadioButton/RadioButtonGroup.swift b/VDS/Components/RadioButton/RadioButtonGroup.swift index 136dbe6e..0ab6b06b 100644 --- a/VDS/Components/RadioButton/RadioButtonGroup.swift +++ b/VDS/Components/RadioButton/RadioButtonGroup.swift @@ -9,32 +9,18 @@ import Foundation import UIKit @objc(VDSRadioButtonGroup) -public class RadioButtonGroup: RadioButtonGroupBase { - - public override func didSelect(_ selectedControl: RadioButton) { - selectedHandler?.toggle() - selectedControl.toggle() - if showError { - showError = false - } - valueChanged() - } -} - -public class RadioButtonGroupBase: SelectorGroupSelectedHandlerBase { +public class RadioButtonGroup: SelectorGroupSelectedHandlerBase { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - public override var selectorViews: [HandlerType] { + public override var selectorViews: [RadioButton] { didSet { for selector in selectorViews { if !mainStackView.arrangedSubviews.contains(selector) { - selector - .publisher(for: .touchUpInside) - .sink { [weak self] handler in - self?.didSelect(handler) - }.store(in: &subscribers) + selector.onClick = { [weak self] handler in + self?.didSelect(handler) + } mainStackView.addArrangedSubview(selector) } } @@ -81,4 +67,22 @@ public class RadioButtonGroupBase: SelectorGroupSe mainStackView.pinToSuperView() } + + public override func didSelect(_ selectedControl: RadioButton) { + if let selectedHandler { + updateToggle(selectedHandler) + } + updateToggle(selectedControl) + if showError { + showError = false + } + valueChanged() + } + + private func updateToggle(_ radioButton: RadioButton) { + if radioButton.showError && radioButton.isSelected == false { + radioButton.showError.toggle() + } + radioButton.isSelected.toggle() + } } diff --git a/VDS/Components/RadioSwatch/RadioSwatch.swift b/VDS/Components/RadioSwatch/RadioSwatch.swift index 105e0dea..211a3ff5 100644 --- a/VDS/Components/RadioSwatch/RadioSwatch.swift +++ b/VDS/Components/RadioSwatch/RadioSwatch.swift @@ -12,26 +12,7 @@ import VDSFormControlsTokens import Combine @objc(VDSRadioSwatch) -public class RadioSwatch: RadioSwatchBase{ - //for groups allows "toggle" - open override func toggle() { - isSelected.toggle() - } -} - -@objc(VDSSoloRadioSwatch) -public class SolorRadioSwatch: RadioSwatchBase{ - public override func initialSetup() { - super.initialSetup() - onClickSubscriber = publisher(for: .touchUpInside) - .sink { control in - control.toggle() - } - } -} - -@objc(VDSRadioSwatchBase) -open class RadioSwatchBase: Control { +open class RadioSwatch: Control { //-------------------------------------------------- // MARK: - Initializers @@ -50,7 +31,7 @@ open class RadioSwatchBase: Control { //-------------------------------------------------- // MARK: - Public Properties - //-------------------------------------------------- + //-------------------------------------------------- public var selectorView = UIView().with { $0.translatesAutoresizingMaskIntoConstraints = false } @@ -78,7 +59,13 @@ open class RadioSwatchBase: Control { //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- - + open override func initialSetup() { + super.initialSetup() + onClick = { control in + control.toggle() + } + } + open override func setup() { super.setup() diff --git a/VDS/Components/RadioSwatch/RadioSwatchGroup.swift b/VDS/Components/RadioSwatch/RadioSwatchGroup.swift index 751d11b3..f3a86449 100644 --- a/VDS/Components/RadioSwatch/RadioSwatchGroup.swift +++ b/VDS/Components/RadioSwatch/RadioSwatchGroup.swift @@ -10,24 +10,12 @@ import UIKit import Combine @objc(VDSRadioSwatchGroup) -public class RadioSwatchGroup: RadioSwatchGroupBase { - - public override func didSelect(selector: RadioSwatch) { - selectedHandler?.toggle() - selector.toggle() - label.text = selector.text - didChange() - valueChanged() - } - -} - -public class RadioSwatchGroupBase: SelectorGroupSelectedHandlerBase, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UICollectionViewDelegate { +public class RadioSwatchGroup: SelectorGroupSelectedHandlerBase, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UICollectionViewDelegate { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - public override var selectorViews: [HandlerType] { + public override var selectorViews: [RadioSwatch] { didSet { collectionView.reloadData() } @@ -160,18 +148,20 @@ public class RadioSwatchGroupBase: SelectorGroupSe public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath) let handler = selectorViews[indexPath.row] - handler.subscribers.forEach{ $0.cancel() } - handler.subscribers.removeAll() - handler.publisher(for: .touchUpInside).sink { [weak self] handler in + handler.onClick = { [weak self] handler in self?.didSelect(selector: handler) - }.store(in: &handler.subscribers) + } cell.subviews.forEach { $0.removeFromSuperview() } cell.addSubview(handler) handler.pinToSuperView() return cell } - open func didSelect(selector: HandlerType) { - fatalError("Must override didSelect") + public func didSelect(selector: RadioSwatch) { + selectedHandler?.toggle() + selector.toggle() + label.text = selector.text + didChange() + valueChanged() } } diff --git a/VDS/Components/TextFields/EntryField/EntryField.swift b/VDS/Components/TextFields/EntryField/EntryField.swift index 321aa074..a773ac43 100644 --- a/VDS/Components/TextFields/EntryField/EntryField.swift +++ b/VDS/Components/TextFields/EntryField/EntryField.swift @@ -172,7 +172,6 @@ open class EntryField: Control { open override func setup() { super.setup() - enabledHighlight = false isAccessibilityElement = true accessibilityTraits = .button addSubview(stackView) diff --git a/VDS/Components/Tilelet/Tilelet.swift b/VDS/Components/Tilelet/Tilelet.swift index 12f940e3..c6d36858 100644 --- a/VDS/Components/Tilelet/Tilelet.swift +++ b/VDS/Components/Tilelet/Tilelet.swift @@ -13,6 +13,7 @@ import Combine @objc(VDSTilelet) open class Tilelet: TileContainer { + //-------------------------------------------------- // MARK: - Enums //-------------------------------------------------- @@ -61,8 +62,8 @@ open class Tilelet: TileContainer { //-------------------------------------------------- // MARK: - Public Properties - //-------------------------------------------------- - open override var onClickSubscriber: AnyCancellable? { + //-------------------------------------------------- + public override var onClickSubscriber: AnyCancellable? { didSet { isAccessibilityElement = onClickSubscriber != nil } diff --git a/VDS/Components/Toggle/Toggle.swift b/VDS/Components/Toggle/Toggle.swift index 499d394d..05349779 100644 --- a/VDS/Components/Toggle/Toggle.swift +++ b/VDS/Components/Toggle/Toggle.swift @@ -18,18 +18,7 @@ import Combine Knob: The circular indicator that slides on the container. */ @objc(VDSToggle) -public class Toggle: ToggleBase{ - public override func initialSetup() { - super.initialSetup() - publisher(for: .touchUpInside) - .sink { control in - control.toggle() - }.store(in: &subscribers) - } -} - -@objc(VDSToggleBase) -open class ToggleBase: Control { +open class Toggle: Control { //-------------------------------------------------- // MARK: - Enums //-------------------------------------------------- @@ -64,6 +53,7 @@ open class ToggleBase: Control { // MARK: - Private Properties //-------------------------------------------------- private var stackView = UIStackView().with { + $0.isUserInteractionEnabled = false $0.translatesAutoresizingMaskIntoConstraints = false $0.axis = .horizontal $0.distribution = .fill @@ -80,7 +70,7 @@ open class ToggleBase: Control { //-------------------------------------------------- // MARK: - Configuration Properties - //-------------------------------------------------- + //-------------------------------------------------- // Sizes are from InVision design specs. public let toggleSize = CGSize(width: 52, height: 24) public let toggleContainerSize = CGSize(width: 52, height: 44) @@ -223,14 +213,16 @@ open class ToggleBase: Control { //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- + open override func initialSetup() { + super.initialSetup() + onClick = { control in + control.toggle() + } + } + open override func setup() { super.setup() - - //add tapGesture to self - publisher(for: UITapGestureRecognizer()).sink { [weak self] _ in - self?.sendActions(for: .touchUpInside) - }.store(in: &subscribers) - + isAccessibilityElement = true accessibilityTraits = .button addSubview(stackView) @@ -288,7 +280,7 @@ open class ToggleBase: Control { toggleView.backgroundColor = toggleColorConfiguration.getColor(self) knobView.backgroundColor = knobColorConfiguration.getColor(self) } - + /// This will toggle the state of the Toggle and execute the actionBlock if provided. open func toggle() { isOn.toggle() diff --git a/VDS/Protocols/Clickable.swift b/VDS/Protocols/Clickable.swift new file mode 100644 index 00000000..4ffecf2b --- /dev/null +++ b/VDS/Protocols/Clickable.swift @@ -0,0 +1,39 @@ +// +// Clickable.swift +// VDS +// +// Created by Matt Bruce on 3/29/23. +// + +import Foundation +import UIKit +import Combine + +public protocol Clickable: Handlerable where Self: UIControl { + var touchUpInsideCount: Int { get set } + var onClickSubscriber: AnyCancellable? { get set } +} + +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 { + if let newValue { + onClickSubscriber = publisher(for: .touchUpInside) + .sink { c in + newValue(c) + } + } else { + onClickSubscriber?.cancel() + onClickSubscriber = nil + } + } + } +} diff --git a/VDS/Publishers/Publisher+Bind.swift b/VDS/Publishers/Publisher+Bind.swift deleted file mode 100644 index 33fcaa94..00000000 --- a/VDS/Publishers/Publisher+Bind.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// Publisher+Bind.swift -// VDS -// -// Created by Matt Bruce on 8/18/22. -// - -import Foundation -import Combine - -public typealias Binding = Subscriber - -public extension Publisher where Failure == Never { - func bind(subscriber: B) -> AnyCancellable - where B.Failure == Never, B.Input == Output { - - handleEvents(receiveSubscription: { subscription in - subscriber.receive(subscription: subscription) - }) - .sink { value in - _ = subscriber.receive(value) - } - } -} diff --git a/VDS/Publishers/UIButton+Publisher.swift b/VDS/Publishers/UIButton+Publisher.swift deleted file mode 100644 index 6d669dfd..00000000 --- a/VDS/Publishers/UIButton+Publisher.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// Button+Publisher.swift -// VDS -// -// Created by Matt Bruce on 8/18/22. -// - -import Foundation -import UIKit -import Combine - -extension UIButton { - public var tapPublisher: AnyPublisher { - publisher(for: .touchUpInside) - .eraseToAnyPublisher() - } -} diff --git a/VDS/Publishers/UIControlPublisher.swift b/VDS/Publishers/UIControlPublisher.swift index c7ccfa0c..6b8fa45d 100644 --- a/VDS/Publishers/UIControlPublisher.swift +++ b/VDS/Publishers/UIControlPublisher.swift @@ -21,10 +21,8 @@ public final class UIControlSubscription 0 { + c.touchUpInsideCount -= 1 } }