diff --git a/VDS/Classes/CollectionView.swift b/VDS/Classes/CollectionView.swift index 7a163e9d..41caa09e 100644 --- a/VDS/Classes/CollectionView.swift +++ b/VDS/Classes/CollectionView.swift @@ -9,13 +9,12 @@ import Foundation import UIKit import Combine -open class CollectionView: UICollectionView, ModelHandlerable, ViewProtocol, Resettable { +open class CollectionView: UICollectionView, ModelHandlerable, ViewProtocol, Resettable { //-------------------------------------------------- // MARK: - Combine Properties //-------------------------------------------------- - @Published public var model: ModelType = ModelType() - public var modelPublisher: Published.Publisher { $model } + public var subject = PassthroughSubject() public var subscribers = Set() //-------------------------------------------------- @@ -23,24 +22,18 @@ open class CollectionView: UICollectionView, ModelHandlera //-------------------------------------------------- private var initialSetupPerformed = false - @Proxy(\.model.surface) - open var surface: Surface - - @Proxy(\.model.disabled) - open var disabled: Bool { - didSet { - self.isEnabled = !disabled - } - } + open var surface: Surface = .light { didSet { subject.send() }} + open var disabled: Bool = false { didSet { isEnabled = !disabled } } + open var isEnabled: Bool { - get { !model.disabled } + get { !disabled } set { - //create local vars for clear coding - let disabled = !newValue - if model.disabled != disabled { - model.disabled = disabled + if disabled != !newValue { + disabled = !newValue } + isUserInteractionEnabled = isEnabled + subject.send() } } @@ -52,22 +45,14 @@ open class CollectionView: UICollectionView, ModelHandlera initialSetup() } - public required init(with model: ModelType) { - super.init(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) - initialSetup() - set(with: model) - } - - public required init(with model: ModelType, collectionViewLayout layout: UICollectionViewLayout) { + public required init(collectionViewLayout layout: UICollectionViewLayout) { super.init(frame: .zero, collectionViewLayout: layout) initialSetup() - set(with: model) } public override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) { super.init(frame: frame, collectionViewLayout: layout) initialSetup() - set(with: model) } public required init?(coder: NSCoder) { @@ -90,15 +75,14 @@ open class CollectionView: UICollectionView, ModelHandlera //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- - open func updateView(viewModel: ModelType) { + open func updateView() { fatalError("Implement updateView") } open func reset() { backgroundColor = .clear - if let model = model as? Resettable { - model.reset() - } + surface = .light + disabled = false } // MARK: - ViewProtocol diff --git a/VDS/Classes/CollectionViewCell.swift b/VDS/Classes/CollectionViewCell.swift index 881665f2..2a203dd8 100644 --- a/VDS/Classes/CollectionViewCell.swift +++ b/VDS/Classes/CollectionViewCell.swift @@ -10,7 +10,6 @@ import UIKit import Combine open class CollectionViewCell: UICollectionViewCell, ViewProtocol { - public typealias ModelType = ModelHandlerType.ModelType //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- @@ -18,10 +17,10 @@ open class CollectionViewCell: UICo public var modelHandler: ModelHandlerType = ModelHandlerType() - @Proxy(\.modelHandler.model.surface) - open var surface: Surface + @Proxy(\.modelHandler.surface) + open var surface: Surface - @Proxy(\.modelHandler.model.disabled) + @Proxy(\.modelHandler.disabled) open var disabled: Bool //-------------------------------------------------- @@ -32,12 +31,6 @@ open class CollectionViewCell: UICo initialSetup() } - public required init(with model: ModelType) { - super.init(frame: .zero) - initialSetup() - set(with: model) - } - public override init(frame: CGRect) { super.init(frame: frame) initialSetup() @@ -58,22 +51,7 @@ open class CollectionViewCell: UICo setup() } } - - //-------------------------------------------------- - // MARK: - Overrides - //-------------------------------------------------- - open func shouldUpdateView(viewModel: ModelType) -> Bool { - return modelHandler.shouldUpdateView(viewModel: viewModel) - } - - open func updateView(viewModel: ModelType) { - modelHandler.updateView(viewModel: viewModel) - } - - public func set(with model: ModelType) { - modelHandler.set(with: model) - } - + // MARK: - ViewProtocol /// Will be called only once. open func setup() { diff --git a/VDS/Classes/ColorConfiguration.swift b/VDS/Classes/ColorConfiguration.swift index da63fc3a..e86756fe 100644 --- a/VDS/Classes/ColorConfiguration.swift +++ b/VDS/Classes/ColorConfiguration.swift @@ -87,6 +87,10 @@ public protocol BinaryColorable{ var userTrueColor: Bool { get } } +extension BinaryColorable where Self: Control { + public var userTrueColor: Bool { return isSelected } +} + extension BinaryColorable where Self: Selectable { public var userTrueColor: Bool { return selected } } diff --git a/VDS/Classes/Control.swift b/VDS/Classes/Control.swift index 227d550e..2e06fd0e 100644 --- a/VDS/Classes/Control.swift +++ b/VDS/Classes/Control.swift @@ -10,13 +10,12 @@ import UIKit import Combine -open class Control: UIControl, ModelHandlerable, ViewProtocol, Resettable { +open class Control: UIControl, ModelHandlerable, ViewProtocol, Resettable { //-------------------------------------------------- // MARK: - Combine Properties //-------------------------------------------------- - @Published public var model: ModelType = ModelType() - public var modelPublisher: Published.Publisher { $model } + public var subject = PassthroughSubject() public var subscribers = Set() //-------------------------------------------------- @@ -24,25 +23,18 @@ open class Control: UIControl, ModelHandlerable, ViewProto //-------------------------------------------------- private var initialSetupPerformed = false - @Proxy(\.model.surface) - open var surface: Surface - - @Proxy(\.model.disabled) - open var disabled: Bool { - didSet { - self.isEnabled = !disabled - } - } + open var surface: Surface = .light { didSet { subject.send() }} + open var disabled: Bool = false { didSet { isEnabled = !disabled } } + open override var isEnabled: Bool { - get { !model.disabled } + get { !disabled } set { - //create local vars for clear coding - let disabled = !newValue - if model.disabled != disabled { - model.disabled = disabled + if disabled != !newValue { + disabled = !newValue } isUserInteractionEnabled = isEnabled + subject.send() } } @@ -54,12 +46,6 @@ open class Control: UIControl, ModelHandlerable, ViewProto initialSetup() } - public required init(with model: ModelType) { - super.init(frame: .zero) - initialSetup() - set(with: model) - } - public override init(frame: CGRect) { super.init(frame: .zero) initialSetup() @@ -92,15 +78,14 @@ open class Control: UIControl, ModelHandlerable, ViewProto //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- - open func updateView(viewModel: ModelType) { + open func updateView() { fatalError("Implement updateView") } open func reset() { backgroundColor = .clear - if let model = model as? Resettable { - model.reset() - } + surface = .light + disabled = false } // MARK: - ViewProtocol diff --git a/VDS/Classes/SelectorGroupHandlerBase.swift b/VDS/Classes/SelectorGroupHandlerBase.swift index 5f8b2b8a..d44e229f 100644 --- a/VDS/Classes/SelectorGroupHandlerBase.swift +++ b/VDS/Classes/SelectorGroupHandlerBase.swift @@ -8,7 +8,7 @@ import Foundation import UIKit -public class SelectorGroupHandlerBase>: Control { +public class SelectorGroupHandlerBase: Control { //-------------------------------------------------- // MARK: - Public Properties @@ -33,42 +33,10 @@ public class SelectorGroupHandlerBase ModelHandlerType? { - return selectorViews.first(where: { existingSelectorView in - return existingSelectorView.model.id == id - }) - } - - public func getCachedSelector(viewModel: ModelHandlerType.ModelType) -> ModelHandlerType.ModelType? { - if let index = model.selectors.firstIndex(where: { element in - return element.id == viewModel.id - }) { - return model.selectors[index] - } else { - return nil - } - } - - public func replace(viewModel: ModelHandlerType.ModelType){ - if let index = model.selectors.firstIndex(where: { element in - return element.id == viewModel.id - }) { - model.selectors[index] = viewModel - } - } - - public func createModelHandler(selector: ModelHandlerType.ModelType) -> ModelHandlerType { + + public func createModelHandler() -> ModelHandlerType { //create view - let newSelectorView = ModelHandlerType(with: selector) - - //add model update to the subscribers - newSelectorView - .modelPublisher - .sink { [weak self] model in - self?.replace(viewModel: model) - } - .store(in: &subscribers) + let newSelectorView = ModelHandlerType() //add the selectedPublisher for the change newSelectorView @@ -93,9 +61,8 @@ public class SelectorGroupHandlerBase>: SelectorGroupHandlerBase where GroupModelType.SelectorModelType == ModelHandlerType.ModelType { - - public var selectedModel: ModelHandlerType.ModelType? { - return model.selectedModel +public class SelectorGroupSelectedHandlerBase: SelectorGroupHandlerBase{ + public var selectedHandler: ModelHandlerType? { + return selectorViews.filter { $0.isSelected == true }.first } } diff --git a/VDS/Classes/View.swift b/VDS/Classes/View.swift index 57ac207b..b2bb0205 100644 --- a/VDS/Classes/View.swift +++ b/VDS/Classes/View.swift @@ -10,39 +10,31 @@ import UIKit import Combine -open class View: UIView, ModelHandlerable, ViewProtocol, Resettable { +open class View: UIView, ModelHandlerable, ViewProtocol, Resettable { //-------------------------------------------------- // MARK: - Combine Properties //-------------------------------------------------- - @Published public var model: ModelType = ModelType() - public var modelPublisher: Published.Publisher { $model } + public var subject = PassthroughSubject() public var subscribers = Set() //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- private var initialSetupPerformed = false - - @Proxy(\.model.surface) - open var surface: Surface - @Proxy(\.model.disabled) - open var disabled: Bool { - didSet { - self.isEnabled = !disabled - } - } + open var surface: Surface = .light { didSet { subject.send() }} + open var disabled: Bool = false { didSet { isEnabled = !disabled } } + open var isEnabled: Bool { - get { !model.disabled } + get { !disabled } set { - //create local vars for clear coding - let disabled = !newValue - if model.disabled != disabled { - model.disabled = disabled + if disabled != !newValue { + disabled = !newValue } isUserInteractionEnabled = isEnabled + subject.send() } } @@ -53,12 +45,6 @@ open class View: UIView, ModelHandlerable, ViewProtocol, R super.init(frame: .zero) initialSetup() } - - public required init(with model: ModelType) { - super.init(frame: .zero) - initialSetup() - set(with: model) - } public override init(frame: CGRect) { super.init(frame: .zero) @@ -85,15 +71,14 @@ open class View: UIView, ModelHandlerable, ViewProtocol, R //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- - open func updateView(viewModel: ModelType) { + open func updateView() { fatalError("Implement updateView") } open func reset() { backgroundColor = .clear - if let model = model as? Resettable { - model.reset() - } + surface = .light + disabled = false } // MARK: - ViewProtocol diff --git a/VDS/Components/Badge/Badge.swift b/VDS/Components/Badge/Badge.swift index 746a445b..b6aceede 100644 --- a/VDS/Components/Badge/Badge.swift +++ b/VDS/Components/Badge/Badge.swift @@ -11,44 +11,34 @@ import VDSColorTokens import VDSFormControlsTokens import Combine -public class Badge: BadgeBase{} +public class Badge: BadgeBase{} -open class BadgeBase: View { +open class BadgeBase: View, Accessable { private var label = Label() //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - @Proxy(\.model.fillColor) - open var fillColor: BadgeFillColor + open var fillColor: BadgeFillColor = .red { didSet { subject.send() }} - @Proxy(\.model.text) - open var text: String + open var text: String = "" { didSet { subject.send() }} - @Proxy(\.model.maxWidth) - open var maxWidth: CGFloat? + open var maxWidth: CGFloat? { didSet { subject.send() }} - @Proxy(\.model.numberOfLines) - open var numberOfLines: Int + open var numberOfLines: Int = 1 { didSet { subject.send() }} - @Proxy(\.model.accessibilityHintEnabled) - open var accessibilityHintEnabled: String? + open var accessibilityHintEnabled: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityHintDisabled) - open var accessibilityHintDisabled: String? + open var accessibilityHintDisabled: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityValueEnabled) - open var accessibilityValueEnabled: String? + open var accessibilityValueEnabled: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityValueDisabled) - open var accessibilityValueDisabled: String? + open var accessibilityValueDisabled: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityLabelEnabled) - open var accessibilityLabelEnabled: String? + open var accessibilityLabelEnabled: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityLabelDisabled) - open var accessibilityLabelDisabled: String? + open var accessibilityLabelDisabled: String? { didSet { subject.send() }} //-------------------------------------------------- // MARK: - Constraints @@ -90,9 +80,9 @@ open class BadgeBase: View { //-------------------------------------------------- // MARK: - Configuration //-------------------------------------------------- - public func backgroundColor(for fillColor: BadgeFillColor) -> UIColor { + public func backgroundColor() -> UIColor { var config: SurfaceColorConfiguration - switch model.fillColor { + switch fillColor { case .red: config = SurfaceColorConfiguration().with { $0.lightColor = VDSColor.backgroundBrandhighlight @@ -130,10 +120,10 @@ open class BadgeBase: View { } } - return config.getColor(model) + return config.getColor(self) } - public func textColorConfiguration(for fillColor: BadgeFillColor) -> AnyColorable { + public func textColorConfiguration() -> AnyColorable { switch fillColor { @@ -165,19 +155,23 @@ open class BadgeBase: View { //-------------------------------------------------- // MARK: - State //-------------------------------------------------- - open override func updateView(viewModel: ModelType) { - backgroundColor = backgroundColor(for: viewModel.fillColor) + open override func updateView() { + backgroundColor = backgroundColor() + label.textColorConfiguration = textColorConfiguration() + label.numberOfLines = numberOfLines + label.textPosition = .left + label.typograpicalStyle = .BoldBodySmall + label.text = text + label.surface = surface + label.disabled = disabled - label.textColorConfiguration = textColorConfiguration(for: viewModel.fillColor) - label.numberOfLines = viewModel.numberOfLines - - if let maxWidth = viewModel.maxWidth, let minWidth = minWidthConstraint?.constant, maxWidth > minWidth { + if let maxWidth = maxWidth, let minWidth = minWidthConstraint?.constant, maxWidth > minWidth { maxWidthConstraint?.constant = maxWidth maxWidthConstraint?.isActive = true } else { maxWidthConstraint?.isActive = false } - label.set(with: viewModel.label) + setAccessibilityLabel() } diff --git a/VDS/Components/Button/Button.swift b/VDS/Components/Button/Button.swift index d31d4807..a7ffe8e4 100644 --- a/VDS/Components/Button/Button.swift +++ b/VDS/Components/Button/Button.swift @@ -11,15 +11,12 @@ import VDSColorTokens import VDSFormControlsTokens import Combine -public class Button:ButtonBase{} - -open class ButtonBase: UIButton, ModelHandlerable, ViewProtocol, Resettable { +open class Button: UIButton, ModelHandlerable, ViewProtocol, Resettable, Useable { //-------------------------------------------------- // MARK: - Combine Properties //-------------------------------------------------- - @Published public var model: ModelType = ModelType() - public var modelPublisher: Published.Publisher { $model } + public var subject = PassthroughSubject() public var subscribers = Set() //-------------------------------------------------- @@ -32,39 +29,29 @@ open class ButtonBase: UIButton, ModelHandlerable, ViewP //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- - @Proxy(\.model.surface) - open var surface: Surface + open var text: String? { didSet { subject.send() }} - @Proxy(\.model.disabled) - open var disabled: Bool { - didSet { - isEnabled = !disabled - } - } + open var use: Use = .primary { didSet { subject.send() }} + + open var size: ButtonSize = .large { didSet { subject.send() }} - @Proxy(\.model.text) - open var text: String? + open var width: CGFloat? { didSet { subject.send() }} - @Proxy(\.model.use) - open var use: Use - - @Proxy(\.model.size) - open var size: ButtonSize - - @Proxy(\.model.width) - open var width: CGFloat? + open var surface: Surface = .light { didSet { subject.send() }} + open var disabled: Bool = false { didSet { isEnabled = !disabled } } + open override var isEnabled: Bool { - get { !model.disabled } + get { !disabled } set { - //create local vars for clear coding - let disabled = !newValue - if model.disabled != disabled { - model.disabled = disabled + if disabled != !newValue { + disabled = !newValue } isUserInteractionEnabled = isEnabled + subject.send() } } + //-------------------------------------------------- // MARK: - Configuration Properties @@ -113,16 +100,9 @@ open class ButtonBase: UIButton, ModelHandlerable, ViewP initialSetup() } - public required init(with model: ModelType) { - super.init(frame: .zero) - initialSetup() - set(with: model) - } - public override init(frame: CGRect) { super.init(frame: .zero) initialSetup() - set(with: model) } public required init?(coder: NSCoder) { @@ -149,15 +129,19 @@ open class ButtonBase: UIButton, ModelHandlerable, ViewP //only 1 of the 2 widths can be on at the same time widthConstraint = widthAnchor.constraint(equalToConstant: 0) - minWidthConstraint = widthAnchor.constraint(greaterThanOrEqualToConstant: model.size.minimumWidth) + minWidthConstraint = widthAnchor.constraint(greaterThanOrEqualToConstant: size.minimumWidth) //height - heightConstraint = heightAnchor.constraint(equalToConstant: model.size.height) + heightConstraint = heightAnchor.constraint(equalToConstant: size.height) heightConstraint?.isActive = true } open func reset() { - model = ModelType() + surface = .light + disabled = false + use = .primary + width = nil + size = .large accessibilityCustomActions = [] accessibilityTraits = .staticText } @@ -165,19 +149,19 @@ open class ButtonBase: UIButton, ModelHandlerable, ViewP //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- - open func updateView(viewModel: ModelType) { + open func updateView() { - let bgColor = buttonBackgroundColorConfiguration.getColor(viewModel) - let borderColor = buttonBorderColorConfiguration.getColor(viewModel) - let titleColor = buttonTitleColorConfiguration.getColor(viewModel) - let borderWidth = viewModel.use == .secondary ? 1.0 : 0.0 - let buttonHeight = viewModel.size.height + let bgColor = buttonBackgroundColorConfiguration.getColor(self) + let borderColor = buttonBorderColorConfiguration.getColor(self) + let titleColor = buttonTitleColorConfiguration.getColor(self) + let borderWidth = use == .secondary ? 1.0 : 0.0 + let buttonHeight = size.height let cornerRadius = buttonHeight / 2 - let minWidth = viewModel.size.minimumWidth - let font = viewModel.size == .large ? TypographicalStyle.BoldBodyLarge.font : TypographicalStyle.BoldBodySmall.font - let edgeInsets = viewModel.size.edgeInsets + let minWidth = size.minimumWidth + let font = size == .large ? TypographicalStyle.BoldBodyLarge.font : TypographicalStyle.BoldBodySmall.font + let edgeInsets = size.edgeInsets - if let text = viewModel.text { + if let text = text { setTitle(text, for: .normal) } else { setTitle("No ViewModel Text", for: .normal) @@ -193,7 +177,7 @@ open class ButtonBase: UIButton, ModelHandlerable, ViewP minWidthConstraint?.constant = minWidth heightConstraint?.constant = buttonHeight - if let width = viewModel.width, width > minWidth { + if let width, width > minWidth { widthConstraint?.constant = width widthConstraint?.isActive = true minWidthConstraint?.isActive = false diff --git a/VDS/Components/Button/ButtonModel.swift b/VDS/Components/Button/ButtonModel.swift index d5b7f0d9..140f5561 100644 --- a/VDS/Components/Button/ButtonModel.swift +++ b/VDS/Components/Button/ButtonModel.swift @@ -24,7 +24,6 @@ public struct DefaultButtonModel: ButtonModel { public var id = UUID() public var text: String? - public var typograpicalStyle: TypographicalStyle = .BoldBodyLarge public var surface: Surface = .light public var use: Use = .primary public var disabled: Bool = false diff --git a/VDS/Components/Checkbox/Checkbox.swift b/VDS/Components/Checkbox/Checkbox.swift index e14e5b2b..036af81a 100644 --- a/VDS/Components/Checkbox/Checkbox.swift +++ b/VDS/Components/Checkbox/Checkbox.swift @@ -11,9 +11,9 @@ import VDSColorTokens import VDSFormControlsTokens import Combine -public class Checkbox: CheckboxBase{} +public class Checkbox: CheckboxBase{} -public class SoloCheckbox: CheckboxBase{ +public class SoloCheckbox: CheckboxBase{ public override func initialSetup() { super.initialSetup() publisher(for: .touchUpInside) @@ -23,11 +23,39 @@ public class SoloCheckbox: CheckboxBase{ } } -open class CheckboxBase: Control { +open class CheckboxBase: Control, Accessable, BinaryColorable, Errorable { + //-------------------------------------------------- + // 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: - Private Properties //-------------------------------------------------- + private var shouldShowError: Bool { + guard showError && !disabled && errorText?.isEmpty == false else { return false } + return true + } + + private var shouldShowLabels: Bool { + guard labelText?.isEmpty == false || childText?.isEmpty == false else { return false } + return true + } + private var mainStackView: UIStackView = { return UIStackView().with { $0.translatesAutoresizingMaskIntoConstraints = false @@ -67,60 +95,77 @@ open class CheckboxBase: Control { }() //can't bind to @Proxy - open override var isSelected: Bool { - get { model.selected } - set { - if model.selected != newValue { - model.selected = newValue - } - } - } + open override var isSelected: Bool { didSet { subject.send() }} - @Proxy(\.model.labelText) - open var labelText: String? + open var labelText: String? { didSet { subject.send() }} - @Proxy(\.model.childText) - open var childText: String? + open var labelTextAttributes: [any LabelAttributeModel]? { didSet { subject.send() }} - @Proxy(\.model.showError) - open var showError: Bool - - @Proxy(\.model.errorText) - open var errorText: String? + open var childText: String? { didSet { subject.send() }} - @Proxy(\.model.inputId) - open var inputId: String? + open var childTextAttributes: [any LabelAttributeModel]? { didSet { subject.send() }} - @Proxy(\.model.value) - open var value: AnyHashable? + open var showError: Bool = false { didSet { subject.send() }} - @Proxy(\.model.dataAnalyticsTrack) - open var dataAnalyticsTrack: String? + open var errorText: String? { didSet { subject.send() }} - @Proxy(\.model.dataClickStream) - open var dataClickStream: String? + open var inputId: String? { didSet { subject.send() }} - @Proxy(\.model.dataTrack) - open var dataTrack: String? + open var value: AnyHashable? { didSet { subject.send() }} + + open var dataAnalyticsTrack: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityHintEnabled) - open var accessibilityHintEnabled: String? + open var dataClickStream: String? { didSet { subject.send() }} + + open var dataTrack: String? { didSet { subject.send() }} + + open var accessibilityHintEnabled: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityHintDisabled) - open var accessibilityHintDisabled: String? + open var accessibilityHintDisabled: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityValueEnabled) - open var accessibilityValueEnabled: String? + open var accessibilityValueEnabled: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityValueDisabled) - open var accessibilityValueDisabled: String? + open var accessibilityValueDisabled: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityLabelEnabled) - open var accessibilityLabelEnabled: String? + open var accessibilityLabelEnabled: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityLabelDisabled) - open var accessibilityLabelDisabled: String? + open var accessibilityLabelDisabled: String? { didSet { subject.send() }} + private var labelModel: DefaultLabelModel? { + guard let labelText = labelText else { return nil } + var model = DefaultLabelModel() + model.textPosition = .left + model.typograpicalStyle = .BoldBodyLarge + model.text = labelText + model.surface = surface + model.disabled = disabled + model.attributes = labelTextAttributes + return model + } + + private var childModel: DefaultLabelModel? { + guard let childText = childText else { return nil } + var model = DefaultLabelModel() + model.textPosition = .left + model.typograpicalStyle = .BodyLarge + model.text = childText + model.surface = surface + model.disabled = disabled + model.attributes = childTextAttributes + return model + } + + private var errorModel: DefaultLabelModel? { + guard let errorText = errorText, showError else { return nil } + var model = DefaultLabelModel() + model.textPosition = .left + model.typograpicalStyle = .BodyMedium + model.text = errorText + model.surface = surface + model.disabled = disabled + return model + } + //-------------------------------------------------- // MARK: - Constraints //-------------------------------------------------- @@ -159,7 +204,7 @@ open class CheckboxBase: Control { selectorWidthConstraint = selectorView.widthAnchor.constraint(equalToConstant: selectorSize.width) selectorWidthConstraint?.isActive = true - updateSelector(model) + updateSelector() mainStackView.topAnchor.constraint(equalTo: topAnchor).isActive = true mainStackView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true @@ -168,22 +213,32 @@ open class CheckboxBase: Control { } - func updateLabels(_ viewModel: ModelType) { + func updateLabels() { //deal with labels - if viewModel.shouldShowLabels { + if shouldShowLabels { //add the stackview to hold the 2 labels //top label - if let labelModel = viewModel.labelModel { - primaryLabel.set(with: labelModel) + if let labelText { + primaryLabel.textPosition = .left + primaryLabel.typograpicalStyle = .BoldBodyLarge + primaryLabel.text = labelText + primaryLabel.surface = surface + primaryLabel.disabled = disabled + primaryLabel.attributes = labelTextAttributes primaryLabel.isHidden = false } else { primaryLabel.isHidden = true } //bottom label - if let childModel = viewModel.childModel { - secondaryLabel.set(with: childModel) + if let childText { + secondaryLabel.textPosition = .left + secondaryLabel.typograpicalStyle = .BodyLarge + secondaryLabel.text = childText + secondaryLabel.surface = surface + secondaryLabel.disabled = disabled + secondaryLabel.attributes = childTextAttributes secondaryLabel.isHidden = false } else { secondaryLabel.isHidden = true @@ -199,20 +254,23 @@ open class CheckboxBase: Control { } //either add/remove the error from the main stack - if let errorModel = model.errorModel, model.shouldShowError { - errorLabel.set(with: errorModel) + if let errorText, shouldShowError { + errorLabel.textPosition = .left + errorLabel.typograpicalStyle = .BodyMedium + errorLabel.text = errorText + errorLabel.surface = surface + errorLabel.disabled = disabled mainStackView.spacing = 8 errorLabel.isHidden = false } else { mainStackView.spacing = 0 errorLabel.isHidden = true } - } public override func reset() { super.reset() - updateSelector(model) + updateSelector() setAccessibilityLabel() } @@ -229,12 +287,12 @@ open class CheckboxBase: Control { //-------------------------------------------------- // MARK: - State //-------------------------------------------------- - open override func updateView(viewModel: ModelType) { - updateLabels(viewModel) - updateSelector(viewModel) + open override func updateView() { + updateLabels() + updateSelector() setAccessibilityHint() - setAccessibilityValue(viewModel.selected) - setAccessibilityLabel(viewModel.selected) + setAccessibilityValue(isSelected) + setAccessibilityLabel(isSelected) setNeedsLayout() layoutIfNeeded() } @@ -294,11 +352,11 @@ open class CheckboxBase: Control { return checkboxSize } - open func updateSelector(_ viewModel: ModelType) { + open func updateSelector() { //get the colors - let backgroundColor = checkboxBackgroundColorConfiguration.getColor(viewModel) - let borderColor = checkboxBorderColorConfiguration.getColor(viewModel) - let checkColor = checkboxCheckColorConfiguration.getColor(viewModel) + let backgroundColor = checkboxBackgroundColorConfiguration.getColor(self) + let borderColor = checkboxBorderColorConfiguration.getColor(self) + let checkColor = checkboxCheckColorConfiguration.getColor(self) if let shapeLayer = shapeLayer, let sublayers = layer.sublayers, sublayers.contains(shapeLayer) { shapeLayer.removeFromSuperlayer() @@ -340,7 +398,7 @@ open class CheckboxBase: Control { shapeLayer.lineJoin = .miter shapeLayer.lineWidth = 2 CATransaction.withDisabledAnimations { - shapeLayer.strokeEnd = model.selected ? 1 : 0 + shapeLayer.strokeEnd = isSelected ? 1 : 0 } } } diff --git a/VDS/Components/Checkbox/CheckboxGroup.swift b/VDS/Components/Checkbox/CheckboxGroup.swift index 67d87993..8f1110a3 100644 --- a/VDS/Components/Checkbox/CheckboxGroup.swift +++ b/VDS/Components/Checkbox/CheckboxGroup.swift @@ -8,7 +8,7 @@ import Foundation import UIKit -public class CheckboxGroup: CheckboxGroupBase { +public class CheckboxGroup: CheckboxGroupBase { public override func didSelect(_ selectedControl: Checkbox) { selectedControl.toggle() if selectedControl.isSelected, showError{ @@ -18,13 +18,25 @@ public class CheckboxGroup: CheckboxGroupBase>: SelectorGroupHandlerBase { +public class CheckboxGroupBase: SelectorGroupHandlerBase { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- + + public override var selectorViews: [ModelHandlerType] { + didSet { + for selector in selectorViews { + if !mainStackView.arrangedSubviews.contains(selector) { + mainStackView.addArrangedSubview(selector) + } + } + } + } + + private var _showError: Bool = false public var showError: Bool { - get { model.showError } + get { _showError } set { var newShowError = newValue let anySelected = selectorViews.filter { $0.isSelected == true }.count > 0 @@ -34,8 +46,7 @@ public class CheckboxGroupBase{} +public class Label: LabelBase {} -open class LabelBase: UILabel, ModelHandlerable, ViewProtocol, Resettable { +open class LabelBase: UILabel, ModelHandlerable, ViewProtocol, Resettable { //-------------------------------------------------- // MARK: - Combine Properties //-------------------------------------------------- - @Published public var model: ModelType = ModelType() - public var modelPublisher: Published.Publisher { $model } + public var subject = PassthroughSubject() public var subscribers = Set() //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- - @Proxy(\.model.surface) - open var surface: Surface + open var surface: Surface = .light { didSet { subject.send() }} - @Proxy(\.model.disabled) - open var disabled: Bool { - didSet { - self.isEnabled = !disabled - } - } + open var disabled: Bool = false { didSet { isEnabled = !disabled } } + + open var attributes: [any LabelAttributeModel]? { didSet { subject.send() }} + open var typograpicalStyle: TypographicalStyle = .defaultStyle { didSet { subject.send() }} + + open var textPosition: TextPosition = .left { didSet { subject.send() }} + open override var isEnabled: Bool { - get { !model.disabled } + get { !disabled } set { - //create local vars for clear coding - let disabled = !newValue - if model.disabled != disabled { - model.disabled = disabled + if disabled != !newValue { + disabled = !newValue } isUserInteractionEnabled = isEnabled + subject.send() } } - @Proxy(\.model.attributes) - open var attributes: [any LabelAttributeModel]? - - @Proxy(\.model.typograpicalStyle) - open var typograpicalStyle: TypographicalStyle - - @Proxy(\.model.textPosition) - open var textPosition: TextPosition - - //can't use @Proxy here override open var text: String? { didSet { - if model.text != oldValue { - model.text = text - } + subject.send() } } @@ -81,17 +67,10 @@ open class LabelBase: UILabel, ModelHandlerable, ViewProt super.init(frame: .zero) initialSetup() } - - public required init(with model: ModelType) { - super.init(frame: .zero) - initialSetup() - set(with: model) - } public override init(frame: CGRect) { super.init(frame: .zero) initialSetup() - set(with: model) } public required init?(coder: NSCoder) { @@ -116,11 +95,13 @@ open class LabelBase: UILabel, ModelHandlerable, ViewProt open func setup() {} open func reset() { + surface = .light + disabled = false + attributes = nil + typograpicalStyle = .defaultStyle + textPosition = .left text = nil attributedText = nil - textColor = .black - font = TypographicalStyle.BodyLarge.font - textAlignment = .left accessibilityCustomActions = [] accessibilityTraits = .staticText numberOfLines = 0 @@ -129,17 +110,12 @@ open class LabelBase: UILabel, ModelHandlerable, ViewProt //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- - open func updateView(viewModel: ModelType) { - textAlignment = viewModel.textPosition.textAlignment - textColor = textColorConfiguration.getColor(viewModel) + open func updateView() { + textAlignment = textPosition.textAlignment + textColor = textColorConfiguration.getColor(self) + font = typograpicalStyle.font - if let vdsFont = viewModel.font { - font = vdsFont - } else { - font = TypographicalStyle.defaultStyle.font - } - - if let text = viewModel.text, let font = font, let textColor = textColor { + if let text = text, let font = font, let textColor = textColor { //clear the arrays holding actions accessibilityCustomActions = [] actions = [] @@ -149,9 +125,9 @@ open class LabelBase: UILabel, ModelHandlerable, ViewProt let mutableText = NSMutableAttributedString(string: text, attributes: startingAttributes) //set the local lineHeight/lineSpacing attributes - setStyleAttributes(viewModel: viewModel, attributedString: mutableText) + setStyleAttributes(attributedString: mutableText) - if let attributes = viewModel.attributes { + if let attributes = attributes { //loop through the models attributes for attribute in attributes { @@ -170,42 +146,40 @@ open class LabelBase: UILabel, ModelHandlerable, ViewProt } //only enabled if enabled and has actions - isUserInteractionEnabled = !viewModel.disabled && !actions.isEmpty + isUserInteractionEnabled = !disabled && !actions.isEmpty //set the attributed text attributedText = mutableText - } else { - text = viewModel.text } } // MARK: - Private Attributes - private func setStyleAttributes(viewModel: ModelType, attributedString: NSMutableAttributedString) { + private func setStyleAttributes(attributedString: NSMutableAttributedString) { //get the range let entireRange = NSRange(location: 0, length: attributedString.length) //set letterSpacing - if viewModel.typograpicalStyle.letterSpacing > 0.0 { - attributedString.addAttribute(.kern, value: viewModel.typograpicalStyle.letterSpacing, range: entireRange) + if typograpicalStyle.letterSpacing > 0.0 { + attributedString.addAttribute(.kern, value: typograpicalStyle.letterSpacing, range: entireRange) } //set lineHeight - if viewModel.typograpicalStyle.lineHeight > 0.0 { - let lineHeight = viewModel.typograpicalStyle.lineHeight + if typograpicalStyle.lineHeight > 0.0 { + let lineHeight = typograpicalStyle.lineHeight let adjustment = lineHeight > font.lineHeight ? 2.0 : 1.0 let baselineOffset = (lineHeight - font.lineHeight) / 2.0 / adjustment let paragraph = NSMutableParagraphStyle().with { $0.maximumLineHeight = lineHeight $0.minimumLineHeight = lineHeight - $0.alignment = viewModel.textPosition.textAlignment + $0.alignment = textPosition.textAlignment $0.lineBreakMode = lineBreakMode } attributedString.addAttribute(.baselineOffset, value: baselineOffset, range: entireRange) attributedString.addAttribute( .paragraphStyle, value: paragraph, range: entireRange) - } else if viewModel.textPosition != .left { + } else if textPosition != .left { let paragraph = NSMutableParagraphStyle().with { - $0.alignment = viewModel.textPosition.textAlignment + $0.alignment = textPosition.textAlignment $0.lineBreakMode = lineBreakMode } attributedString.addAttribute( .paragraphStyle, value: paragraph, range: entireRange) diff --git a/VDS/Components/RadioBox/RadioBox.swift b/VDS/Components/RadioBox/RadioBox.swift index a4e47b40..f7e4ec71 100644 --- a/VDS/Components/RadioBox/RadioBox.swift +++ b/VDS/Components/RadioBox/RadioBox.swift @@ -11,9 +11,9 @@ import VDSColorTokens import VDSFormControlsTokens import Combine -public class RadioBox: RadioBoxBase{} +public class RadioBox: RadioBoxBase{} -public class SolorRadioBox: RadioBoxBase{ +public class SolorRadioBox: RadioBoxBase{ public override func initialSetup() { super.initialSetup() @@ -24,8 +24,25 @@ public class SolorRadioBox: RadioBoxBase{ } } -open class RadioBoxBase: Control { +open class RadioBoxBase: Control, BinaryColorable, Accessable { + //-------------------------------------------------- + // 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: - Private Properties //-------------------------------------------------- @@ -67,61 +84,42 @@ open class RadioBoxBase: Control { $0.translatesAutoresizingMaskIntoConstraints = false } }() - - //can't bind to @Proxy - open override var isSelected: Bool { - get { model.selected } - set { - if model.selected != newValue { - model.selected = newValue - } - } - } - @Proxy(\.model.text) - open var text: String + open var text: String = "Default Text" { didSet { subject.send() }} - @Proxy(\.model.subText) - open var subText: String? + open var textAttributes: [any LabelAttributeModel]? { didSet { subject.send() }} + + open var subText: String? { didSet { subject.send() }} - @Proxy(\.model.subTextRight) - open var subTextRight: String? + open var subTextAttributes: [any LabelAttributeModel]? { didSet { subject.send() }} - @Proxy(\.model.strikethrough) - open var strikethrough: Bool + open var subTextRight: String? { didSet { subject.send() }} - @Proxy(\.model.inputId) - open var inputId: String? + open var subTextRightAttributes: [any LabelAttributeModel]? { didSet { subject.send() }} - @Proxy(\.model.value) - open var value: AnyHashable? + open var strikethrough: Bool = false { didSet { subject.send() }} + + open var inputId: String? { didSet { subject.send() }} + + open var value: AnyHashable? { didSet { subject.send() }} - @Proxy(\.model.dataAnalyticsTrack) - open var dataAnalyticsTrack: String? + open var dataAnalyticsTrack: String? { didSet { subject.send() }} - @Proxy(\.model.dataClickStream) - open var dataClickStream: String? + open var dataClickStream: String? { didSet { subject.send() }} - @Proxy(\.model.dataTrack) - open var dataTrack: String? + open var dataTrack: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityHintEnabled) - open var accessibilityHintEnabled: String? + open var accessibilityHintEnabled: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityHintDisabled) - open var accessibilityHintDisabled: String? + open var accessibilityHintDisabled: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityValueEnabled) - open var accessibilityValueEnabled: String? + open var accessibilityValueEnabled: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityValueDisabled) - open var accessibilityValueDisabled: String? + open var accessibilityValueDisabled: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityLabelEnabled) - open var accessibilityLabelEnabled: String? + open var accessibilityLabelEnabled: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityLabelDisabled) - open var accessibilityLabelDisabled: String? + open var accessibilityLabelDisabled: String? { didSet { subject.send() }} //functions //-------------------------------------------------- @@ -153,7 +151,7 @@ open class RadioBoxBase: Control { selectorLeftLabelStackView.spacing = 4 selectorLeftLabelStackView.isHidden = false - updateSelector(model) + updateSelector() selectorView.topAnchor.constraint(equalTo: topAnchor).isActive = true selectorView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true @@ -167,23 +165,38 @@ open class RadioBoxBase: Control { } - func updateLabels(_ viewModel: ModelType) { + func updateLabels() { //add the stackview to hold the 2 labels //text label - textLabel.set(with: viewModel.textModel) - + textLabel.textPosition = .left + textLabel.typograpicalStyle = .BoldBodyLarge + textLabel.text = text + textLabel.surface = surface + textLabel.disabled = disabled + textLabel.attributes = textAttributes + //subText label - if let subTextModel = viewModel.subTextModel { - subTextLabel.set(with: subTextModel) + if let subText { + subTextLabel.textPosition = .left + subTextLabel.typograpicalStyle = .BodyLarge + subTextLabel.text = subText + subTextLabel.surface = surface + subTextLabel.disabled = disabled + subTextLabel.attributes = subTextAttributes subTextLabel.isHidden = false } else { subTextLabel.isHidden = true } //subTextRight label - if let subTextRightModel = viewModel.subTextRightModel { - subTextRightLabel.set(with: subTextRightModel) + if let subTextRight { + subTextRightLabel.textPosition = .right + subTextRightLabel.typograpicalStyle = .BodyLarge + subTextRightLabel.text = subTextRight + subTextRightLabel.surface = surface + subTextRightLabel.disabled = disabled + subTextRightLabel.attributes = subTextRightAttributes subTextRightLabel.isHidden = false } else { subTextRightLabel.isHidden = true @@ -192,7 +205,7 @@ open class RadioBoxBase: Control { public override func reset() { super.reset() - updateSelector(model) + updateSelector() setAccessibilityLabel() } @@ -206,12 +219,12 @@ open class RadioBoxBase: Control { //-------------------------------------------------- // MARK: - State //-------------------------------------------------- - open override func updateView(viewModel: ModelType) { - updateLabels(viewModel) - updateSelector(viewModel) + open override func updateView() { + updateLabels() + updateSelector() setAccessibilityHint() - setAccessibilityValue(viewModel.selected) - setAccessibilityLabel(viewModel.selected) + setAccessibilityValue(isSelected) + setAccessibilityLabel(isSelected) setNeedsLayout() layoutIfNeeded() } @@ -256,11 +269,11 @@ open class RadioBoxBase: Control { private var shapeLayer: CAShapeLayer? - open func updateSelector(_ viewModel: ModelType) { + open func updateSelector() { //get the colors - let backgroundColor = radioBoxBackgroundColorConfiguration.getColor(viewModel) - let borderColor = radioBoxBorderColorConfiguration.getColor(viewModel) - let borderWidth = viewModel.selected ? selectorBorderWidthSelected : selectorBorderWidth + let backgroundColor = radioBoxBackgroundColorConfiguration.getColor(self) + let borderColor = radioBoxBorderColorConfiguration.getColor(self) + let borderWidth = isSelected ? selectorBorderWidthSelected : selectorBorderWidth selectorView.backgroundColor = backgroundColor selectorView.layer.borderColor = borderColor.cgColor @@ -278,12 +291,12 @@ open class RadioBoxBase: Control { open override func draw(_ layer: CALayer, in ctx: CGContext) { - let borderColor = radioBoxBorderColorConfiguration.getColor(model) + let borderColor = radioBoxBorderColorConfiguration.getColor(self) shapeLayer?.removeFromSuperlayer() shapeLayer = nil - if model.strikethrough { + if strikethrough { let bounds = selectorView.bounds let length = max(bounds.size.height, bounds.size.width) guard length > 0.0, shapeLayer == nil else { return } diff --git a/VDS/Components/RadioBox/RadioBoxGroup.swift b/VDS/Components/RadioBox/RadioBoxGroup.swift index 74059739..5a0e323a 100644 --- a/VDS/Components/RadioBox/RadioBoxGroup.swift +++ b/VDS/Components/RadioBox/RadioBoxGroup.swift @@ -8,7 +8,7 @@ import Foundation import UIKit -public class RadioBoxGroup: RadioBoxGroupBase { +public class RadioBoxGroup: RadioBoxGroupBase { public override func didSelect(_ selectedControl: RadioBox) { let oldSelectedControl = selectorViews.filter { $0.isSelected == true }.first @@ -18,8 +18,21 @@ public class RadioBoxGroup: RadioBoxGroupBase>: SelectorGroupSelectedHandlerBase { - +public class RadioBoxGroupBase: SelectorGroupSelectedHandlerBase { + + //-------------------------------------------------- + // MARK: - Public Properties + //-------------------------------------------------- + public override var selectorViews: [ModelHandlerType] { + didSet { + for selector in selectorViews { + if !mainStackView.arrangedSubviews.contains(selector) { + mainStackView.addArrangedSubview(selector) + } + } + } + } + //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- @@ -68,22 +81,6 @@ public class RadioBoxGroupBase{ +public class RadioButton: RadioButtonBase { //for groups allows "toggle" open override func toggle() { //removed error @@ -21,7 +21,7 @@ public class RadioButton: RadioButtonBase{ } } -public class SoloRadioButton: RadioButtonBase{ +public class SoloRadioButton: RadioButtonBase { public override func initialSetup() { super.initialSetup() publisher(for: .touchUpInside) @@ -31,11 +31,38 @@ public class SoloRadioButton: RadioButtonBase{ } } -open class RadioButtonBase: Control { +open class RadioButtonBase: Control, Accessable, BinaryColorable, Errorable { + //-------------------------------------------------- + // 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: - Private Properties //-------------------------------------------------- + private var shouldShowError: Bool { + guard showError && !disabled && errorText?.isEmpty == false else { return false } + return true + } + + private var shouldShowLabels: Bool { + guard labelText?.isEmpty == false || childText?.isEmpty == false else { return false } + return true + } + private var mainStackView: UIStackView = { return UIStackView().with { $0.translatesAutoresizingMaskIntoConstraints = false @@ -73,61 +100,40 @@ open class RadioButtonBase: Control { $0.translatesAutoresizingMaskIntoConstraints = false } }() - - //can't bind to @Proxy - open override var isSelected: Bool { - get { model.selected } - set { - if model.selected != newValue { - model.selected = newValue - } - } - } - @Proxy(\.model.labelText) - open var labelText: String? + open var labelText: String? { didSet { subject.send() }} - @Proxy(\.model.childText) - open var childText: String? + open var labelTextAttributes: [any LabelAttributeModel]? { didSet { subject.send() }} + + open var childText: String? { didSet { subject.send() }} - @Proxy(\.model.showError) - open var showError: Bool - - @Proxy(\.model.errorText) - open var errorText: String? + open var childTextAttributes: [any LabelAttributeModel]? { didSet { subject.send() }} - @Proxy(\.model.inputId) - open var inputId: String? + open var showError: Bool = false { didSet { subject.send() }} + + open var errorText: String? { didSet { subject.send() }} - @Proxy(\.model.value) - open var value: AnyHashable? - - @Proxy(\.model.dataAnalyticsTrack) - open var dataAnalyticsTrack: String? + open var inputId: String? { didSet { subject.send() }} - @Proxy(\.model.dataClickStream) - open var dataClickStream: String? + open var value: AnyHashable? { didSet { subject.send() }} + + open var dataAnalyticsTrack: String? { didSet { subject.send() }} - @Proxy(\.model.dataTrack) - open var dataTrack: String? + open var dataClickStream: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityHintEnabled) - open var accessibilityHintEnabled: String? + open var dataTrack: String? { didSet { subject.send() }} + + open var accessibilityHintEnabled: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityHintDisabled) - open var accessibilityHintDisabled: String? + open var accessibilityHintDisabled: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityValueEnabled) - open var accessibilityValueEnabled: String? + open var accessibilityValueEnabled: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityValueDisabled) - open var accessibilityValueDisabled: String? + open var accessibilityValueDisabled: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityLabelEnabled) - open var accessibilityLabelEnabled: String? + open var accessibilityLabelEnabled: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityLabelDisabled) - open var accessibilityLabelDisabled: String? + open var accessibilityLabelDisabled: String? { didSet { subject.send() }} //-------------------------------------------------- // MARK: - Constraints @@ -167,7 +173,7 @@ open class RadioButtonBase: Control { selectorWidthConstraint = selectorView.widthAnchor.constraint(equalToConstant: selectorSize.width) selectorWidthConstraint?.isActive = true - updateSelector(model) + updateSelector() mainStackView.topAnchor.constraint(equalTo: topAnchor).isActive = true mainStackView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true @@ -176,22 +182,32 @@ open class RadioButtonBase: Control { } - func updateLabels(_ viewModel: ModelType) { + func updateLabels() { //deal with labels - if viewModel.shouldShowLabels { + if shouldShowLabels { //add the stackview to hold the 2 labels //top label - if let labelModel = viewModel.labelModel { - primaryLabel.set(with: labelModel) + if let labelText { + primaryLabel.textPosition = .left + primaryLabel.typograpicalStyle = .BoldBodyLarge + primaryLabel.text = labelText + primaryLabel.surface = surface + primaryLabel.disabled = disabled + primaryLabel.attributes = labelTextAttributes primaryLabel.isHidden = false } else { primaryLabel.isHidden = true } //bottom label - if let childModel = viewModel.childModel { - secondaryLabel.set(with: childModel) + if let childText { + secondaryLabel.textPosition = .left + secondaryLabel.typograpicalStyle = .BodyLarge + secondaryLabel.text = childText + secondaryLabel.surface = surface + secondaryLabel.disabled = disabled + secondaryLabel.attributes = childTextAttributes secondaryLabel.isHidden = false } else { secondaryLabel.isHidden = true @@ -207,8 +223,12 @@ open class RadioButtonBase: Control { } //either add/remove the error from the main stack - if let errorModel = model.errorModel, model.shouldShowError { - errorLabel.set(with: errorModel) + if let errorText, shouldShowError { + errorLabel.textPosition = .left + errorLabel.typograpicalStyle = .BodyMedium + errorLabel.text = errorText + errorLabel.surface = surface + errorLabel.disabled = disabled mainStackView.spacing = 8 errorLabel.isHidden = false } else { @@ -220,7 +240,7 @@ open class RadioButtonBase: Control { public override func reset() { super.reset() - updateSelector(model) + updateSelector() setAccessibilityLabel() } @@ -239,12 +259,12 @@ open class RadioButtonBase: Control { //-------------------------------------------------- // MARK: - State //-------------------------------------------------- - open override func updateView(viewModel: ModelType) { - updateLabels(viewModel) - updateSelector(viewModel) + open override func updateView() { + updateLabels() + updateSelector() setAccessibilityHint() - setAccessibilityValue(viewModel.selected) - setAccessibilityLabel(viewModel.selected) + setAccessibilityValue(isSelected) + setAccessibilityLabel(isSelected) setNeedsLayout() layoutIfNeeded() } @@ -295,7 +315,7 @@ open class RadioButtonBase: Control { radioButtonSize } - open func updateSelector(_ viewModel: ModelType) { + open func updateSelector() { if let shapeLayer = shapeLayer, let sublayers = layer.sublayers, sublayers.contains(shapeLayer) { shapeLayer.removeFromSuperlayer() @@ -307,9 +327,9 @@ open class RadioButtonBase: Control { guard length > 0.0, shapeLayer == nil else { return } //get the colors - let backgroundColor = radioButtonBackgroundColorConfiguration.getColor(viewModel) - let borderColor = radioButtonBorderColorConfiguration.getColor(viewModel) - let radioSelectedColor = radioButtonCheckColorConfiguration.getColor(viewModel) + let backgroundColor = radioButtonBackgroundColorConfiguration.getColor(self) + let borderColor = radioButtonBorderColorConfiguration.getColor(self) + let radioSelectedColor = radioButtonCheckColorConfiguration.getColor(self) selectorView.backgroundColor = backgroundColor selectorView.layer.borderColor = borderColor.cgColor diff --git a/VDS/Components/RadioButton/RadioButtonGroup.swift b/VDS/Components/RadioButton/RadioButtonGroup.swift index 7d48e404..57f3c0e3 100644 --- a/VDS/Components/RadioButton/RadioButtonGroup.swift +++ b/VDS/Components/RadioButton/RadioButtonGroup.swift @@ -8,7 +8,7 @@ import Foundation import UIKit -public class RadioButtonGroup: RadioButtonGroupBase { +public class RadioButtonGroup: RadioButtonGroupBase { public override func didSelect(_ selectedControl: RadioButton) { let oldSelectedControl = selectorViews.filter { $0.isSelected == true }.first @@ -21,22 +21,33 @@ public class RadioButtonGroup: RadioButtonGroupBase>: SelectorGroupSelectedHandlerBase { +public class RadioButtonGroupBase: SelectorGroupSelectedHandlerBase { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- + public override var selectorViews: [ModelHandlerType] { + didSet { + for selector in selectorViews { + if !mainStackView.arrangedSubviews.contains(selector) { + mainStackView.addArrangedSubview(selector) + } + } + } + } + + private var _showError: Bool = false public var showError: Bool { - get { model.showError } + get { _showError } set { var newShowError = newValue - if selectedModel != nil, newShowError { + if selectedModelHandler != nil, newShowError { newShowError = false } selectorViews.forEach { handler in handler.showError = newShowError } - model.showError = newShowError + _showError = newShowError } } @@ -68,22 +79,6 @@ public class RadioButtonGroupBase{ +public class RadioSwatch: RadioSwatchBase{ public override func initialSetup() { super.initialSetup() publisher(for: .touchUpInside) @@ -21,8 +21,26 @@ public class RadioSwatch: RadioSwatchBase{ } } -open class RadioSwatchBase: Control { - +open class RadioSwatchBase: Control, Accessable, DataTrackable, BinaryColorable { + + //-------------------------------------------------- + // 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: - Public Properties //-------------------------------------------------- @@ -38,55 +56,38 @@ open class RadioSwatchBase: Control { $0.contentMode = .scaleAspectFit } }() + + open var fillImage: UIImage? { didSet { subject.send() }} + + open var text: String = "" { didSet { subject.send() }} + + open var primaryColor: UIColor? { didSet { subject.send() }} + + open var secondaryColor: UIColor? { didSet { subject.send() }} + + open var strikethrough: Bool = false { didSet { subject.send() }} - //can't bind to @Proxy - open override var isSelected: Bool { - get { model.selected } - set { - if model.selected != newValue { - model.selected = newValue - } - } - } - - @Proxy(\.model.text) - open var text: String - - @Proxy(\.model.strikethrough) - open var strikethrough: Bool + open var inputId: String? { didSet { subject.send() }} - @Proxy(\.model.inputId) - open var inputId: String? + open var value: AnyHashable? { didSet { subject.send() }} + + open var dataAnalyticsTrack: String? { didSet { subject.send() }} - @Proxy(\.model.value) - open var value: AnyHashable? - - @Proxy(\.model.dataAnalyticsTrack) - open var dataAnalyticsTrack: String? + open var dataClickStream: String? { didSet { subject.send() }} - @Proxy(\.model.dataClickStream) - open var dataClickStream: String? + open var dataTrack: String? { didSet { subject.send() }} - @Proxy(\.model.dataTrack) - open var dataTrack: String? - - @Proxy(\.model.accessibilityHintEnabled) - open var accessibilityHintEnabled: String? + open var accessibilityHintEnabled: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityHintDisabled) - open var accessibilityHintDisabled: String? + open var accessibilityHintDisabled: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityValueEnabled) - open var accessibilityValueEnabled: String? + open var accessibilityValueEnabled: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityValueDisabled) - open var accessibilityValueDisabled: String? + open var accessibilityValueDisabled: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityLabelEnabled) - open var accessibilityLabelEnabled: String? + open var accessibilityLabelEnabled: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityLabelDisabled) - open var accessibilityLabelDisabled: String? + open var accessibilityLabelDisabled: String? { didSet { subject.send() }} //functions //-------------------------------------------------- @@ -107,7 +108,7 @@ open class RadioSwatchBase: Control { selectorView.addSubview(fillView) - updateSelector(model) + updateSelector() selectorView.topAnchor.constraint(equalTo: topAnchor).isActive = true selectorView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true @@ -128,7 +129,7 @@ open class RadioSwatchBase: Control { public override func reset() { super.reset() - updateSelector(model) + updateSelector() setAccessibilityLabel() } @@ -140,11 +141,11 @@ open class RadioSwatchBase: Control { //-------------------------------------------------- // MARK: - State //-------------------------------------------------- - open override func updateView(viewModel: ModelType) { - updateSelector(viewModel) + open override func updateView() { + updateSelector() setAccessibilityHint() - setAccessibilityValue(viewModel.selected) - setAccessibilityLabel(viewModel.selected) + setAccessibilityValue(isSelected) + setAccessibilityLabel(isSelected) setNeedsLayout() layoutIfNeeded() } @@ -199,15 +200,15 @@ open class RadioSwatchBase: Control { return swatchSize } - open func updateSelector(_ viewModel: ModelType) { + open func updateSelector() { //get the colors - let backgroundColor = radioSwatchBackgroundColorConfiguration.getColor(viewModel) - let borderColor = viewModel.selected ? radioSwatchBorderColorConfiguration.getColor(viewModel) : .clear - let fillBorderColor = radioSwatchFillBorderColorConfiguration.getColor(viewModel) + let backgroundColor = radioSwatchBackgroundColorConfiguration.getColor(self) + let borderColor = isSelected ? radioSwatchBorderColorConfiguration.getColor(self) : .clear + let fillBorderColor = radioSwatchFillBorderColorConfiguration.getColor(self) selectorView.backgroundColor = backgroundColor selectorView.layer.borderColor = borderColor.cgColor selectorView.layer.cornerRadius = selectorView.bounds.width * 0.5 - selectorView.layer.borderWidth = viewModel.selected ? selectorBorderWidth : 0 + selectorView.layer.borderWidth = isSelected ? selectorBorderWidth : 0 selectorView.layer.masksToBounds = true gradientLayer?.removeFromSuperlayer() @@ -215,14 +216,14 @@ open class RadioSwatchBase: Control { var fillColorBackground: UIColor = .clear - if let fillImage = viewModel.fillImage { - fillView.image = viewModel.disabled ? fillImage.image(alpha: disabledAlpha) : fillImage + if let fillImage { + fillView.image = disabled ? fillImage.image(alpha: disabledAlpha) : fillImage } else { fillView.image = nil - if let primary = viewModel.primaryColor, let secondary = viewModel.secondaryColor { - let firstColor = viewModel.disabled ? primary.withAlphaComponent(disabledAlpha) : primary - let secondColor = viewModel.disabled ? secondary.withAlphaComponent(disabledAlpha) : secondary + if let primary = primaryColor, let secondary = secondaryColor { + let firstColor = disabled ? primary.withAlphaComponent(disabledAlpha) : primary + let secondColor = disabled ? secondary.withAlphaComponent(disabledAlpha) : secondary let gradient = CAGradientLayer() gradientLayer = gradient gradient.frame = fillView.bounds @@ -231,10 +232,11 @@ open class RadioSwatchBase: Control { gradient.transform = CATransform3DMakeRotation(135.0 / 180.0 * .pi, 0.0, 0.0, 1.0) fillView.layer.addSublayer(gradient) } else { - fillColorBackground = viewModel.primaryColor ?? .white + fillColorBackground = primaryColor ?? .white } } - fillView.backgroundColor = viewModel.disabled ? fillColorBackground.withAlphaComponent(disabledAlpha) : fillColorBackground + + fillView.backgroundColor = disabled ? fillColorBackground.withAlphaComponent(disabledAlpha) : fillColorBackground fillView.layer.borderColor = fillBorderColor.cgColor fillView.layer.cornerRadius = fillView.bounds.width * 0.5 fillView.layer.borderWidth = selectorBorderWidth @@ -250,12 +252,12 @@ open class RadioSwatchBase: Control { open override func draw(_ layer: CALayer, in ctx: CGContext) { - let borderColor = radioSwatchBorderColorConfiguration.getColor(model) + let borderColor = radioSwatchBorderColorConfiguration.getColor(self) shapeLayer?.removeFromSuperlayer() shapeLayer = nil - if model.strikethrough { + if strikethrough { let bounds = selectorView.bounds let length = max(bounds.size.height, bounds.size.width) guard length > 0.0, shapeLayer == nil else { return } diff --git a/VDS/Components/RadioSwatch/RadioSwatchGroup.swift b/VDS/Components/RadioSwatch/RadioSwatchGroup.swift index 1769a89a..e6f1a242 100644 --- a/VDS/Components/RadioSwatch/RadioSwatchGroup.swift +++ b/VDS/Components/RadioSwatch/RadioSwatchGroup.swift @@ -9,29 +9,31 @@ import Foundation import UIKit import Combine -public class RadioSwatchGroup: RadioSwatchGroupBase { +public class RadioSwatchGroup: RadioSwatchGroupBase { public override func didSelect(selector: RadioSwatch) { - if let index = model.selectors.firstIndex(where: {$0.selected == true }), + if let index = selectorViews.firstIndex(where: {$0.isSelected == true }), let cell = collectionView.cellForItem(at: IndexPath(item: index, section: 0)) as? CollectionViewCell { cell.modelHandler.toggle() } selector.toggle() - label.text = selector.model.text + label.text = selector.text valueChanged() } } -public class RadioSwatchGroupBase>: Control, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UICollectionViewDelegate { +public class RadioSwatchGroupBase: SelectorGroupSelectedHandlerBase, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UICollectionViewDelegate { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - public var selectedModel: ModelHandlerType.ModelType? { - return model.selectedModel + public override var selectorViews: [ModelHandlerType] { + didSet { + collectionView.reloadData() + } } - + //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- @@ -109,16 +111,14 @@ public class RadioSwatchGroupBase 0 else { + guard selectorViews.count > 0 else { collectionViewHeight?.constant = 0 return } // Calculate the height let swatchesInRow = floor(CGFloat(collectionView.bounds.width/(cellSize + itemSpacing))) - let numberOfRows = ceil(CGFloat(swatches.count)/swatchesInRow) + let numberOfRows = ceil(CGFloat(selectorViews.count)/swatchesInRow) let height = (numberOfRows * cellSize) + (itemSpacing * (numberOfRows-1)) collectionViewHeight?.constant = CGFloat(height) @@ -130,31 +130,23 @@ public class RadioSwatchGroupBase Bool { - return !model.selectors[indexPath.row].disabled + return !selectorViews[indexPath.row].disabled } open func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { @@ -182,34 +174,16 @@ public class RadioSwatchGroupBase Int { - return model.selectors.count + return selectorViews.count } - var cellsubs: [Int: AnyCancellable] = [:] public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath) as? CollectionViewCell else { return UICollectionViewCell() } - let model = model.selectors[indexPath.row] + let model = selectorViews[indexPath.row] + cell.modelHandler = selectorViews[indexPath.row] cell.modelHandler.isUserInteractionEnabled = false - - //cancel if sub exists - if let sub = cellsubs[indexPath.row] { - sub.cancel() - cellsubs[indexPath.row] = nil - } - - let sub = cell.modelHandler - .handlerPublisher() - .sink { [weak self] changed in - if cell.modelHandler.shouldUpdateView(viewModel: model) { - print("Model Change: \(changed)") - self?.replace(viewModel: changed) - } - } - cellsubs[indexPath.row] = sub - - cell.set(with: model) return cell } @@ -217,18 +191,4 @@ public class RadioSwatchGroupBase else { - return nil - } - return cell.modelHandler - } } diff --git a/VDS/Components/Toggle/Toggle.swift b/VDS/Components/Toggle/Toggle.swift index 9157e2e5..aed60e41 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. */ -public class Toggle: ToggleBase{ +public class Toggle: ToggleBase{ public override func initialSetup() { super.initialSetup() publisher(for: .touchUpInside) @@ -28,7 +28,25 @@ public class Toggle: ToggleBase{ } } -open class ToggleBase: Control { +open class ToggleBase: Control, Accessable, DataTrackable, BinaryColorable { + + //-------------------------------------------------- + // 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: - Private Properties @@ -86,62 +104,60 @@ open class ToggleBase: Control { $0.forFalse.disabled.darkColor = VDSColor.paletteGray44 } + private var typograpicalStyle: TypographicalStyle { + if textSize == .small { + if textWeight == .bold { + return .BoldBodySmall + } else { + return .BodySmall + } + } else { + if textWeight == .bold { + return .BoldBodyLarge + } else { + return .BodyLarge + } + } + } + //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - @Proxy(\.model.on) - open var isOn: Bool + open var isOn: Bool = false { didSet { subject.send() }} - @Proxy(\.model.showText) - public var showText: Bool + public var showText: Bool = false { didSet { subject.send() }} - @Proxy(\.model.onText) - public var onText: String + public var onText: String = "On" { didSet { subject.send() }} - @Proxy(\.model.offText) - public var offText: String + public var offText: String = "Off" { didSet { subject.send() }} - @Proxy(\.model.textSize) - public var textSize: ToggleTextSize + public var textSize: ToggleTextSize = .small { didSet { subject.send() }} - @Proxy(\.model.textWeight) - public var textWeight: ToggleTextWeight + public var textWeight: ToggleTextWeight = .regular { didSet { subject.send() }} - @Proxy(\.model.textPosition) - public var textPosition: ToggleTextPosition + public var textPosition: ToggleTextPosition = .left { didSet { subject.send() }} - @Proxy(\.model.inputId) - open var inputId: String? + open var inputId: String? { didSet { subject.send() }} - @Proxy(\.model.value) - open var value: AnyHashable? + open var value: AnyHashable? { didSet { subject.send() }} - @Proxy(\.model.dataAnalyticsTrack) - open var dataAnalyticsTrack: String? + open var dataAnalyticsTrack: String? { didSet { subject.send() }} - @Proxy(\.model.dataClickStream) - open var dataClickStream: String? + open var dataClickStream: String? { didSet { subject.send() }} - @Proxy(\.model.dataTrack) - open var dataTrack: String? + open var dataTrack: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityHintEnabled) - open var accessibilityHintEnabled: String? + open var accessibilityHintEnabled: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityHintDisabled) - open var accessibilityHintDisabled: String? + open var accessibilityHintDisabled: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityValueEnabled) - open var accessibilityValueEnabled: String? + open var accessibilityValueEnabled: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityValueDisabled) - open var accessibilityValueDisabled: String? + open var accessibilityValueDisabled: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityLabelEnabled) - open var accessibilityLabelEnabled: String? + open var accessibilityLabelEnabled: String? { didSet { subject.send() }} - @Proxy(\.model.accessibilityLabelDisabled) - open var accessibilityLabelDisabled: String? + open var accessibilityLabelDisabled: String? { didSet { subject.send() }} //-------------------------------------------------- // MARK: - Constraints @@ -157,12 +173,12 @@ open class ToggleBase: Control { //-------------------------------------------------- // MARK: - Toggle //-------------------------------------------------- - private func updateToggle(_ viewModel: ModelType) { + private func updateToggle() { //private func func constrainKnob(){ self.knobLeadingConstraint?.isActive = false self.knobTrailingConstraint?.isActive = false - if viewModel.on { + if isOn { self.knobTrailingConstraint = self.toggleView.trailingAnchor.constraint(equalTo: self.knobView.trailingAnchor, constant: 2) self.knobLeadingConstraint = self.knobView.leadingAnchor.constraint(greaterThanOrEqualTo: self.toggleView.leadingAnchor) } else { @@ -175,10 +191,10 @@ open class ToggleBase: Control { self.layoutIfNeeded() } - let toggleColor = toggleColorConfiguration.getColor(viewModel) - let knobColor = knobColorConfiguration.getColor(viewModel) + let toggleColor = toggleColorConfiguration.getColor(self) + let knobColor = knobColorConfiguration.getColor(self) - if viewModel.disabled { + if disabled { toggleView.backgroundColor = toggleColor knobView.backgroundColor = knobColor constrainKnob() @@ -197,16 +213,21 @@ open class ToggleBase: Control { //-------------------------------------------------- // MARK: - Labels //-------------------------------------------------- - private func updateLabel(_ viewModel: ModelType) { - let showText = viewModel.showText - stackView.spacing = showText ? 12 : 0 - label.set(with: viewModel.labelModel) + private func updateLabel() { + stackView.spacing = showText ? 12 : 0 + if stackView.subviews.contains(label) { label.removeFromSuperview() } if showText { + label.textPosition = textPosition == .left ? .left : .right + label.typograpicalStyle = typograpicalStyle + label.text = isOn ? onText : offText + label.surface = surface + label.disabled = disabled + if textPosition == .left { stackView.insertArrangedSubview(label, at: 0) } else { @@ -246,7 +267,7 @@ open class ToggleBase: Control { toggleView.layer.cornerRadius = toggleSize.height / 2.0 knobView.layer.cornerRadius = knobSize.height / 2.0 - toggleView.backgroundColor = toggleColorConfiguration.getColor(model) + toggleView.backgroundColor = toggleColorConfiguration.getColor(self) toggleContainerView.addSubview(toggleView) toggleView.addSubview(knobView) @@ -261,7 +282,7 @@ open class ToggleBase: Control { toggleView.bottomAnchor.constraint(greaterThanOrEqualTo: knobView.bottomAnchor).isActive = true - updateLabel(model) + updateLabel() stackView.addArrangedSubview(toggleContainerView) stackView.topAnchor.constraint(equalTo: topAnchor).isActive = true stackView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true @@ -275,8 +296,8 @@ open class ToggleBase: Control { public override func reset() { super.reset() - toggleView.backgroundColor = toggleColorConfiguration.getColor(model) - knobView.backgroundColor = knobColorConfiguration.getColor(model) + toggleView.backgroundColor = toggleColorConfiguration.getColor(self) + knobView.backgroundColor = knobColorConfiguration.getColor(self) setAccessibilityLabel() } @@ -289,13 +310,13 @@ open class ToggleBase: Control { //-------------------------------------------------- // MARK: - State //-------------------------------------------------- - open override func updateView(viewModel: ModelType) { - updateLabel(viewModel) - updateToggle(viewModel) + open override func updateView() { + updateLabel() + updateToggle() setAccessibilityHint() - setAccessibilityValue(viewModel.on) - setAccessibilityLabel(viewModel.on) - backgroundColor = viewModel.surface.color + setAccessibilityValue(isOn) + setAccessibilityLabel(isOn) + backgroundColor = surface.color setNeedsLayout() layoutIfNeeded() } diff --git a/VDS/Protocols/Accessable.swift b/VDS/Protocols/Accessable.swift index fd9ad370..0b2981fe 100644 --- a/VDS/Protocols/Accessable.swift +++ b/VDS/Protocols/Accessable.swift @@ -18,37 +18,31 @@ public protocol Accessable { } //Configurations to set within the UIControl -extension ModelHandlerable where Self: UIView { - private var accessableModel: Accessable? { - guard let model = self.model as? Accessable else { - return nil - } - return model - } - +extension ModelHandlerable where Self: UIView, Self: Accessable { + public func setAccessibilityHint(_ override: Bool? = nil) { - let check = override ?? !model.disabled - if let value = accessableModel?.accessibilityHintEnabled, check { + let check = override ?? !disabled + if let value = accessibilityHintEnabled, check { accessibilityHint = value - } else if let value = accessableModel?.accessibilityHintDisabled, !check { + } else if let value = accessibilityHintDisabled, !check { accessibilityHint = value } } public func setAccessibilityValue(_ override: Bool? = nil) { - let check = override ?? !model.disabled - if let value = accessableModel?.accessibilityValueEnabled, check { + let check = override ?? !disabled + if let value = accessibilityValueEnabled, check { accessibilityValue = value - } else if let value = accessableModel?.accessibilityValueDisabled, !check { + } else if let value = accessibilityValueDisabled, !check { accessibilityValue = value } } public func setAccessibilityLabel(_ override: Bool? = nil) { - let check = override ?? !model.disabled - if let value = accessableModel?.accessibilityLabelEnabled, check { + let check = override ?? !disabled + if let value = accessibilityLabelEnabled, check { accessibilityLabel = value - } else if let value = accessableModel?.accessibilityLabelDisabled, !check { + } else if let value = accessibilityLabelDisabled, !check { accessibilityLabel = value } } diff --git a/VDS/Protocols/ModelHandlerable.swift b/VDS/Protocols/ModelHandlerable.swift index 0b3c684e..feab652e 100644 --- a/VDS/Protocols/ModelHandlerable.swift +++ b/VDS/Protocols/ModelHandlerable.swift @@ -9,44 +9,26 @@ import Foundation import Combine import UIKit -public protocol ModelHandlerable: AnyObject, Initable { - associatedtype ModelType: Modelable - var model: ModelType { get set } - var modelPublisher: Published.Publisher { get } +public protocol ModelHandlerable: AnyObject, Initable, Disabling, Surfaceable { + var subject: PassthroughSubject { get set } var subscribers: Set { get set } - init(with model: ModelType) - func set(with model: ModelType) - func shouldUpdateView(viewModel: ModelType) -> Bool - func updateView(viewModel: ModelType) + func updateView() } extension ModelHandlerable { - public init() { - self.init(with: ModelType()) - } - - public func set(with model: ModelType) { - if shouldUpdateView(viewModel: model){ - updateView(viewModel: model) - self.model = model - } - } - - public func shouldUpdateView(viewModel: ModelType) -> Bool { - model != viewModel - } public func setupUpdateView() { handlerPublisher() .subscribe(on: RunLoop.main) - .sink { [weak self] viewModel in - self?.updateView(viewModel: viewModel) + .sink { [weak self] _ in + self?.updateView() } .store(in: &subscribers) } - public func handlerPublisher() -> AnyPublisher { - modelPublisher + public func handlerPublisher() -> AnyPublisher { + subject + .eraseToAnyPublisher() .debounce(for: .seconds(Constants.ModelStateDebounce), scheduler: RunLoop.main) .eraseToAnyPublisher() }