refactored to remove models
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
parent
dead6947f1
commit
4bf9ce83b5
@ -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 {
|
|
||||||
didSet {
|
|
||||||
self.isEnabled = !disabled
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
open var disabled: Bool = false { didSet { 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
|
||||||
|
|||||||
@ -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()
|
||||||
@ -58,22 +51,7 @@ open class CollectionViewCell<ModelHandlerType: ModelHandlerable & UIView>: UICo
|
|||||||
setup()
|
setup()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
|
||||||
// MARK: - Overrides
|
|
||||||
//--------------------------------------------------
|
|
||||||
open func shouldUpdateView(viewModel: ModelType) -> Bool {
|
|
||||||
return modelHandler.shouldUpdateView(viewModel: viewModel)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func updateView(viewModel: ModelType) {
|
|
||||||
modelHandler.updateView(viewModel: viewModel)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func set(with model: ModelType) {
|
|
||||||
modelHandler.set(with: model)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - ViewProtocol
|
// MARK: - ViewProtocol
|
||||||
/// Will be called only once.
|
/// Will be called only once.
|
||||||
open func setup() {
|
open func setup() {
|
||||||
|
|||||||
@ -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 }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 {
|
|
||||||
didSet {
|
|
||||||
self.isEnabled = !disabled
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
open var disabled: Bool = false { didSet { 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
|
||||||
|
|||||||
@ -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
|
||||||
@ -33,42 +33,10 @@ 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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,39 +10,31 @@ 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>()
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
private var initialSetupPerformed = false
|
private var initialSetupPerformed = false
|
||||||
|
|
||||||
@Proxy(\.model.surface)
|
|
||||||
open var surface: Surface
|
|
||||||
|
|
||||||
@Proxy(\.model.disabled)
|
open var surface: Surface = .light { didSet { subject.send() }}
|
||||||
open var disabled: Bool {
|
|
||||||
didSet {
|
|
||||||
self.isEnabled = !disabled
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
open var disabled: Bool = false { didSet { 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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,12 +45,6 @@ open class View<ModelType: Modelable>: UIView, ModelHandlerable, ViewProtocol, R
|
|||||||
super.init(frame: .zero)
|
super.init(frame: .zero)
|
||||||
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)
|
||||||
@ -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
|
||||||
|
|||||||
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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,39 +29,29 @@ 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 {
|
open var size: ButtonSize = .large { didSet { subject.send() }}
|
||||||
isEnabled = !disabled
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Proxy(\.model.text)
|
open var width: CGFloat? { didSet { subject.send() }}
|
||||||
open var text: String?
|
|
||||||
|
|
||||||
@Proxy(\.model.use)
|
open var surface: Surface = .light { didSet { subject.send() }}
|
||||||
open var use: Use
|
|
||||||
|
|
||||||
@Proxy(\.model.size)
|
|
||||||
open var size: ButtonSize
|
|
||||||
|
|
||||||
@Proxy(\.model.width)
|
|
||||||
open var width: CGFloat?
|
|
||||||
|
|
||||||
|
open var disabled: Bool = false { didSet { 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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// 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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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,60 +95,77 @@ 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 {
|
|
||||||
if model.selected != newValue {
|
|
||||||
model.selected = newValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Proxy(\.model.labelText)
|
open var labelText: String? { didSet { subject.send() }}
|
||||||
open var labelText: String?
|
|
||||||
|
|
||||||
@Proxy(\.model.childText)
|
open var labelTextAttributes: [any LabelAttributeModel]? { didSet { subject.send() }}
|
||||||
open var childText: String?
|
|
||||||
|
|
||||||
@Proxy(\.model.showError)
|
open var childText: String? { didSet { subject.send() }}
|
||||||
open var showError: Bool
|
|
||||||
|
|
||||||
@Proxy(\.model.errorText)
|
|
||||||
open var errorText: String?
|
|
||||||
|
|
||||||
@Proxy(\.model.inputId)
|
open var childTextAttributes: [any LabelAttributeModel]? { didSet { subject.send() }}
|
||||||
open var inputId: String?
|
|
||||||
|
|
||||||
@Proxy(\.model.value)
|
open var showError: Bool = false { didSet { subject.send() }}
|
||||||
open var value: AnyHashable?
|
|
||||||
|
|
||||||
@Proxy(\.model.dataAnalyticsTrack)
|
open var errorText: String? { didSet { subject.send() }}
|
||||||
open var dataAnalyticsTrack: String?
|
|
||||||
|
|
||||||
@Proxy(\.model.dataClickStream)
|
open var inputId: String? { didSet { subject.send() }}
|
||||||
open var dataClickStream: String?
|
|
||||||
|
|
||||||
@Proxy(\.model.dataTrack)
|
open var value: AnyHashable? { didSet { subject.send() }}
|
||||||
open var dataTrack: String?
|
|
||||||
|
open var dataAnalyticsTrack: String? { didSet { subject.send() }}
|
||||||
|
|
||||||
@Proxy(\.model.accessibilityHintEnabled)
|
open var dataClickStream: String? { didSet { subject.send() }}
|
||||||
open var accessibilityHintEnabled: String?
|
|
||||||
|
open var dataTrack: String? { didSet { subject.send() }}
|
||||||
|
|
||||||
|
open var accessibilityHintEnabled: String? { didSet { subject.send() }}
|
||||||
|
|
||||||
@Proxy(\.model.accessibilityHintDisabled)
|
open var accessibilityHintDisabled: String? { 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?
|
|
||||||
|
|
||||||
|
private var labelModel: DefaultLabelModel? {
|
||||||
|
guard let labelText = labelText else { return nil }
|
||||||
|
var model = DefaultLabelModel()
|
||||||
|
model.textPosition = .left
|
||||||
|
model.typograpicalStyle = .BoldBodyLarge
|
||||||
|
model.text = labelText
|
||||||
|
model.surface = surface
|
||||||
|
model.disabled = disabled
|
||||||
|
model.attributes = labelTextAttributes
|
||||||
|
return model
|
||||||
|
}
|
||||||
|
|
||||||
|
private var childModel: DefaultLabelModel? {
|
||||||
|
guard let childText = childText else { return nil }
|
||||||
|
var model = DefaultLabelModel()
|
||||||
|
model.textPosition = .left
|
||||||
|
model.typograpicalStyle = .BodyLarge
|
||||||
|
model.text = childText
|
||||||
|
model.surface = surface
|
||||||
|
model.disabled = disabled
|
||||||
|
model.attributes = childTextAttributes
|
||||||
|
return model
|
||||||
|
}
|
||||||
|
|
||||||
|
private var errorModel: DefaultLabelModel? {
|
||||||
|
guard let errorText = errorText, showError else { return nil }
|
||||||
|
var model = DefaultLabelModel()
|
||||||
|
model.textPosition = .left
|
||||||
|
model.typograpicalStyle = .BodyMedium
|
||||||
|
model.text = errorText
|
||||||
|
model.surface = surface
|
||||||
|
model.disabled = disabled
|
||||||
|
return model
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Constraints
|
// 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,22 +78,6 @@ public class CheckboxGroupBase<GroupModelType: CheckboxGroupModel, ModelHandlerT
|
|||||||
mainStackView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
|
mainStackView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
|
||||||
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 }
|
||||||
|
|||||||
@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,17 +67,10 @@ open class LabelBase<ModelType: LabelModel>: UILabel, ModelHandlerable, ViewProt
|
|||||||
super.init(frame: .zero)
|
super.init(frame: .zero)
|
||||||
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)
|
||||||
|
|||||||
@ -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,8 +24,25 @@ 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
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -67,61 +84,42 @@ open class RadioBoxBase<ModelType: RadioBoxModel>: Control<ModelType> {
|
|||||||
$0.translatesAutoresizingMaskIntoConstraints = false
|
$0.translatesAutoresizingMaskIntoConstraints = false
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
//can't bind to @Proxy
|
|
||||||
open override var isSelected: Bool {
|
|
||||||
get { model.selected }
|
|
||||||
set {
|
|
||||||
if model.selected != newValue {
|
|
||||||
model.selected = newValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Proxy(\.model.text)
|
open var text: String = "Default Text" { didSet { subject.send() }}
|
||||||
open var text: String
|
|
||||||
|
|
||||||
@Proxy(\.model.subText)
|
open var textAttributes: [any LabelAttributeModel]? { didSet { subject.send() }}
|
||||||
open var subText: String?
|
|
||||||
|
open var subText: String? { didSet { subject.send() }}
|
||||||
|
|
||||||
@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?
|
|
||||||
|
open var inputId: String? { didSet { subject.send() }}
|
||||||
|
|
||||||
|
open var value: AnyHashable? { didSet { subject.send() }}
|
||||||
|
|
||||||
@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?
|
|
||||||
|
|
||||||
//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 }
|
||||||
|
|||||||
@ -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,8 +18,21 @@ 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
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -68,22 +81,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
|
||||||
|
|||||||
@ -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
|
||||||
@ -73,61 +100,40 @@ open class RadioButtonBase<ModelType: RadioButtonModel>: Control<ModelType> {
|
|||||||
$0.translatesAutoresizingMaskIntoConstraints = false
|
$0.translatesAutoresizingMaskIntoConstraints = false
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
//can't bind to @Proxy
|
|
||||||
open override var isSelected: Bool {
|
|
||||||
get { model.selected }
|
|
||||||
set {
|
|
||||||
if model.selected != newValue {
|
|
||||||
model.selected = newValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Proxy(\.model.labelText)
|
open var labelText: String? { didSet { subject.send() }}
|
||||||
open var labelText: String?
|
|
||||||
|
|
||||||
@Proxy(\.model.childText)
|
open var labelTextAttributes: [any LabelAttributeModel]? { didSet { subject.send() }}
|
||||||
open var childText: String?
|
|
||||||
|
open var childText: String? { didSet { subject.send() }}
|
||||||
|
|
||||||
@Proxy(\.model.showError)
|
open var childTextAttributes: [any LabelAttributeModel]? { didSet { subject.send() }}
|
||||||
open var showError: Bool
|
|
||||||
|
|
||||||
@Proxy(\.model.errorText)
|
|
||||||
open var errorText: String?
|
|
||||||
|
|
||||||
@Proxy(\.model.inputId)
|
open var showError: Bool = false { didSet { subject.send() }}
|
||||||
open var inputId: String?
|
|
||||||
|
open var errorText: String? { didSet { subject.send() }}
|
||||||
|
|
||||||
@Proxy(\.model.value)
|
open var inputId: String? { didSet { subject.send() }}
|
||||||
open var value: AnyHashable?
|
|
||||||
|
|
||||||
@Proxy(\.model.dataAnalyticsTrack)
|
|
||||||
open var dataAnalyticsTrack: String?
|
|
||||||
|
|
||||||
@Proxy(\.model.dataClickStream)
|
open var value: AnyHashable? { didSet { subject.send() }}
|
||||||
open var dataClickStream: String?
|
|
||||||
|
open var dataAnalyticsTrack: String? { didSet { subject.send() }}
|
||||||
|
|
||||||
@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?
|
|
||||||
|
open var accessibilityHintEnabled: String? { didSet { subject.send() }}
|
||||||
|
|
||||||
@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
|
||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,22 +79,6 @@ public class RadioButtonGroupBase<GroupModelType: RadioButtonGroupModel, ModelHa
|
|||||||
mainStackView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
|
mainStackView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
|
||||||
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
|
||||||
|
|||||||
@ -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,8 +21,26 @@ 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
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -38,55 +56,38 @@ open class RadioSwatchBase<ModelType: RadioSwatchModel>: Control<ModelType> {
|
|||||||
$0.contentMode = .scaleAspectFit
|
$0.contentMode = .scaleAspectFit
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
open var fillImage: UIImage? { didSet { subject.send() }}
|
||||||
|
|
||||||
|
open var text: String = "" { didSet { subject.send() }}
|
||||||
|
|
||||||
|
open var primaryColor: UIColor? { didSet { subject.send() }}
|
||||||
|
|
||||||
|
open var secondaryColor: UIColor? { didSet { subject.send() }}
|
||||||
|
|
||||||
|
open var strikethrough: Bool = false { didSet { subject.send() }}
|
||||||
|
|
||||||
//can't bind to @Proxy
|
open var inputId: String? { 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
|
|
||||||
|
|
||||||
@Proxy(\.model.strikethrough)
|
|
||||||
open var strikethrough: Bool
|
|
||||||
|
|
||||||
@Proxy(\.model.inputId)
|
open var value: AnyHashable? { didSet { subject.send() }}
|
||||||
open var inputId: String?
|
|
||||||
|
open var dataAnalyticsTrack: String? { didSet { subject.send() }}
|
||||||
|
|
||||||
@Proxy(\.model.value)
|
open var dataClickStream: String? { didSet { subject.send() }}
|
||||||
open var value: AnyHashable?
|
|
||||||
|
|
||||||
@Proxy(\.model.dataAnalyticsTrack)
|
|
||||||
open var dataAnalyticsTrack: String?
|
|
||||||
|
|
||||||
@Proxy(\.model.dataClickStream)
|
open var dataTrack: String? { didSet { subject.send() }}
|
||||||
open var dataClickStream: String?
|
|
||||||
|
|
||||||
@Proxy(\.model.dataTrack)
|
open var accessibilityHintEnabled: String? { didSet { subject.send() }}
|
||||||
open var dataTrack: String?
|
|
||||||
|
|
||||||
@Proxy(\.model.accessibilityHintEnabled)
|
|
||||||
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?
|
|
||||||
|
|
||||||
//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 }
|
||||||
|
|||||||
@ -9,29 +9,31 @@ 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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Private Properties
|
// MARK: - Private Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -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,31 +130,23 @@ 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
|
|
||||||
public func updateSelectors(){
|
|
||||||
let selectors = model.selectors.compactMap { existing in
|
|
||||||
return existing.copyWith {
|
|
||||||
$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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
private func updateSelectors() {
|
||||||
|
for selector in selectorViews {
|
||||||
|
selector.surface = surface
|
||||||
|
selector.disabled = disabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - UICollectionViewDelegateFlowLayout
|
// MARK: - UICollectionViewDelegateFlowLayout
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
|
||||||
label.set(with: viewModel.labelModel)
|
|
||||||
|
|
||||||
|
stackView.spacing = showText ? 12 : 0
|
||||||
|
|
||||||
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()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user