refactored to remove models

Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
Matt Bruce 2022-10-20 10:55:36 -05:00
parent dead6947f1
commit 4bf9ce83b5
21 changed files with 668 additions and 777 deletions

View File

@ -9,13 +9,12 @@ import Foundation
import UIKit import UIKit
import Combine import Combine
open class CollectionView<ModelType: Modelable>: UICollectionView, ModelHandlerable, ViewProtocol, Resettable { open class CollectionView: UICollectionView, ModelHandlerable, ViewProtocol, Resettable {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Combine Properties // MARK: - Combine Properties
//-------------------------------------------------- //--------------------------------------------------
@Published public var model: ModelType = ModelType() public var subject = PassthroughSubject<Void, Never>()
public var modelPublisher: Published<ModelType>.Publisher { $model }
public var subscribers = Set<AnyCancellable>() public var subscribers = Set<AnyCancellable>()
//-------------------------------------------------- //--------------------------------------------------
@ -23,24 +22,18 @@ open class CollectionView<ModelType: Modelable>: UICollectionView, ModelHandlera
//-------------------------------------------------- //--------------------------------------------------
private var initialSetupPerformed = false private var initialSetupPerformed = false
@Proxy(\.model.surface) open var surface: Surface = .light { didSet { subject.send() }}
open var surface: Surface
@Proxy(\.model.disabled) open var disabled: Bool = false { didSet { isEnabled = !disabled } }
open var disabled: Bool {
didSet {
self.isEnabled = !disabled
}
}
open var isEnabled: Bool { open var isEnabled: Bool {
get { !model.disabled } get { !disabled }
set { set {
//create local vars for clear coding if disabled != !newValue {
let disabled = !newValue disabled = !newValue
if model.disabled != disabled {
model.disabled = disabled
} }
isUserInteractionEnabled = isEnabled
subject.send()
} }
} }
@ -52,22 +45,14 @@ open class CollectionView<ModelType: Modelable>: UICollectionView, ModelHandlera
initialSetup() initialSetup()
} }
public required init(with model: ModelType) { public required init(collectionViewLayout layout: UICollectionViewLayout) {
super.init(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
initialSetup()
set(with: model)
}
public required init(with model: ModelType, collectionViewLayout layout: UICollectionViewLayout) {
super.init(frame: .zero, collectionViewLayout: layout) super.init(frame: .zero, collectionViewLayout: layout)
initialSetup() initialSetup()
set(with: model)
} }
public override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) { public override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
super.init(frame: frame, collectionViewLayout: layout) super.init(frame: frame, collectionViewLayout: layout)
initialSetup() initialSetup()
set(with: model)
} }
public required init?(coder: NSCoder) { public required init?(coder: NSCoder) {
@ -90,15 +75,14 @@ open class CollectionView<ModelType: Modelable>: UICollectionView, ModelHandlera
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Overrides // MARK: - Overrides
//-------------------------------------------------- //--------------------------------------------------
open func updateView(viewModel: ModelType) { open func updateView() {
fatalError("Implement updateView") fatalError("Implement updateView")
} }
open func reset() { open func reset() {
backgroundColor = .clear backgroundColor = .clear
if let model = model as? Resettable { surface = .light
model.reset() disabled = false
}
} }
// MARK: - ViewProtocol // MARK: - ViewProtocol

View File

@ -10,7 +10,6 @@ import UIKit
import Combine import Combine
open class CollectionViewCell<ModelHandlerType: ModelHandlerable & UIView>: UICollectionViewCell, ViewProtocol { open class CollectionViewCell<ModelHandlerType: ModelHandlerable & UIView>: UICollectionViewCell, ViewProtocol {
public typealias ModelType = ModelHandlerType.ModelType
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
@ -18,10 +17,10 @@ open class CollectionViewCell<ModelHandlerType: ModelHandlerable & UIView>: UICo
public var modelHandler: ModelHandlerType = ModelHandlerType() public var modelHandler: ModelHandlerType = ModelHandlerType()
@Proxy(\.modelHandler.model.surface) @Proxy(\.modelHandler.surface)
open var surface: Surface open var surface: Surface
@Proxy(\.modelHandler.model.disabled) @Proxy(\.modelHandler.disabled)
open var disabled: Bool open var disabled: Bool
//-------------------------------------------------- //--------------------------------------------------
@ -32,12 +31,6 @@ open class CollectionViewCell<ModelHandlerType: ModelHandlerable & UIView>: UICo
initialSetup() initialSetup()
} }
public required init(with model: ModelType) {
super.init(frame: .zero)
initialSetup()
set(with: model)
}
public override init(frame: CGRect) { public override init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
initialSetup() initialSetup()
@ -59,21 +52,6 @@ open class CollectionViewCell<ModelHandlerType: ModelHandlerable & UIView>: UICo
} }
} }
//--------------------------------------------------
// 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 // MARK: - ViewProtocol
/// Will be called only once. /// Will be called only once.
open func setup() { open func setup() {

View File

@ -87,6 +87,10 @@ public protocol BinaryColorable{
var userTrueColor: Bool { get } var userTrueColor: Bool { get }
} }
extension BinaryColorable where Self: Control {
public var userTrueColor: Bool { return isSelected }
}
extension BinaryColorable where Self: Selectable { extension BinaryColorable where Self: Selectable {
public var userTrueColor: Bool { return selected } public var userTrueColor: Bool { return selected }
} }

View File

@ -10,13 +10,12 @@ import UIKit
import Combine import Combine
open class Control<ModelType: Modelable>: UIControl, ModelHandlerable, ViewProtocol, Resettable { open class Control: UIControl, ModelHandlerable, ViewProtocol, Resettable {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Combine Properties // MARK: - Combine Properties
//-------------------------------------------------- //--------------------------------------------------
@Published public var model: ModelType = ModelType() public var subject = PassthroughSubject<Void, Never>()
public var modelPublisher: Published<ModelType>.Publisher { $model }
public var subscribers = Set<AnyCancellable>() public var subscribers = Set<AnyCancellable>()
//-------------------------------------------------- //--------------------------------------------------
@ -24,25 +23,18 @@ open class Control<ModelType: Modelable>: UIControl, ModelHandlerable, ViewProto
//-------------------------------------------------- //--------------------------------------------------
private var initialSetupPerformed = false private var initialSetupPerformed = false
@Proxy(\.model.surface) open var surface: Surface = .light { didSet { subject.send() }}
open var surface: Surface
@Proxy(\.model.disabled) open var disabled: Bool = false { didSet { isEnabled = !disabled } }
open var disabled: Bool {
didSet {
self.isEnabled = !disabled
}
}
open override var isEnabled: Bool { open override var isEnabled: Bool {
get { !model.disabled } get { !disabled }
set { set {
//create local vars for clear coding if disabled != !newValue {
let disabled = !newValue disabled = !newValue
if model.disabled != disabled {
model.disabled = disabled
} }
isUserInteractionEnabled = isEnabled isUserInteractionEnabled = isEnabled
subject.send()
} }
} }
@ -54,12 +46,6 @@ open class Control<ModelType: Modelable>: UIControl, ModelHandlerable, ViewProto
initialSetup() initialSetup()
} }
public required init(with model: ModelType) {
super.init(frame: .zero)
initialSetup()
set(with: model)
}
public override init(frame: CGRect) { public override init(frame: CGRect) {
super.init(frame: .zero) super.init(frame: .zero)
initialSetup() initialSetup()
@ -92,15 +78,14 @@ open class Control<ModelType: Modelable>: UIControl, ModelHandlerable, ViewProto
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Overrides // MARK: - Overrides
//-------------------------------------------------- //--------------------------------------------------
open func updateView(viewModel: ModelType) { open func updateView() {
fatalError("Implement updateView") fatalError("Implement updateView")
} }
open func reset() { open func reset() {
backgroundColor = .clear backgroundColor = .clear
if let model = model as? Resettable { surface = .light
model.reset() disabled = false
}
} }
// MARK: - ViewProtocol // MARK: - ViewProtocol

View File

@ -8,7 +8,7 @@
import Foundation import Foundation
import UIKit import UIKit
public class SelectorGroupHandlerBase<GroupModelType: SelectorGroupModelable, ModelHandlerType: Control<GroupModelType.SelectorModelType>>: Control<GroupModelType> { public class SelectorGroupHandlerBase<ModelHandlerType: Control>: Control {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Public Properties // MARK: - Public Properties
@ -34,41 +34,9 @@ public class SelectorGroupHandlerBase<GroupModelType: SelectorGroupModelable, Mo
} }
} }
public func findSelectorView(id: UUID?) -> ModelHandlerType? { public func createModelHandler() -> 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 {
//create view //create view
let newSelectorView = ModelHandlerType(with: selector) let newSelectorView = ModelHandlerType()
//add model update to the subscribers
newSelectorView
.modelPublisher
.sink { [weak self] model in
self?.replace(viewModel: model)
}
.store(in: &subscribers)
//add the selectedPublisher for the change //add the selectedPublisher for the change
newSelectorView newSelectorView
@ -93,9 +61,8 @@ public class SelectorGroupHandlerBase<GroupModelType: SelectorGroupModelable, Mo
} }
} }
public class SelectorGroupSelectedHandlerBase<GroupModelType: SelectorGroupSelectedModelable, ModelHandlerType: Control<GroupModelType.SelectorModelType>>: SelectorGroupHandlerBase<GroupModelType, ModelHandlerType> where GroupModelType.SelectorModelType == ModelHandlerType.ModelType { public class SelectorGroupSelectedHandlerBase<ModelHandlerType: Control>: SelectorGroupHandlerBase<ModelHandlerType>{
public var selectedHandler: ModelHandlerType? {
public var selectedModel: ModelHandlerType.ModelType? { return selectorViews.filter { $0.isSelected == true }.first
return model.selectedModel
} }
} }

View File

@ -10,13 +10,12 @@ import UIKit
import Combine import Combine
open class View<ModelType: Modelable>: UIView, ModelHandlerable, ViewProtocol, Resettable { open class View: UIView, ModelHandlerable, ViewProtocol, Resettable {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Combine Properties // MARK: - Combine Properties
//-------------------------------------------------- //--------------------------------------------------
@Published public var model: ModelType = ModelType() public var subject = PassthroughSubject<Void, Never>()
public var modelPublisher: Published<ModelType>.Publisher { $model }
public var subscribers = Set<AnyCancellable>() public var subscribers = Set<AnyCancellable>()
//-------------------------------------------------- //--------------------------------------------------
@ -24,25 +23,18 @@ open class View<ModelType: Modelable>: UIView, ModelHandlerable, ViewProtocol, R
//-------------------------------------------------- //--------------------------------------------------
private var initialSetupPerformed = false private var initialSetupPerformed = false
@Proxy(\.model.surface) open var surface: Surface = .light { didSet { subject.send() }}
open var surface: Surface
@Proxy(\.model.disabled) open var disabled: Bool = false { didSet { isEnabled = !disabled } }
open var disabled: Bool {
didSet {
self.isEnabled = !disabled
}
}
open var isEnabled: Bool { open var isEnabled: Bool {
get { !model.disabled } get { !disabled }
set { set {
//create local vars for clear coding if disabled != !newValue {
let disabled = !newValue disabled = !newValue
if model.disabled != disabled {
model.disabled = disabled
} }
isUserInteractionEnabled = isEnabled isUserInteractionEnabled = isEnabled
subject.send()
} }
} }
@ -54,12 +46,6 @@ open class View<ModelType: Modelable>: UIView, ModelHandlerable, ViewProtocol, R
initialSetup() initialSetup()
} }
public required init(with model: ModelType) {
super.init(frame: .zero)
initialSetup()
set(with: model)
}
public override init(frame: CGRect) { public override init(frame: CGRect) {
super.init(frame: .zero) super.init(frame: .zero)
initialSetup() initialSetup()
@ -85,15 +71,14 @@ open class View<ModelType: Modelable>: UIView, ModelHandlerable, ViewProtocol, R
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Overrides // MARK: - Overrides
//-------------------------------------------------- //--------------------------------------------------
open func updateView(viewModel: ModelType) { open func updateView() {
fatalError("Implement updateView") fatalError("Implement updateView")
} }
open func reset() { open func reset() {
backgroundColor = .clear backgroundColor = .clear
if let model = model as? Resettable { surface = .light
model.reset() disabled = false
}
} }
// MARK: - ViewProtocol // MARK: - ViewProtocol

View File

@ -11,44 +11,34 @@ import VDSColorTokens
import VDSFormControlsTokens import VDSFormControlsTokens
import Combine import Combine
public class Badge: BadgeBase<DefaultBadgeModel>{} public class Badge: BadgeBase{}
open class BadgeBase<ModelType: BadgeModel>: View<ModelType> { open class BadgeBase: View, Accessable {
private var label = Label() private var label = Label()
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Public Properties // MARK: - Public Properties
//-------------------------------------------------- //--------------------------------------------------
@Proxy(\.model.fillColor) open var fillColor: BadgeFillColor = .red { didSet { subject.send() }}
open var fillColor: BadgeFillColor
@Proxy(\.model.text) open var text: String = "" { didSet { subject.send() }}
open var text: String
@Proxy(\.model.maxWidth) open var maxWidth: CGFloat? { didSet { subject.send() }}
open var maxWidth: CGFloat?
@Proxy(\.model.numberOfLines) open var numberOfLines: Int = 1 { didSet { subject.send() }}
open var numberOfLines: Int
@Proxy(\.model.accessibilityHintEnabled) open var accessibilityHintEnabled: String? { didSet { subject.send() }}
open var accessibilityHintEnabled: String?
@Proxy(\.model.accessibilityHintDisabled) open var accessibilityHintDisabled: String? { didSet { subject.send() }}
open var accessibilityHintDisabled: String?
@Proxy(\.model.accessibilityValueEnabled) open var accessibilityValueEnabled: String? { didSet { subject.send() }}
open var accessibilityValueEnabled: String?
@Proxy(\.model.accessibilityValueDisabled) open var accessibilityValueDisabled: String? { didSet { subject.send() }}
open var accessibilityValueDisabled: String?
@Proxy(\.model.accessibilityLabelEnabled) open var accessibilityLabelEnabled: String? { didSet { subject.send() }}
open var accessibilityLabelEnabled: String?
@Proxy(\.model.accessibilityLabelDisabled) open var accessibilityLabelDisabled: String? { didSet { subject.send() }}
open var accessibilityLabelDisabled: String?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Constraints // MARK: - Constraints
@ -90,9 +80,9 @@ open class BadgeBase<ModelType: BadgeModel>: View<ModelType> {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Configuration // MARK: - Configuration
//-------------------------------------------------- //--------------------------------------------------
public func backgroundColor(for fillColor: BadgeFillColor) -> UIColor { public func backgroundColor() -> UIColor {
var config: SurfaceColorConfiguration var config: SurfaceColorConfiguration
switch model.fillColor { switch fillColor {
case .red: case .red:
config = SurfaceColorConfiguration().with { config = SurfaceColorConfiguration().with {
$0.lightColor = VDSColor.backgroundBrandhighlight $0.lightColor = VDSColor.backgroundBrandhighlight
@ -130,10 +120,10 @@ open class BadgeBase<ModelType: BadgeModel>: View<ModelType> {
} }
} }
return config.getColor(model) return config.getColor(self)
} }
public func textColorConfiguration(for fillColor: BadgeFillColor) -> AnyColorable { public func textColorConfiguration() -> AnyColorable {
switch fillColor { switch fillColor {
@ -165,19 +155,23 @@ open class BadgeBase<ModelType: BadgeModel>: View<ModelType> {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - State // MARK: - State
//-------------------------------------------------- //--------------------------------------------------
open override func updateView(viewModel: ModelType) { open override func updateView() {
backgroundColor = backgroundColor(for: viewModel.fillColor) 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) if let maxWidth = maxWidth, let minWidth = minWidthConstraint?.constant, maxWidth > minWidth {
label.numberOfLines = viewModel.numberOfLines
if let maxWidth = viewModel.maxWidth, let minWidth = minWidthConstraint?.constant, maxWidth > minWidth {
maxWidthConstraint?.constant = maxWidth maxWidthConstraint?.constant = maxWidth
maxWidthConstraint?.isActive = true maxWidthConstraint?.isActive = true
} else { } else {
maxWidthConstraint?.isActive = false maxWidthConstraint?.isActive = false
} }
label.set(with: viewModel.label)
setAccessibilityLabel() setAccessibilityLabel()
} }

View File

@ -11,15 +11,12 @@ import VDSColorTokens
import VDSFormControlsTokens import VDSFormControlsTokens
import Combine import Combine
public class Button:ButtonBase<DefaultButtonModel>{} open class Button: UIButton, ModelHandlerable, ViewProtocol, Resettable, Useable {
open class ButtonBase<ModelType: ButtonModel>: UIButton, ModelHandlerable, ViewProtocol, Resettable {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Combine Properties // MARK: - Combine Properties
//-------------------------------------------------- //--------------------------------------------------
@Published public var model: ModelType = ModelType() public var subject = PassthroughSubject<Void, Never>()
public var modelPublisher: Published<ModelType>.Publisher { $model }
public var subscribers = Set<AnyCancellable>() public var subscribers = Set<AnyCancellable>()
//-------------------------------------------------- //--------------------------------------------------
@ -32,40 +29,30 @@ open class ButtonBase<ModelType: ButtonModel>: UIButton, ModelHandlerable, ViewP
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
@Proxy(\.model.surface) open var text: String? { didSet { subject.send() }}
open var surface: Surface
@Proxy(\.model.disabled) open var use: Use = .primary { didSet { subject.send() }}
open var disabled: Bool {
didSet {
isEnabled = !disabled
}
}
@Proxy(\.model.text) open var size: ButtonSize = .large { didSet { subject.send() }}
open var text: String?
@Proxy(\.model.use) open var width: CGFloat? { didSet { subject.send() }}
open var use: Use
@Proxy(\.model.size) open var surface: Surface = .light { didSet { subject.send() }}
open var size: ButtonSize
@Proxy(\.model.width) open var disabled: Bool = false { didSet { isEnabled = !disabled } }
open var width: CGFloat?
open override var isEnabled: Bool { open override var isEnabled: Bool {
get { !model.disabled } get { !disabled }
set { set {
//create local vars for clear coding if disabled != !newValue {
let disabled = !newValue disabled = !newValue
if model.disabled != disabled {
model.disabled = disabled
} }
isUserInteractionEnabled = isEnabled isUserInteractionEnabled = isEnabled
subject.send()
} }
} }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Configuration Properties // MARK: - Configuration Properties
//-------------------------------------------------- //--------------------------------------------------
@ -113,16 +100,9 @@ open class ButtonBase<ModelType: ButtonModel>: UIButton, ModelHandlerable, ViewP
initialSetup() initialSetup()
} }
public required init(with model: ModelType) {
super.init(frame: .zero)
initialSetup()
set(with: model)
}
public override init(frame: CGRect) { public override init(frame: CGRect) {
super.init(frame: .zero) super.init(frame: .zero)
initialSetup() initialSetup()
set(with: model)
} }
public required init?(coder: NSCoder) { public required init?(coder: NSCoder) {
@ -149,15 +129,19 @@ open class ButtonBase<ModelType: ButtonModel>: UIButton, ModelHandlerable, ViewP
//only 1 of the 2 widths can be on at the same time //only 1 of the 2 widths can be on at the same time
widthConstraint = widthAnchor.constraint(equalToConstant: 0) widthConstraint = widthAnchor.constraint(equalToConstant: 0)
minWidthConstraint = widthAnchor.constraint(greaterThanOrEqualToConstant: model.size.minimumWidth) minWidthConstraint = widthAnchor.constraint(greaterThanOrEqualToConstant: size.minimumWidth)
//height //height
heightConstraint = heightAnchor.constraint(equalToConstant: model.size.height) heightConstraint = heightAnchor.constraint(equalToConstant: size.height)
heightConstraint?.isActive = true heightConstraint?.isActive = true
} }
open func reset() { open func reset() {
model = ModelType() surface = .light
disabled = false
use = .primary
width = nil
size = .large
accessibilityCustomActions = [] accessibilityCustomActions = []
accessibilityTraits = .staticText accessibilityTraits = .staticText
} }
@ -165,19 +149,19 @@ open class ButtonBase<ModelType: ButtonModel>: UIButton, ModelHandlerable, ViewP
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Overrides // MARK: - Overrides
//-------------------------------------------------- //--------------------------------------------------
open func updateView(viewModel: ModelType) { open func updateView() {
let bgColor = buttonBackgroundColorConfiguration.getColor(viewModel) let bgColor = buttonBackgroundColorConfiguration.getColor(self)
let borderColor = buttonBorderColorConfiguration.getColor(viewModel) let borderColor = buttonBorderColorConfiguration.getColor(self)
let titleColor = buttonTitleColorConfiguration.getColor(viewModel) let titleColor = buttonTitleColorConfiguration.getColor(self)
let borderWidth = viewModel.use == .secondary ? 1.0 : 0.0 let borderWidth = use == .secondary ? 1.0 : 0.0
let buttonHeight = viewModel.size.height let buttonHeight = size.height
let cornerRadius = buttonHeight / 2 let cornerRadius = buttonHeight / 2
let minWidth = viewModel.size.minimumWidth let minWidth = size.minimumWidth
let font = viewModel.size == .large ? TypographicalStyle.BoldBodyLarge.font : TypographicalStyle.BoldBodySmall.font let font = size == .large ? TypographicalStyle.BoldBodyLarge.font : TypographicalStyle.BoldBodySmall.font
let edgeInsets = viewModel.size.edgeInsets let edgeInsets = size.edgeInsets
if let text = viewModel.text { if let text = text {
setTitle(text, for: .normal) setTitle(text, for: .normal)
} else { } else {
setTitle("No ViewModel Text", for: .normal) setTitle("No ViewModel Text", for: .normal)
@ -193,7 +177,7 @@ open class ButtonBase<ModelType: ButtonModel>: UIButton, ModelHandlerable, ViewP
minWidthConstraint?.constant = minWidth minWidthConstraint?.constant = minWidth
heightConstraint?.constant = buttonHeight heightConstraint?.constant = buttonHeight
if let width = viewModel.width, width > minWidth { if let width, width > minWidth {
widthConstraint?.constant = width widthConstraint?.constant = width
widthConstraint?.isActive = true widthConstraint?.isActive = true
minWidthConstraint?.isActive = false minWidthConstraint?.isActive = false

View File

@ -24,7 +24,6 @@ public struct DefaultButtonModel: ButtonModel {
public var id = UUID() public var id = UUID()
public var text: String? public var text: String?
public var typograpicalStyle: TypographicalStyle = .BoldBodyLarge
public var surface: Surface = .light public var surface: Surface = .light
public var use: Use = .primary public var use: Use = .primary
public var disabled: Bool = false public var disabled: Bool = false

View File

@ -11,9 +11,9 @@ import VDSColorTokens
import VDSFormControlsTokens import VDSFormControlsTokens
import Combine import Combine
public class Checkbox: CheckboxBase<DefaultCheckboxModel>{} public class Checkbox: CheckboxBase{}
public class SoloCheckbox: CheckboxBase<DefaultCheckboxModel>{ public class SoloCheckbox: CheckboxBase{
public override func initialSetup() { public override func initialSetup() {
super.initialSetup() super.initialSetup()
publisher(for: .touchUpInside) publisher(for: .touchUpInside)
@ -23,11 +23,39 @@ public class SoloCheckbox: CheckboxBase<DefaultCheckboxModel>{
} }
} }
open class CheckboxBase<ModelType: CheckboxModel>: Control<ModelType> { 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 // 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 = { private var mainStackView: UIStackView = {
return UIStackView().with { return UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false $0.translatesAutoresizingMaskIntoConstraints = false
@ -67,59 +95,76 @@ open class CheckboxBase<ModelType: CheckboxModel>: Control<ModelType> {
}() }()
//can't bind to @Proxy //can't bind to @Proxy
open override var isSelected: Bool { open override var isSelected: Bool { didSet { subject.send() }}
get { model.selected }
set { open var labelText: String? { didSet { subject.send() }}
if model.selected != newValue {
model.selected = newValue open var labelTextAttributes: [any LabelAttributeModel]? { didSet { subject.send() }}
}
} open var childText: String? { didSet { subject.send() }}
open var childTextAttributes: [any LabelAttributeModel]? { didSet { subject.send() }}
open var showError: Bool = false { didSet { subject.send() }}
open var errorText: String? { didSet { subject.send() }}
open var inputId: String? { didSet { subject.send() }}
open var value: AnyHashable? { didSet { subject.send() }}
open var dataAnalyticsTrack: String? { didSet { subject.send() }}
open var dataClickStream: String? { didSet { subject.send() }}
open var dataTrack: String? { didSet { subject.send() }}
open var accessibilityHintEnabled: String? { didSet { subject.send() }}
open var accessibilityHintDisabled: String? { didSet { subject.send() }}
open var accessibilityValueEnabled: String? { didSet { subject.send() }}
open var accessibilityValueDisabled: String? { didSet { subject.send() }}
open var accessibilityLabelEnabled: String? { didSet { subject.send() }}
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
} }
@Proxy(\.model.labelText) private var childModel: DefaultLabelModel? {
open var labelText: String? 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
}
@Proxy(\.model.childText) private var errorModel: DefaultLabelModel? {
open var childText: String? guard let errorText = errorText, showError else { return nil }
var model = DefaultLabelModel()
@Proxy(\.model.showError) model.textPosition = .left
open var showError: Bool model.typograpicalStyle = .BodyMedium
model.text = errorText
@Proxy(\.model.errorText) model.surface = surface
open var errorText: String? model.disabled = disabled
return model
@Proxy(\.model.inputId) }
open var inputId: String?
@Proxy(\.model.value)
open var value: AnyHashable?
@Proxy(\.model.dataAnalyticsTrack)
open var dataAnalyticsTrack: String?
@Proxy(\.model.dataClickStream)
open var dataClickStream: String?
@Proxy(\.model.dataTrack)
open var dataTrack: String?
@Proxy(\.model.accessibilityHintEnabled)
open var accessibilityHintEnabled: String?
@Proxy(\.model.accessibilityHintDisabled)
open var accessibilityHintDisabled: String?
@Proxy(\.model.accessibilityValueEnabled)
open var accessibilityValueEnabled: String?
@Proxy(\.model.accessibilityValueDisabled)
open var accessibilityValueDisabled: String?
@Proxy(\.model.accessibilityLabelEnabled)
open var accessibilityLabelEnabled: String?
@Proxy(\.model.accessibilityLabelDisabled)
open var accessibilityLabelDisabled: String?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Constraints // MARK: - Constraints
@ -159,7 +204,7 @@ open class CheckboxBase<ModelType: CheckboxModel>: Control<ModelType> {
selectorWidthConstraint = selectorView.widthAnchor.constraint(equalToConstant: selectorSize.width) selectorWidthConstraint = selectorView.widthAnchor.constraint(equalToConstant: selectorSize.width)
selectorWidthConstraint?.isActive = true selectorWidthConstraint?.isActive = true
updateSelector(model) updateSelector()
mainStackView.topAnchor.constraint(equalTo: topAnchor).isActive = true mainStackView.topAnchor.constraint(equalTo: topAnchor).isActive = true
mainStackView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true mainStackView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
@ -168,22 +213,32 @@ open class CheckboxBase<ModelType: CheckboxModel>: Control<ModelType> {
} }
func updateLabels(_ viewModel: ModelType) { func updateLabels() {
//deal with labels //deal with labels
if viewModel.shouldShowLabels { if shouldShowLabels {
//add the stackview to hold the 2 labels //add the stackview to hold the 2 labels
//top label //top label
if let labelModel = viewModel.labelModel { if let labelText {
primaryLabel.set(with: labelModel) primaryLabel.textPosition = .left
primaryLabel.typograpicalStyle = .BoldBodyLarge
primaryLabel.text = labelText
primaryLabel.surface = surface
primaryLabel.disabled = disabled
primaryLabel.attributes = labelTextAttributes
primaryLabel.isHidden = false primaryLabel.isHidden = false
} else { } else {
primaryLabel.isHidden = true primaryLabel.isHidden = true
} }
//bottom label //bottom label
if let childModel = viewModel.childModel { if let childText {
secondaryLabel.set(with: childModel) secondaryLabel.textPosition = .left
secondaryLabel.typograpicalStyle = .BodyLarge
secondaryLabel.text = childText
secondaryLabel.surface = surface
secondaryLabel.disabled = disabled
secondaryLabel.attributes = childTextAttributes
secondaryLabel.isHidden = false secondaryLabel.isHidden = false
} else { } else {
secondaryLabel.isHidden = true secondaryLabel.isHidden = true
@ -199,20 +254,23 @@ open class CheckboxBase<ModelType: CheckboxModel>: Control<ModelType> {
} }
//either add/remove the error from the main stack //either add/remove the error from the main stack
if let errorModel = model.errorModel, model.shouldShowError { if let errorText, shouldShowError {
errorLabel.set(with: errorModel) errorLabel.textPosition = .left
errorLabel.typograpicalStyle = .BodyMedium
errorLabel.text = errorText
errorLabel.surface = surface
errorLabel.disabled = disabled
mainStackView.spacing = 8 mainStackView.spacing = 8
errorLabel.isHidden = false errorLabel.isHidden = false
} else { } else {
mainStackView.spacing = 0 mainStackView.spacing = 0
errorLabel.isHidden = true errorLabel.isHidden = true
} }
} }
public override func reset() { public override func reset() {
super.reset() super.reset()
updateSelector(model) updateSelector()
setAccessibilityLabel() setAccessibilityLabel()
} }
@ -229,12 +287,12 @@ open class CheckboxBase<ModelType: CheckboxModel>: Control<ModelType> {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - State // MARK: - State
//-------------------------------------------------- //--------------------------------------------------
open override func updateView(viewModel: ModelType) { open override func updateView() {
updateLabels(viewModel) updateLabels()
updateSelector(viewModel) updateSelector()
setAccessibilityHint() setAccessibilityHint()
setAccessibilityValue(viewModel.selected) setAccessibilityValue(isSelected)
setAccessibilityLabel(viewModel.selected) setAccessibilityLabel(isSelected)
setNeedsLayout() setNeedsLayout()
layoutIfNeeded() layoutIfNeeded()
} }
@ -294,11 +352,11 @@ open class CheckboxBase<ModelType: CheckboxModel>: Control<ModelType> {
return checkboxSize return checkboxSize
} }
open func updateSelector(_ viewModel: ModelType) { open func updateSelector() {
//get the colors //get the colors
let backgroundColor = checkboxBackgroundColorConfiguration.getColor(viewModel) let backgroundColor = checkboxBackgroundColorConfiguration.getColor(self)
let borderColor = checkboxBorderColorConfiguration.getColor(viewModel) let borderColor = checkboxBorderColorConfiguration.getColor(self)
let checkColor = checkboxCheckColorConfiguration.getColor(viewModel) let checkColor = checkboxCheckColorConfiguration.getColor(self)
if let shapeLayer = shapeLayer, let sublayers = layer.sublayers, sublayers.contains(shapeLayer) { if let shapeLayer = shapeLayer, let sublayers = layer.sublayers, sublayers.contains(shapeLayer) {
shapeLayer.removeFromSuperlayer() shapeLayer.removeFromSuperlayer()
@ -340,7 +398,7 @@ open class CheckboxBase<ModelType: CheckboxModel>: Control<ModelType> {
shapeLayer.lineJoin = .miter shapeLayer.lineJoin = .miter
shapeLayer.lineWidth = 2 shapeLayer.lineWidth = 2
CATransaction.withDisabledAnimations { CATransaction.withDisabledAnimations {
shapeLayer.strokeEnd = model.selected ? 1 : 0 shapeLayer.strokeEnd = isSelected ? 1 : 0
} }
} }
} }

View File

@ -8,7 +8,7 @@
import Foundation import Foundation
import UIKit import UIKit
public class CheckboxGroup: CheckboxGroupBase<DefaultCheckboxGroupModel, Checkbox> { public class CheckboxGroup: CheckboxGroupBase<Checkbox> {
public override func didSelect(_ selectedControl: Checkbox) { public override func didSelect(_ selectedControl: Checkbox) {
selectedControl.toggle() selectedControl.toggle()
if selectedControl.isSelected, showError{ if selectedControl.isSelected, showError{
@ -18,13 +18,25 @@ public class CheckboxGroup: CheckboxGroupBase<DefaultCheckboxGroupModel, Checkbo
} }
} }
public class CheckboxGroupBase<GroupModelType: CheckboxGroupModel, ModelHandlerType: CheckboxBase<GroupModelType.SelectorModelType>>: SelectorGroupHandlerBase<GroupModelType, ModelHandlerType> { public class CheckboxGroupBase<ModelHandlerType: CheckboxBase>: SelectorGroupHandlerBase<ModelHandlerType> {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Public Properties // 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 { public var showError: Bool {
get { model.showError } get { _showError }
set { set {
var newShowError = newValue var newShowError = newValue
let anySelected = selectorViews.filter { $0.isSelected == true }.count > 0 let anySelected = selectorViews.filter { $0.isSelected == true }.count > 0
@ -34,8 +46,7 @@ public class CheckboxGroupBase<GroupModelType: CheckboxGroupModel, ModelHandlerT
selectorViews.forEach { handler in selectorViews.forEach { handler in
handler.showError = newShowError handler.showError = newShowError
} }
model.showError = newShowError _showError = newShowError
} }
} }
@ -68,22 +79,6 @@ public class CheckboxGroupBase<GroupModelType: CheckboxGroupModel, ModelHandlerT
mainStackView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true mainStackView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
} }
open override func updateView(viewModel: ModelType) {
for selectorModel in viewModel.selectors {
//see if view is there for the model
if let foundSelectorView = findSelectorView(id: selectorModel.id) {
foundSelectorView.set(with: selectorModel)
} else {
//create view
let newSelectorView = createModelHandler(selector: selectorModel)
self.selectorViews.append(newSelectorView)
mainStackView.addArrangedSubview(newSelectorView)
}
}
}
public var selectedModelHandlers: [ModelHandlerType]? { public var selectedModelHandlers: [ModelHandlerType]? {
let selected = selectorViews.filter{ $0.isSelected == true } let selected = selectorViews.filter{ $0.isSelected == true }
guard selected.count > 0 else { return nil } guard selected.count > 0 else { return nil }

View File

@ -10,57 +10,43 @@ import UIKit
import VDSColorTokens import VDSColorTokens
import Combine import Combine
public class Label:LabelBase<DefaultLabelModel>{} public class Label: LabelBase {}
open class LabelBase<ModelType: LabelModel>: UILabel, ModelHandlerable, ViewProtocol, Resettable { open class LabelBase: UILabel, ModelHandlerable, ViewProtocol, Resettable {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Combine Properties // MARK: - Combine Properties
//-------------------------------------------------- //--------------------------------------------------
@Published public var model: ModelType = ModelType() public var subject = PassthroughSubject<Void, Never>()
public var modelPublisher: Published<ModelType>.Publisher { $model }
public var subscribers = Set<AnyCancellable>() public var subscribers = Set<AnyCancellable>()
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
@Proxy(\.model.surface) open var surface: Surface = .light { didSet { subject.send() }}
open var surface: Surface
@Proxy(\.model.disabled) open var disabled: Bool = false { didSet { isEnabled = !disabled } }
open var disabled: Bool {
didSet { open var attributes: [any LabelAttributeModel]? { didSet { subject.send() }}
self.isEnabled = !disabled
} open var typograpicalStyle: TypographicalStyle = .defaultStyle { didSet { subject.send() }}
}
open var textPosition: TextPosition = .left { didSet { subject.send() }}
open override var isEnabled: Bool { open override var isEnabled: Bool {
get { !model.disabled } get { !disabled }
set { set {
//create local vars for clear coding if disabled != !newValue {
let disabled = !newValue disabled = !newValue
if model.disabled != disabled {
model.disabled = disabled
} }
isUserInteractionEnabled = isEnabled 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? { override open var text: String? {
didSet { didSet {
if model.text != oldValue { subject.send()
model.text = text
}
} }
} }
@ -82,16 +68,9 @@ open class LabelBase<ModelType: LabelModel>: UILabel, ModelHandlerable, ViewProt
initialSetup() initialSetup()
} }
public required init(with model: ModelType) {
super.init(frame: .zero)
initialSetup()
set(with: model)
}
public override init(frame: CGRect) { public override init(frame: CGRect) {
super.init(frame: .zero) super.init(frame: .zero)
initialSetup() initialSetup()
set(with: model)
} }
public required init?(coder: NSCoder) { public required init?(coder: NSCoder) {
@ -116,11 +95,13 @@ open class LabelBase<ModelType: LabelModel>: UILabel, ModelHandlerable, ViewProt
open func setup() {} open func setup() {}
open func reset() { open func reset() {
surface = .light
disabled = false
attributes = nil
typograpicalStyle = .defaultStyle
textPosition = .left
text = nil text = nil
attributedText = nil attributedText = nil
textColor = .black
font = TypographicalStyle.BodyLarge.font
textAlignment = .left
accessibilityCustomActions = [] accessibilityCustomActions = []
accessibilityTraits = .staticText accessibilityTraits = .staticText
numberOfLines = 0 numberOfLines = 0
@ -129,17 +110,12 @@ open class LabelBase<ModelType: LabelModel>: UILabel, ModelHandlerable, ViewProt
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Overrides // MARK: - Overrides
//-------------------------------------------------- //--------------------------------------------------
open func updateView(viewModel: ModelType) { open func updateView() {
textAlignment = viewModel.textPosition.textAlignment textAlignment = textPosition.textAlignment
textColor = textColorConfiguration.getColor(viewModel) textColor = textColorConfiguration.getColor(self)
font = typograpicalStyle.font
if let vdsFont = viewModel.font { if let text = text, let font = font, let textColor = textColor {
font = vdsFont
} else {
font = TypographicalStyle.defaultStyle.font
}
if let text = viewModel.text, let font = font, let textColor = textColor {
//clear the arrays holding actions //clear the arrays holding actions
accessibilityCustomActions = [] accessibilityCustomActions = []
actions = [] actions = []
@ -149,9 +125,9 @@ open class LabelBase<ModelType: LabelModel>: UILabel, ModelHandlerable, ViewProt
let mutableText = NSMutableAttributedString(string: text, attributes: startingAttributes) let mutableText = NSMutableAttributedString(string: text, attributes: startingAttributes)
//set the local lineHeight/lineSpacing attributes //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 //loop through the models attributes
for attribute in attributes { for attribute in attributes {
@ -170,42 +146,40 @@ open class LabelBase<ModelType: LabelModel>: UILabel, ModelHandlerable, ViewProt
} }
//only enabled if enabled and has actions //only enabled if enabled and has actions
isUserInteractionEnabled = !viewModel.disabled && !actions.isEmpty isUserInteractionEnabled = !disabled && !actions.isEmpty
//set the attributed text //set the attributed text
attributedText = mutableText attributedText = mutableText
} else {
text = viewModel.text
} }
} }
// MARK: - Private Attributes // MARK: - Private Attributes
private func setStyleAttributes(viewModel: ModelType, attributedString: NSMutableAttributedString) { private func setStyleAttributes(attributedString: NSMutableAttributedString) {
//get the range //get the range
let entireRange = NSRange(location: 0, length: attributedString.length) let entireRange = NSRange(location: 0, length: attributedString.length)
//set letterSpacing //set letterSpacing
if viewModel.typograpicalStyle.letterSpacing > 0.0 { if typograpicalStyle.letterSpacing > 0.0 {
attributedString.addAttribute(.kern, value: viewModel.typograpicalStyle.letterSpacing, range: entireRange) attributedString.addAttribute(.kern, value: typograpicalStyle.letterSpacing, range: entireRange)
} }
//set lineHeight //set lineHeight
if viewModel.typograpicalStyle.lineHeight > 0.0 { if typograpicalStyle.lineHeight > 0.0 {
let lineHeight = viewModel.typograpicalStyle.lineHeight let lineHeight = typograpicalStyle.lineHeight
let adjustment = lineHeight > font.lineHeight ? 2.0 : 1.0 let adjustment = lineHeight > font.lineHeight ? 2.0 : 1.0
let baselineOffset = (lineHeight - font.lineHeight) / 2.0 / adjustment let baselineOffset = (lineHeight - font.lineHeight) / 2.0 / adjustment
let paragraph = NSMutableParagraphStyle().with { let paragraph = NSMutableParagraphStyle().with {
$0.maximumLineHeight = lineHeight $0.maximumLineHeight = lineHeight
$0.minimumLineHeight = lineHeight $0.minimumLineHeight = lineHeight
$0.alignment = viewModel.textPosition.textAlignment $0.alignment = textPosition.textAlignment
$0.lineBreakMode = lineBreakMode $0.lineBreakMode = lineBreakMode
} }
attributedString.addAttribute(.baselineOffset, value: baselineOffset, range: entireRange) attributedString.addAttribute(.baselineOffset, value: baselineOffset, range: entireRange)
attributedString.addAttribute( .paragraphStyle, value: paragraph, range: entireRange) attributedString.addAttribute( .paragraphStyle, value: paragraph, range: entireRange)
} else if viewModel.textPosition != .left { } else if textPosition != .left {
let paragraph = NSMutableParagraphStyle().with { let paragraph = NSMutableParagraphStyle().with {
$0.alignment = viewModel.textPosition.textAlignment $0.alignment = textPosition.textAlignment
$0.lineBreakMode = lineBreakMode $0.lineBreakMode = lineBreakMode
} }
attributedString.addAttribute( .paragraphStyle, value: paragraph, range: entireRange) attributedString.addAttribute( .paragraphStyle, value: paragraph, range: entireRange)

View File

@ -11,9 +11,9 @@ import VDSColorTokens
import VDSFormControlsTokens import VDSFormControlsTokens
import Combine import Combine
public class RadioBox: RadioBoxBase<DefaultRadioBoxModel>{} public class RadioBox: RadioBoxBase{}
public class SolorRadioBox: RadioBoxBase<DefaultRadioBoxModel>{ public class SolorRadioBox: RadioBoxBase{
public override func initialSetup() { public override func initialSetup() {
super.initialSetup() super.initialSetup()
@ -24,7 +24,24 @@ public class SolorRadioBox: RadioBoxBase<DefaultRadioBoxModel>{
} }
} }
open class RadioBoxBase<ModelType: RadioBoxModel>: Control<ModelType> { 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 // MARK: - Private Properties
@ -68,60 +85,41 @@ open class RadioBoxBase<ModelType: RadioBoxModel>: Control<ModelType> {
} }
}() }()
//can't bind to @Proxy open var text: String = "Default Text" { didSet { subject.send() }}
open override var isSelected: Bool {
get { model.selected }
set {
if model.selected != newValue {
model.selected = newValue
}
}
}
@Proxy(\.model.text) open var textAttributes: [any LabelAttributeModel]? { didSet { subject.send() }}
open var text: String
@Proxy(\.model.subText) open var subText: String? { didSet { subject.send() }}
open var subText: String?
@Proxy(\.model.subTextRight) open var subTextAttributes: [any LabelAttributeModel]? { didSet { subject.send() }}
open var subTextRight: String?
@Proxy(\.model.strikethrough) open var subTextRight: String? { didSet { subject.send() }}
open var strikethrough: Bool
@Proxy(\.model.inputId) open var subTextRightAttributes: [any LabelAttributeModel]? { didSet { subject.send() }}
open var inputId: String?
@Proxy(\.model.value) open var strikethrough: Bool = false { didSet { subject.send() }}
open var value: AnyHashable?
@Proxy(\.model.dataAnalyticsTrack) open var inputId: String? { didSet { subject.send() }}
open var dataAnalyticsTrack: String?
@Proxy(\.model.dataClickStream) open var value: AnyHashable? { didSet { subject.send() }}
open var dataClickStream: String?
@Proxy(\.model.dataTrack) open var dataAnalyticsTrack: String? { didSet { subject.send() }}
open var dataTrack: String?
@Proxy(\.model.accessibilityHintEnabled) open var dataClickStream: String? { didSet { subject.send() }}
open var accessibilityHintEnabled: String?
@Proxy(\.model.accessibilityHintDisabled) open var dataTrack: String? { didSet { subject.send() }}
open var accessibilityHintDisabled: String?
@Proxy(\.model.accessibilityValueEnabled) open var accessibilityHintEnabled: String? { didSet { subject.send() }}
open var accessibilityValueEnabled: String?
@Proxy(\.model.accessibilityValueDisabled) open var accessibilityHintDisabled: String? { didSet { subject.send() }}
open var accessibilityValueDisabled: String?
@Proxy(\.model.accessibilityLabelEnabled) open var accessibilityValueEnabled: String? { didSet { subject.send() }}
open var accessibilityLabelEnabled: String?
@Proxy(\.model.accessibilityLabelDisabled) open var accessibilityValueDisabled: String? { didSet { subject.send() }}
open var accessibilityLabelDisabled: String?
open var accessibilityLabelEnabled: String? { didSet { subject.send() }}
open var accessibilityLabelDisabled: String? { didSet { subject.send() }}
//functions //functions
//-------------------------------------------------- //--------------------------------------------------
@ -153,7 +151,7 @@ open class RadioBoxBase<ModelType: RadioBoxModel>: Control<ModelType> {
selectorLeftLabelStackView.spacing = 4 selectorLeftLabelStackView.spacing = 4
selectorLeftLabelStackView.isHidden = false selectorLeftLabelStackView.isHidden = false
updateSelector(model) updateSelector()
selectorView.topAnchor.constraint(equalTo: topAnchor).isActive = true selectorView.topAnchor.constraint(equalTo: topAnchor).isActive = true
selectorView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true selectorView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
@ -167,23 +165,38 @@ open class RadioBoxBase<ModelType: RadioBoxModel>: Control<ModelType> {
} }
func updateLabels(_ viewModel: ModelType) { func updateLabels() {
//add the stackview to hold the 2 labels //add the stackview to hold the 2 labels
//text label //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 //subText label
if let subTextModel = viewModel.subTextModel { if let subText {
subTextLabel.set(with: subTextModel) subTextLabel.textPosition = .left
subTextLabel.typograpicalStyle = .BodyLarge
subTextLabel.text = subText
subTextLabel.surface = surface
subTextLabel.disabled = disabled
subTextLabel.attributes = subTextAttributes
subTextLabel.isHidden = false subTextLabel.isHidden = false
} else { } else {
subTextLabel.isHidden = true subTextLabel.isHidden = true
} }
//subTextRight label //subTextRight label
if let subTextRightModel = viewModel.subTextRightModel { if let subTextRight {
subTextRightLabel.set(with: subTextRightModel) subTextRightLabel.textPosition = .right
subTextRightLabel.typograpicalStyle = .BodyLarge
subTextRightLabel.text = subTextRight
subTextRightLabel.surface = surface
subTextRightLabel.disabled = disabled
subTextRightLabel.attributes = subTextRightAttributes
subTextRightLabel.isHidden = false subTextRightLabel.isHidden = false
} else { } else {
subTextRightLabel.isHidden = true subTextRightLabel.isHidden = true
@ -192,7 +205,7 @@ open class RadioBoxBase<ModelType: RadioBoxModel>: Control<ModelType> {
public override func reset() { public override func reset() {
super.reset() super.reset()
updateSelector(model) updateSelector()
setAccessibilityLabel() setAccessibilityLabel()
} }
@ -206,12 +219,12 @@ open class RadioBoxBase<ModelType: RadioBoxModel>: Control<ModelType> {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - State // MARK: - State
//-------------------------------------------------- //--------------------------------------------------
open override func updateView(viewModel: ModelType) { open override func updateView() {
updateLabels(viewModel) updateLabels()
updateSelector(viewModel) updateSelector()
setAccessibilityHint() setAccessibilityHint()
setAccessibilityValue(viewModel.selected) setAccessibilityValue(isSelected)
setAccessibilityLabel(viewModel.selected) setAccessibilityLabel(isSelected)
setNeedsLayout() setNeedsLayout()
layoutIfNeeded() layoutIfNeeded()
} }
@ -256,11 +269,11 @@ open class RadioBoxBase<ModelType: RadioBoxModel>: Control<ModelType> {
private var shapeLayer: CAShapeLayer? private var shapeLayer: CAShapeLayer?
open func updateSelector(_ viewModel: ModelType) { open func updateSelector() {
//get the colors //get the colors
let backgroundColor = radioBoxBackgroundColorConfiguration.getColor(viewModel) let backgroundColor = radioBoxBackgroundColorConfiguration.getColor(self)
let borderColor = radioBoxBorderColorConfiguration.getColor(viewModel) let borderColor = radioBoxBorderColorConfiguration.getColor(self)
let borderWidth = viewModel.selected ? selectorBorderWidthSelected : selectorBorderWidth let borderWidth = isSelected ? selectorBorderWidthSelected : selectorBorderWidth
selectorView.backgroundColor = backgroundColor selectorView.backgroundColor = backgroundColor
selectorView.layer.borderColor = borderColor.cgColor selectorView.layer.borderColor = borderColor.cgColor
@ -278,12 +291,12 @@ open class RadioBoxBase<ModelType: RadioBoxModel>: Control<ModelType> {
open override func draw(_ layer: CALayer, in ctx: CGContext) { open override func draw(_ layer: CALayer, in ctx: CGContext) {
let borderColor = radioBoxBorderColorConfiguration.getColor(model) let borderColor = radioBoxBorderColorConfiguration.getColor(self)
shapeLayer?.removeFromSuperlayer() shapeLayer?.removeFromSuperlayer()
shapeLayer = nil shapeLayer = nil
if model.strikethrough { if strikethrough {
let bounds = selectorView.bounds let bounds = selectorView.bounds
let length = max(bounds.size.height, bounds.size.width) let length = max(bounds.size.height, bounds.size.width)
guard length > 0.0, shapeLayer == nil else { return } guard length > 0.0, shapeLayer == nil else { return }

View File

@ -8,7 +8,7 @@
import Foundation import Foundation
import UIKit import UIKit
public class RadioBoxGroup: RadioBoxGroupBase<DefaultRadioBoxGroupModel, RadioBox> { public class RadioBoxGroup: RadioBoxGroupBase<RadioBox> {
public override func didSelect(_ selectedControl: RadioBox) { public override func didSelect(_ selectedControl: RadioBox) {
let oldSelectedControl = selectorViews.filter { $0.isSelected == true }.first let oldSelectedControl = selectorViews.filter { $0.isSelected == true }.first
@ -18,7 +18,20 @@ public class RadioBoxGroup: RadioBoxGroupBase<DefaultRadioBoxGroupModel, RadioBo
} }
} }
public class RadioBoxGroupBase<GroupModelType: RadioBoxGroupModel, ModelHandlerType: RadioBoxBase<GroupModelType.SelectorModelType>>: SelectorGroupSelectedHandlerBase<GroupModelType, ModelHandlerType> { public class RadioBoxGroupBase<ModelHandlerType: RadioBoxBase>: SelectorGroupSelectedHandlerBase<ModelHandlerType> {
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
public override var selectorViews: [ModelHandlerType] {
didSet {
for selector in selectorViews {
if !mainStackView.arrangedSubviews.contains(selector) {
mainStackView.addArrangedSubview(selector)
}
}
}
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Private Properties // MARK: - Private Properties
@ -69,22 +82,6 @@ public class RadioBoxGroupBase<GroupModelType: RadioBoxGroupModel, ModelHandlerT
}.store(in: &subscribers) }.store(in: &subscribers)
} }
open override func updateView(viewModel: ModelType) {
for selectorModel in viewModel.selectors {
//see if view is there for the model
if let foundSelectorView = findSelectorView(id: selectorModel.id) {
foundSelectorView.set(with: selectorModel)
} else {
//create view
let newSelectorView = createModelHandler(selector: selectorModel)
self.selectorViews.append(newSelectorView)
mainStackView.addArrangedSubview(newSelectorView)
}
}
}
public var selectedModelHandler: ModelHandlerType? { public var selectedModelHandler: ModelHandlerType? {
if let index = selectorViews.firstIndex(where: { element in if let index = selectorViews.firstIndex(where: { element in
return element.isSelected == true return element.isSelected == true

View File

@ -10,7 +10,7 @@ import UIKit
import VDSColorTokens import VDSColorTokens
import VDSFormControlsTokens import VDSFormControlsTokens
public class RadioButton: RadioButtonBase<DefaultRadioButtonModel>{ public class RadioButton: RadioButtonBase {
//for groups allows "toggle" //for groups allows "toggle"
open override func toggle() { open override func toggle() {
//removed error //removed error
@ -21,7 +21,7 @@ public class RadioButton: RadioButtonBase<DefaultRadioButtonModel>{
} }
} }
public class SoloRadioButton: RadioButtonBase<DefaultRadioButtonModel>{ public class SoloRadioButton: RadioButtonBase {
public override func initialSetup() { public override func initialSetup() {
super.initialSetup() super.initialSetup()
publisher(for: .touchUpInside) publisher(for: .touchUpInside)
@ -31,11 +31,38 @@ public class SoloRadioButton: RadioButtonBase<DefaultRadioButtonModel>{
} }
} }
open class RadioButtonBase<ModelType: RadioButtonModel>: Control<ModelType> { 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 // 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 = { private var mainStackView: UIStackView = {
return UIStackView().with { return UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false $0.translatesAutoresizingMaskIntoConstraints = false
@ -74,60 +101,39 @@ open class RadioButtonBase<ModelType: RadioButtonModel>: Control<ModelType> {
} }
}() }()
//can't bind to @Proxy open var labelText: String? { didSet { subject.send() }}
open override var isSelected: Bool {
get { model.selected }
set {
if model.selected != newValue {
model.selected = newValue
}
}
}
@Proxy(\.model.labelText) open var labelTextAttributes: [any LabelAttributeModel]? { didSet { subject.send() }}
open var labelText: String?
@Proxy(\.model.childText) open var childText: String? { didSet { subject.send() }}
open var childText: String?
@Proxy(\.model.showError) open var childTextAttributes: [any LabelAttributeModel]? { didSet { subject.send() }}
open var showError: Bool
@Proxy(\.model.errorText) open var showError: Bool = false { didSet { subject.send() }}
open var errorText: String?
@Proxy(\.model.inputId) open var errorText: String? { didSet { subject.send() }}
open var inputId: String?
@Proxy(\.model.value) open var inputId: String? { didSet { subject.send() }}
open var value: AnyHashable?
@Proxy(\.model.dataAnalyticsTrack) open var value: AnyHashable? { didSet { subject.send() }}
open var dataAnalyticsTrack: String?
@Proxy(\.model.dataClickStream) open var dataAnalyticsTrack: String? { didSet { subject.send() }}
open var dataClickStream: String?
@Proxy(\.model.dataTrack) open var dataClickStream: String? { didSet { subject.send() }}
open var dataTrack: String?
@Proxy(\.model.accessibilityHintEnabled) open var dataTrack: String? { didSet { subject.send() }}
open var accessibilityHintEnabled: String?
@Proxy(\.model.accessibilityHintDisabled) open var accessibilityHintEnabled: String? { didSet { subject.send() }}
open var accessibilityHintDisabled: String?
@Proxy(\.model.accessibilityValueEnabled) open var accessibilityHintDisabled: String? { didSet { subject.send() }}
open var accessibilityValueEnabled: String?
@Proxy(\.model.accessibilityValueDisabled) open var accessibilityValueEnabled: String? { didSet { subject.send() }}
open var accessibilityValueDisabled: String?
@Proxy(\.model.accessibilityLabelEnabled) open var accessibilityValueDisabled: String? { didSet { subject.send() }}
open var accessibilityLabelEnabled: String?
@Proxy(\.model.accessibilityLabelDisabled) open var accessibilityLabelEnabled: String? { didSet { subject.send() }}
open var accessibilityLabelDisabled: String?
open var accessibilityLabelDisabled: String? { didSet { subject.send() }}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Constraints // MARK: - Constraints
@ -167,7 +173,7 @@ open class RadioButtonBase<ModelType: RadioButtonModel>: Control<ModelType> {
selectorWidthConstraint = selectorView.widthAnchor.constraint(equalToConstant: selectorSize.width) selectorWidthConstraint = selectorView.widthAnchor.constraint(equalToConstant: selectorSize.width)
selectorWidthConstraint?.isActive = true selectorWidthConstraint?.isActive = true
updateSelector(model) updateSelector()
mainStackView.topAnchor.constraint(equalTo: topAnchor).isActive = true mainStackView.topAnchor.constraint(equalTo: topAnchor).isActive = true
mainStackView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true mainStackView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
@ -176,22 +182,32 @@ open class RadioButtonBase<ModelType: RadioButtonModel>: Control<ModelType> {
} }
func updateLabels(_ viewModel: ModelType) { func updateLabels() {
//deal with labels //deal with labels
if viewModel.shouldShowLabels { if shouldShowLabels {
//add the stackview to hold the 2 labels //add the stackview to hold the 2 labels
//top label //top label
if let labelModel = viewModel.labelModel { if let labelText {
primaryLabel.set(with: labelModel) primaryLabel.textPosition = .left
primaryLabel.typograpicalStyle = .BoldBodyLarge
primaryLabel.text = labelText
primaryLabel.surface = surface
primaryLabel.disabled = disabled
primaryLabel.attributes = labelTextAttributes
primaryLabel.isHidden = false primaryLabel.isHidden = false
} else { } else {
primaryLabel.isHidden = true primaryLabel.isHidden = true
} }
//bottom label //bottom label
if let childModel = viewModel.childModel { if let childText {
secondaryLabel.set(with: childModel) secondaryLabel.textPosition = .left
secondaryLabel.typograpicalStyle = .BodyLarge
secondaryLabel.text = childText
secondaryLabel.surface = surface
secondaryLabel.disabled = disabled
secondaryLabel.attributes = childTextAttributes
secondaryLabel.isHidden = false secondaryLabel.isHidden = false
} else { } else {
secondaryLabel.isHidden = true secondaryLabel.isHidden = true
@ -207,8 +223,12 @@ open class RadioButtonBase<ModelType: RadioButtonModel>: Control<ModelType> {
} }
//either add/remove the error from the main stack //either add/remove the error from the main stack
if let errorModel = model.errorModel, model.shouldShowError { if let errorText, shouldShowError {
errorLabel.set(with: errorModel) errorLabel.textPosition = .left
errorLabel.typograpicalStyle = .BodyMedium
errorLabel.text = errorText
errorLabel.surface = surface
errorLabel.disabled = disabled
mainStackView.spacing = 8 mainStackView.spacing = 8
errorLabel.isHidden = false errorLabel.isHidden = false
} else { } else {
@ -220,7 +240,7 @@ open class RadioButtonBase<ModelType: RadioButtonModel>: Control<ModelType> {
public override func reset() { public override func reset() {
super.reset() super.reset()
updateSelector(model) updateSelector()
setAccessibilityLabel() setAccessibilityLabel()
} }
@ -239,12 +259,12 @@ open class RadioButtonBase<ModelType: RadioButtonModel>: Control<ModelType> {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - State // MARK: - State
//-------------------------------------------------- //--------------------------------------------------
open override func updateView(viewModel: ModelType) { open override func updateView() {
updateLabels(viewModel) updateLabels()
updateSelector(viewModel) updateSelector()
setAccessibilityHint() setAccessibilityHint()
setAccessibilityValue(viewModel.selected) setAccessibilityValue(isSelected)
setAccessibilityLabel(viewModel.selected) setAccessibilityLabel(isSelected)
setNeedsLayout() setNeedsLayout()
layoutIfNeeded() layoutIfNeeded()
} }
@ -295,7 +315,7 @@ open class RadioButtonBase<ModelType: RadioButtonModel>: Control<ModelType> {
radioButtonSize radioButtonSize
} }
open func updateSelector(_ viewModel: ModelType) { open func updateSelector() {
if let shapeLayer = shapeLayer, let sublayers = layer.sublayers, sublayers.contains(shapeLayer) { if let shapeLayer = shapeLayer, let sublayers = layer.sublayers, sublayers.contains(shapeLayer) {
shapeLayer.removeFromSuperlayer() shapeLayer.removeFromSuperlayer()
@ -307,9 +327,9 @@ open class RadioButtonBase<ModelType: RadioButtonModel>: Control<ModelType> {
guard length > 0.0, shapeLayer == nil else { return } guard length > 0.0, shapeLayer == nil else { return }
//get the colors //get the colors
let backgroundColor = radioButtonBackgroundColorConfiguration.getColor(viewModel) let backgroundColor = radioButtonBackgroundColorConfiguration.getColor(self)
let borderColor = radioButtonBorderColorConfiguration.getColor(viewModel) let borderColor = radioButtonBorderColorConfiguration.getColor(self)
let radioSelectedColor = radioButtonCheckColorConfiguration.getColor(viewModel) let radioSelectedColor = radioButtonCheckColorConfiguration.getColor(self)
selectorView.backgroundColor = backgroundColor selectorView.backgroundColor = backgroundColor
selectorView.layer.borderColor = borderColor.cgColor selectorView.layer.borderColor = borderColor.cgColor

View File

@ -8,7 +8,7 @@
import Foundation import Foundation
import UIKit import UIKit
public class RadioButtonGroup: RadioButtonGroupBase<DefaultRadioButtonGroupModel, RadioButton> { public class RadioButtonGroup: RadioButtonGroupBase<RadioButton> {
public override func didSelect(_ selectedControl: RadioButton) { public override func didSelect(_ selectedControl: RadioButton) {
let oldSelectedControl = selectorViews.filter { $0.isSelected == true }.first let oldSelectedControl = selectorViews.filter { $0.isSelected == true }.first
@ -21,22 +21,33 @@ public class RadioButtonGroup: RadioButtonGroupBase<DefaultRadioButtonGroupModel
} }
} }
public class RadioButtonGroupBase<GroupModelType: RadioButtonGroupModel, ModelHandlerType: RadioButtonBase<GroupModelType.SelectorModelType>>: SelectorGroupSelectedHandlerBase<GroupModelType, ModelHandlerType> { public class RadioButtonGroupBase<ModelHandlerType: RadioButtonBase>: SelectorGroupSelectedHandlerBase<ModelHandlerType> {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Public Properties // 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 { public var showError: Bool {
get { model.showError } get { _showError }
set { set {
var newShowError = newValue var newShowError = newValue
if selectedModel != nil, newShowError { if selectedModelHandler != nil, newShowError {
newShowError = false newShowError = false
} }
selectorViews.forEach { handler in selectorViews.forEach { handler in
handler.showError = newShowError handler.showError = newShowError
} }
model.showError = newShowError _showError = newShowError
} }
} }
@ -69,22 +80,6 @@ public class RadioButtonGroupBase<GroupModelType: RadioButtonGroupModel, ModelHa
mainStackView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true mainStackView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
} }
open override func updateView(viewModel: ModelType) {
for selectorModel in viewModel.selectors {
//see if view is there for the model
if let foundSelectorView = findSelectorView(id: selectorModel.id) {
foundSelectorView.set(with: selectorModel)
} else {
//create view
let newSelectorView = createModelHandler(selector: selectorModel)
self.selectorViews.append(newSelectorView)
mainStackView.addArrangedSubview(newSelectorView)
}
}
}
public var selectedModelHandler: ModelHandlerType? { public var selectedModelHandler: ModelHandlerType? {
if let index = selectorViews.firstIndex(where: { element in if let index = selectorViews.firstIndex(where: { element in
return element.isSelected == true return element.isSelected == true

View File

@ -11,7 +11,7 @@ import VDSColorTokens
import VDSFormControlsTokens import VDSFormControlsTokens
import Combine import Combine
public class RadioSwatch: RadioSwatchBase<DefaultRadioSwatchModel>{ public class RadioSwatch: RadioSwatchBase{
public override func initialSetup() { public override func initialSetup() {
super.initialSetup() super.initialSetup()
publisher(for: .touchUpInside) publisher(for: .touchUpInside)
@ -21,7 +21,25 @@ public class RadioSwatch: RadioSwatchBase<DefaultRadioSwatchModel>{
} }
} }
open class RadioSwatchBase<ModelType: RadioSwatchModel>: Control<ModelType> { 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 // MARK: - Public Properties
@ -39,54 +57,37 @@ open class RadioSwatchBase<ModelType: RadioSwatchModel>: Control<ModelType> {
} }
}() }()
//can't bind to @Proxy open var fillImage: UIImage? { didSet { subject.send() }}
open override var isSelected: Bool {
get { model.selected }
set {
if model.selected != newValue {
model.selected = newValue
}
}
}
@Proxy(\.model.text) open var text: String = "" { didSet { subject.send() }}
open var text: String
@Proxy(\.model.strikethrough) open var primaryColor: UIColor? { didSet { subject.send() }}
open var strikethrough: Bool
@Proxy(\.model.inputId) open var secondaryColor: UIColor? { didSet { subject.send() }}
open var inputId: String?
@Proxy(\.model.value) open var strikethrough: Bool = false { didSet { subject.send() }}
open var value: AnyHashable?
@Proxy(\.model.dataAnalyticsTrack) open var inputId: String? { didSet { subject.send() }}
open var dataAnalyticsTrack: String?
@Proxy(\.model.dataClickStream) open var value: AnyHashable? { didSet { subject.send() }}
open var dataClickStream: String?
@Proxy(\.model.dataTrack) open var dataAnalyticsTrack: String? { didSet { subject.send() }}
open var dataTrack: String?
@Proxy(\.model.accessibilityHintEnabled) open var dataClickStream: String? { didSet { subject.send() }}
open var accessibilityHintEnabled: String?
@Proxy(\.model.accessibilityHintDisabled) open var dataTrack: String? { didSet { subject.send() }}
open var accessibilityHintDisabled: String?
@Proxy(\.model.accessibilityValueEnabled) open var accessibilityHintEnabled: String? { didSet { subject.send() }}
open var accessibilityValueEnabled: String?
@Proxy(\.model.accessibilityValueDisabled) open var accessibilityHintDisabled: String? { didSet { subject.send() }}
open var accessibilityValueDisabled: String?
@Proxy(\.model.accessibilityLabelEnabled) open var accessibilityValueEnabled: String? { didSet { subject.send() }}
open var accessibilityLabelEnabled: String?
@Proxy(\.model.accessibilityLabelDisabled) open var accessibilityValueDisabled: String? { didSet { subject.send() }}
open var accessibilityLabelDisabled: String?
open var accessibilityLabelEnabled: String? { didSet { subject.send() }}
open var accessibilityLabelDisabled: String? { didSet { subject.send() }}
//functions //functions
//-------------------------------------------------- //--------------------------------------------------
@ -107,7 +108,7 @@ open class RadioSwatchBase<ModelType: RadioSwatchModel>: Control<ModelType> {
selectorView.addSubview(fillView) selectorView.addSubview(fillView)
updateSelector(model) updateSelector()
selectorView.topAnchor.constraint(equalTo: topAnchor).isActive = true selectorView.topAnchor.constraint(equalTo: topAnchor).isActive = true
selectorView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true selectorView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
@ -128,7 +129,7 @@ open class RadioSwatchBase<ModelType: RadioSwatchModel>: Control<ModelType> {
public override func reset() { public override func reset() {
super.reset() super.reset()
updateSelector(model) updateSelector()
setAccessibilityLabel() setAccessibilityLabel()
} }
@ -140,11 +141,11 @@ open class RadioSwatchBase<ModelType: RadioSwatchModel>: Control<ModelType> {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - State // MARK: - State
//-------------------------------------------------- //--------------------------------------------------
open override func updateView(viewModel: ModelType) { open override func updateView() {
updateSelector(viewModel) updateSelector()
setAccessibilityHint() setAccessibilityHint()
setAccessibilityValue(viewModel.selected) setAccessibilityValue(isSelected)
setAccessibilityLabel(viewModel.selected) setAccessibilityLabel(isSelected)
setNeedsLayout() setNeedsLayout()
layoutIfNeeded() layoutIfNeeded()
} }
@ -199,15 +200,15 @@ open class RadioSwatchBase<ModelType: RadioSwatchModel>: Control<ModelType> {
return swatchSize return swatchSize
} }
open func updateSelector(_ viewModel: ModelType) { open func updateSelector() {
//get the colors //get the colors
let backgroundColor = radioSwatchBackgroundColorConfiguration.getColor(viewModel) let backgroundColor = radioSwatchBackgroundColorConfiguration.getColor(self)
let borderColor = viewModel.selected ? radioSwatchBorderColorConfiguration.getColor(viewModel) : .clear let borderColor = isSelected ? radioSwatchBorderColorConfiguration.getColor(self) : .clear
let fillBorderColor = radioSwatchFillBorderColorConfiguration.getColor(viewModel) let fillBorderColor = radioSwatchFillBorderColorConfiguration.getColor(self)
selectorView.backgroundColor = backgroundColor selectorView.backgroundColor = backgroundColor
selectorView.layer.borderColor = borderColor.cgColor selectorView.layer.borderColor = borderColor.cgColor
selectorView.layer.cornerRadius = selectorView.bounds.width * 0.5 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 selectorView.layer.masksToBounds = true
gradientLayer?.removeFromSuperlayer() gradientLayer?.removeFromSuperlayer()
@ -215,14 +216,14 @@ open class RadioSwatchBase<ModelType: RadioSwatchModel>: Control<ModelType> {
var fillColorBackground: UIColor = .clear var fillColorBackground: UIColor = .clear
if let fillImage = viewModel.fillImage { if let fillImage {
fillView.image = viewModel.disabled ? fillImage.image(alpha: disabledAlpha) : fillImage fillView.image = disabled ? fillImage.image(alpha: disabledAlpha) : fillImage
} else { } else {
fillView.image = nil fillView.image = nil
if let primary = viewModel.primaryColor, let secondary = viewModel.secondaryColor { if let primary = primaryColor, let secondary = secondaryColor {
let firstColor = viewModel.disabled ? primary.withAlphaComponent(disabledAlpha) : primary let firstColor = disabled ? primary.withAlphaComponent(disabledAlpha) : primary
let secondColor = viewModel.disabled ? secondary.withAlphaComponent(disabledAlpha) : secondary let secondColor = disabled ? secondary.withAlphaComponent(disabledAlpha) : secondary
let gradient = CAGradientLayer() let gradient = CAGradientLayer()
gradientLayer = gradient gradientLayer = gradient
gradient.frame = fillView.bounds gradient.frame = fillView.bounds
@ -231,10 +232,11 @@ open class RadioSwatchBase<ModelType: RadioSwatchModel>: Control<ModelType> {
gradient.transform = CATransform3DMakeRotation(135.0 / 180.0 * .pi, 0.0, 0.0, 1.0) gradient.transform = CATransform3DMakeRotation(135.0 / 180.0 * .pi, 0.0, 0.0, 1.0)
fillView.layer.addSublayer(gradient) fillView.layer.addSublayer(gradient)
} else { } 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.borderColor = fillBorderColor.cgColor
fillView.layer.cornerRadius = fillView.bounds.width * 0.5 fillView.layer.cornerRadius = fillView.bounds.width * 0.5
fillView.layer.borderWidth = selectorBorderWidth fillView.layer.borderWidth = selectorBorderWidth
@ -250,12 +252,12 @@ open class RadioSwatchBase<ModelType: RadioSwatchModel>: Control<ModelType> {
open override func draw(_ layer: CALayer, in ctx: CGContext) { open override func draw(_ layer: CALayer, in ctx: CGContext) {
let borderColor = radioSwatchBorderColorConfiguration.getColor(model) let borderColor = radioSwatchBorderColorConfiguration.getColor(self)
shapeLayer?.removeFromSuperlayer() shapeLayer?.removeFromSuperlayer()
shapeLayer = nil shapeLayer = nil
if model.strikethrough { if strikethrough {
let bounds = selectorView.bounds let bounds = selectorView.bounds
let length = max(bounds.size.height, bounds.size.width) let length = max(bounds.size.height, bounds.size.width)
guard length > 0.0, shapeLayer == nil else { return } guard length > 0.0, shapeLayer == nil else { return }

View File

@ -9,27 +9,29 @@ import Foundation
import UIKit import UIKit
import Combine import Combine
public class RadioSwatchGroup: RadioSwatchGroupBase<DefaultRadioSwatchGroupModel, RadioSwatch> { public class RadioSwatchGroup: RadioSwatchGroupBase<RadioSwatch> {
public override func didSelect(selector: RadioSwatch) { 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<RadioSwatch> { let cell = collectionView.cellForItem(at: IndexPath(item: index, section: 0)) as? CollectionViewCell<RadioSwatch> {
cell.modelHandler.toggle() cell.modelHandler.toggle()
} }
selector.toggle() selector.toggle()
label.text = selector.model.text label.text = selector.text
valueChanged() valueChanged()
} }
} }
public class RadioSwatchGroupBase<GroupModelType: RadioSwatchGroupModel, ModelHandlerType: RadioSwatchBase<GroupModelType.SelectorModelType>>: Control<GroupModelType>, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UICollectionViewDelegate { public class RadioSwatchGroupBase<ModelHandlerType: RadioSwatchBase>: SelectorGroupSelectedHandlerBase<ModelHandlerType>, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UICollectionViewDelegate {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Public Properties // MARK: - Public Properties
//-------------------------------------------------- //--------------------------------------------------
public var selectedModel: ModelHandlerType.ModelType? { public override var selectorViews: [ModelHandlerType] {
return model.selectedModel didSet {
collectionView.reloadData()
}
} }
//-------------------------------------------------- //--------------------------------------------------
@ -109,16 +111,14 @@ public class RadioSwatchGroupBase<GroupModelType: RadioSwatchGroupModel, ModelHa
} }
open func setHeight() { open func setHeight() {
let swatches = model.selectors guard selectorViews.count > 0 else {
guard swatches.count > 0 else {
collectionViewHeight?.constant = 0 collectionViewHeight?.constant = 0
return return
} }
// Calculate the height // Calculate the height
let swatchesInRow = floor(CGFloat(collectionView.bounds.width/(cellSize + itemSpacing))) 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)) let height = (numberOfRows * cellSize) + (itemSpacing * (numberOfRows-1))
collectionViewHeight?.constant = CGFloat(height) collectionViewHeight?.constant = CGFloat(height)
@ -130,28 +130,20 @@ public class RadioSwatchGroupBase<GroupModelType: RadioSwatchGroupModel, ModelHa
collectionView.dataSource = self collectionView.dataSource = self
} }
open override func updateView(viewModel: ModelType) { open override func updateView() {
label.set(with: viewModel.labelModel) label.textPosition = .left
label.typograpicalStyle = .BodySmall
label.text = selectedHandler?.text ?? " "
label.surface = surface
label.disabled = disabled
collectionView.reloadData() collectionView.reloadData()
setNeedsLayout() setNeedsLayout()
} }
//Refactor into new CollectionView Selector protocol private func updateSelectors() {
public func updateSelectors(){ for selector in selectorViews {
let selectors = model.selectors.compactMap { existing in selector.surface = surface
return existing.copyWith { selector.disabled = disabled
$0.disabled = disabled
$0.surface = surface
}
}
model.selectors = selectors
}
public func replace(viewModel: ModelHandlerType.ModelType){
if let index = model.selectors.firstIndex(where: { element in
return element.id == viewModel.id
}) {
model.selectors[index] = viewModel
} }
} }
@ -166,7 +158,7 @@ public class RadioSwatchGroupBase<GroupModelType: RadioSwatchGroupModel, ModelHa
// MARK: - UICollectionViewDelegate // MARK: - UICollectionViewDelegate
//-------------------------------------------------- //--------------------------------------------------
open func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool { open func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
return !model.selectors[indexPath.row].disabled return !selectorViews[indexPath.row].disabled
} }
open func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { open func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
@ -182,34 +174,16 @@ public class RadioSwatchGroupBase<GroupModelType: RadioSwatchGroupModel, ModelHa
} }
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return model.selectors.count return selectorViews.count
} }
var cellsubs: [Int: AnyCancellable] = [:]
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath) as? CollectionViewCell<ModelHandlerType> else { return UICollectionViewCell() } guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath) as? CollectionViewCell<ModelHandlerType> else { return UICollectionViewCell() }
let model = model.selectors[indexPath.row] let model = selectorViews[indexPath.row]
cell.modelHandler = selectorViews[indexPath.row]
cell.modelHandler.isUserInteractionEnabled = false 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 return cell
} }
@ -217,18 +191,4 @@ public class RadioSwatchGroupBase<GroupModelType: RadioSwatchGroupModel, ModelHa
open func didSelect(selector: ModelHandlerType) { open func didSelect(selector: ModelHandlerType) {
fatalError("Must override didSelect") fatalError("Must override didSelect")
} }
public func valueChanged() {
DispatchQueue.main.asyncAfter(deadline: .now() + Constants.ModelStateDebounce) { [weak self] in
self?.sendActions(for: .valueChanged)
}
}
public var selectedModelHandler: ModelHandlerType? {
guard let index = model.selectors.firstIndex(where: {$0.selected == true }),
let cell = collectionView.cellForItem(at: IndexPath(item: index, section: 0)) as? CollectionViewCell<ModelHandlerType> else {
return nil
}
return cell.modelHandler
}
} }

View File

@ -18,7 +18,7 @@ import Combine
Knob: The circular indicator that slides on the container. Knob: The circular indicator that slides on the container.
*/ */
public class Toggle: ToggleBase<DefaultToggleModel>{ public class Toggle: ToggleBase{
public override func initialSetup() { public override func initialSetup() {
super.initialSetup() super.initialSetup()
publisher(for: .touchUpInside) publisher(for: .touchUpInside)
@ -28,7 +28,25 @@ public class Toggle: ToggleBase<DefaultToggleModel>{
} }
} }
open class ToggleBase<ModelType: ToggleModel>: Control<ModelType> { 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 // MARK: - Private Properties
@ -86,62 +104,60 @@ open class ToggleBase<ModelType: ToggleModel>: Control<ModelType> {
$0.forFalse.disabled.darkColor = VDSColor.paletteGray44 $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 // MARK: - Public Properties
//-------------------------------------------------- //--------------------------------------------------
@Proxy(\.model.on) open var isOn: Bool = false { didSet { subject.send() }}
open var isOn: Bool
@Proxy(\.model.showText) public var showText: Bool = false { didSet { subject.send() }}
public var showText: Bool
@Proxy(\.model.onText) public var onText: String = "On" { didSet { subject.send() }}
public var onText: String
@Proxy(\.model.offText) public var offText: String = "Off" { didSet { subject.send() }}
public var offText: String
@Proxy(\.model.textSize) public var textSize: ToggleTextSize = .small { didSet { subject.send() }}
public var textSize: ToggleTextSize
@Proxy(\.model.textWeight) public var textWeight: ToggleTextWeight = .regular { didSet { subject.send() }}
public var textWeight: ToggleTextWeight
@Proxy(\.model.textPosition) public var textPosition: ToggleTextPosition = .left { didSet { subject.send() }}
public var textPosition: ToggleTextPosition
@Proxy(\.model.inputId) open var inputId: String? { didSet { subject.send() }}
open var inputId: String?
@Proxy(\.model.value) open var value: AnyHashable? { didSet { subject.send() }}
open var value: AnyHashable?
@Proxy(\.model.dataAnalyticsTrack) open var dataAnalyticsTrack: String? { didSet { subject.send() }}
open var dataAnalyticsTrack: String?
@Proxy(\.model.dataClickStream) open var dataClickStream: String? { didSet { subject.send() }}
open var dataClickStream: String?
@Proxy(\.model.dataTrack) open var dataTrack: String? { didSet { subject.send() }}
open var dataTrack: String?
@Proxy(\.model.accessibilityHintEnabled) open var accessibilityHintEnabled: String? { didSet { subject.send() }}
open var accessibilityHintEnabled: String?
@Proxy(\.model.accessibilityHintDisabled) open var accessibilityHintDisabled: String? { didSet { subject.send() }}
open var accessibilityHintDisabled: String?
@Proxy(\.model.accessibilityValueEnabled) open var accessibilityValueEnabled: String? { didSet { subject.send() }}
open var accessibilityValueEnabled: String?
@Proxy(\.model.accessibilityValueDisabled) open var accessibilityValueDisabled: String? { didSet { subject.send() }}
open var accessibilityValueDisabled: String?
@Proxy(\.model.accessibilityLabelEnabled) open var accessibilityLabelEnabled: String? { didSet { subject.send() }}
open var accessibilityLabelEnabled: String?
@Proxy(\.model.accessibilityLabelDisabled) open var accessibilityLabelDisabled: String? { didSet { subject.send() }}
open var accessibilityLabelDisabled: String?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Constraints // MARK: - Constraints
@ -157,12 +173,12 @@ open class ToggleBase<ModelType: ToggleModel>: Control<ModelType> {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Toggle // MARK: - Toggle
//-------------------------------------------------- //--------------------------------------------------
private func updateToggle(_ viewModel: ModelType) { private func updateToggle() {
//private func //private func
func constrainKnob(){ func constrainKnob(){
self.knobLeadingConstraint?.isActive = false self.knobLeadingConstraint?.isActive = false
self.knobTrailingConstraint?.isActive = false self.knobTrailingConstraint?.isActive = false
if viewModel.on { if isOn {
self.knobTrailingConstraint = self.toggleView.trailingAnchor.constraint(equalTo: self.knobView.trailingAnchor, constant: 2) self.knobTrailingConstraint = self.toggleView.trailingAnchor.constraint(equalTo: self.knobView.trailingAnchor, constant: 2)
self.knobLeadingConstraint = self.knobView.leadingAnchor.constraint(greaterThanOrEqualTo: self.toggleView.leadingAnchor) self.knobLeadingConstraint = self.knobView.leadingAnchor.constraint(greaterThanOrEqualTo: self.toggleView.leadingAnchor)
} else { } else {
@ -175,10 +191,10 @@ open class ToggleBase<ModelType: ToggleModel>: Control<ModelType> {
self.layoutIfNeeded() self.layoutIfNeeded()
} }
let toggleColor = toggleColorConfiguration.getColor(viewModel) let toggleColor = toggleColorConfiguration.getColor(self)
let knobColor = knobColorConfiguration.getColor(viewModel) let knobColor = knobColorConfiguration.getColor(self)
if viewModel.disabled { if disabled {
toggleView.backgroundColor = toggleColor toggleView.backgroundColor = toggleColor
knobView.backgroundColor = knobColor knobView.backgroundColor = knobColor
constrainKnob() constrainKnob()
@ -197,16 +213,21 @@ open class ToggleBase<ModelType: ToggleModel>: Control<ModelType> {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Labels // MARK: - Labels
//-------------------------------------------------- //--------------------------------------------------
private func updateLabel(_ viewModel: ModelType) { private func updateLabel() {
let showText = viewModel.showText
stackView.spacing = showText ? 12 : 0 stackView.spacing = showText ? 12 : 0
label.set(with: viewModel.labelModel)
if stackView.subviews.contains(label) { if stackView.subviews.contains(label) {
label.removeFromSuperview() label.removeFromSuperview()
} }
if showText { 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 { if textPosition == .left {
stackView.insertArrangedSubview(label, at: 0) stackView.insertArrangedSubview(label, at: 0)
} else { } else {
@ -246,7 +267,7 @@ open class ToggleBase<ModelType: ToggleModel>: Control<ModelType> {
toggleView.layer.cornerRadius = toggleSize.height / 2.0 toggleView.layer.cornerRadius = toggleSize.height / 2.0
knobView.layer.cornerRadius = knobSize.height / 2.0 knobView.layer.cornerRadius = knobSize.height / 2.0
toggleView.backgroundColor = toggleColorConfiguration.getColor(model) toggleView.backgroundColor = toggleColorConfiguration.getColor(self)
toggleContainerView.addSubview(toggleView) toggleContainerView.addSubview(toggleView)
toggleView.addSubview(knobView) toggleView.addSubview(knobView)
@ -261,7 +282,7 @@ open class ToggleBase<ModelType: ToggleModel>: Control<ModelType> {
toggleView.bottomAnchor.constraint(greaterThanOrEqualTo: knobView.bottomAnchor).isActive = true toggleView.bottomAnchor.constraint(greaterThanOrEqualTo: knobView.bottomAnchor).isActive = true
updateLabel(model) updateLabel()
stackView.addArrangedSubview(toggleContainerView) stackView.addArrangedSubview(toggleContainerView)
stackView.topAnchor.constraint(equalTo: topAnchor).isActive = true stackView.topAnchor.constraint(equalTo: topAnchor).isActive = true
stackView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true stackView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
@ -275,8 +296,8 @@ open class ToggleBase<ModelType: ToggleModel>: Control<ModelType> {
public override func reset() { public override func reset() {
super.reset() super.reset()
toggleView.backgroundColor = toggleColorConfiguration.getColor(model) toggleView.backgroundColor = toggleColorConfiguration.getColor(self)
knobView.backgroundColor = knobColorConfiguration.getColor(model) knobView.backgroundColor = knobColorConfiguration.getColor(self)
setAccessibilityLabel() setAccessibilityLabel()
} }
@ -289,13 +310,13 @@ open class ToggleBase<ModelType: ToggleModel>: Control<ModelType> {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - State // MARK: - State
//-------------------------------------------------- //--------------------------------------------------
open override func updateView(viewModel: ModelType) { open override func updateView() {
updateLabel(viewModel) updateLabel()
updateToggle(viewModel) updateToggle()
setAccessibilityHint() setAccessibilityHint()
setAccessibilityValue(viewModel.on) setAccessibilityValue(isOn)
setAccessibilityLabel(viewModel.on) setAccessibilityLabel(isOn)
backgroundColor = viewModel.surface.color backgroundColor = surface.color
setNeedsLayout() setNeedsLayout()
layoutIfNeeded() layoutIfNeeded()
} }

View File

@ -18,37 +18,31 @@ public protocol Accessable {
} }
//Configurations to set within the UIControl //Configurations to set within the UIControl
extension ModelHandlerable where Self: UIView { extension ModelHandlerable where Self: UIView, Self: Accessable {
private var accessableModel: Accessable? {
guard let model = self.model as? Accessable else {
return nil
}
return model
}
public func setAccessibilityHint(_ override: Bool? = nil) { public func setAccessibilityHint(_ override: Bool? = nil) {
let check = override ?? !model.disabled let check = override ?? !disabled
if let value = accessableModel?.accessibilityHintEnabled, check { if let value = accessibilityHintEnabled, check {
accessibilityHint = value accessibilityHint = value
} else if let value = accessableModel?.accessibilityHintDisabled, !check { } else if let value = accessibilityHintDisabled, !check {
accessibilityHint = value accessibilityHint = value
} }
} }
public func setAccessibilityValue(_ override: Bool? = nil) { public func setAccessibilityValue(_ override: Bool? = nil) {
let check = override ?? !model.disabled let check = override ?? !disabled
if let value = accessableModel?.accessibilityValueEnabled, check { if let value = accessibilityValueEnabled, check {
accessibilityValue = value accessibilityValue = value
} else if let value = accessableModel?.accessibilityValueDisabled, !check { } else if let value = accessibilityValueDisabled, !check {
accessibilityValue = value accessibilityValue = value
} }
} }
public func setAccessibilityLabel(_ override: Bool? = nil) { public func setAccessibilityLabel(_ override: Bool? = nil) {
let check = override ?? !model.disabled let check = override ?? !disabled
if let value = accessableModel?.accessibilityLabelEnabled, check { if let value = accessibilityLabelEnabled, check {
accessibilityLabel = value accessibilityLabel = value
} else if let value = accessableModel?.accessibilityLabelDisabled, !check { } else if let value = accessibilityLabelDisabled, !check {
accessibilityLabel = value accessibilityLabel = value
} }
} }

View File

@ -9,44 +9,26 @@ import Foundation
import Combine import Combine
import UIKit import UIKit
public protocol ModelHandlerable: AnyObject, Initable { public protocol ModelHandlerable: AnyObject, Initable, Disabling, Surfaceable {
associatedtype ModelType: Modelable var subject: PassthroughSubject<Void, Never> { get set }
var model: ModelType { get set }
var modelPublisher: Published<ModelType>.Publisher { get }
var subscribers: Set<AnyCancellable> { get set } var subscribers: Set<AnyCancellable> { get set }
init(with model: ModelType) func updateView()
func set(with model: ModelType)
func shouldUpdateView(viewModel: ModelType) -> Bool
func updateView(viewModel: ModelType)
} }
extension ModelHandlerable { 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() { public func setupUpdateView() {
handlerPublisher() handlerPublisher()
.subscribe(on: RunLoop.main) .subscribe(on: RunLoop.main)
.sink { [weak self] viewModel in .sink { [weak self] _ in
self?.updateView(viewModel: viewModel) self?.updateView()
} }
.store(in: &subscribers) .store(in: &subscribers)
} }
public func handlerPublisher() -> AnyPublisher<ModelType, Never> { public func handlerPublisher() -> AnyPublisher<Void, Never> {
modelPublisher subject
.eraseToAnyPublisher()
.debounce(for: .seconds(Constants.ModelStateDebounce), scheduler: RunLoop.main) .debounce(for: .seconds(Constants.ModelStateDebounce), scheduler: RunLoop.main)
.eraseToAnyPublisher() .eraseToAnyPublisher()
} }