updated control to take in generic class

updated toggle to include this and make a abstract class and new subclass

Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
Matt Bruce 2022-07-30 10:33:23 -05:00
parent 482d3854a1
commit 49e84c5255
4 changed files with 51 additions and 58 deletions

View File

@ -7,8 +7,21 @@
import Foundation import Foundation
import UIKit import UIKit
import Combine
open class VDSControl: UIControl, ViewProtocol { open class VDSControl<ModelType>: UIControl, Modelable, ViewProtocol {
@Published public var model: ModelType
private var cancellable: AnyCancellable?
open func set(with model: ModelType) {
self.model = model
}
open func onStateChange(viewModel: ModelType) {
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
@ -18,18 +31,14 @@ open class VDSControl: UIControl, ViewProtocol {
// MARK: - Initializers // MARK: - Initializers
//-------------------------------------------------- //--------------------------------------------------
public override init(frame: CGRect) { required public init(with model: ModelType) {
super.init(frame: .zero) self.model = model
initialSetup()
}
public init() {
super.init(frame: .zero) super.init(frame: .zero)
initialSetup() initialSetup()
set(with: model)
} }
public required init?(coder: NSCoder) { public required init?(coder: NSCoder) {
super.init(coder: coder)
fatalError("Control does not support xib.") fatalError("Control does not support xib.")
} }
@ -42,6 +51,9 @@ open class VDSControl: UIControl, ViewProtocol {
initialSetupPerformed = true initialSetupPerformed = true
setupView() setupView()
} }
cancellable = $model.debounce(for: .seconds(ModelStateDebounce), scheduler: RunLoop.main).sink { [weak self] viewModel in
self?.onStateChange(viewModel: viewModel)
}
} }
open func reset() { open func reset() {

View File

@ -10,7 +10,8 @@ import UIKit
import VDSColorTokens import VDSColorTokens
import Combine import Combine
open class VDSLabel: UILabel, Modelable { open class VDSLabel: UILabel, Modelable, Initable {
@Published public var model: VDSLabelModel = DefaultLabelModel() @Published public var model: VDSLabelModel = DefaultLabelModel()
private var cancellable: AnyCancellable? private var cancellable: AnyCancellable?
@ -30,10 +31,16 @@ open class VDSLabel: UILabel, Modelable {
public var surface: Surface public var surface: Surface
//Initializers //Initializers
public convenience init() { required public convenience init() {
self.init(frame: .zero) self.init(frame: .zero)
} }
public required convenience init(with model: VDSLabelModel) {
self.init()
self.model = model
set(with: model)
}
public override init(frame: CGRect) { public override init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
setup() setup()

View File

@ -17,11 +17,22 @@ import Combine
Container: The background of the toggle control. Container: The background of the toggle control.
Knob: The circular indicator that slides on the container. Knob: The circular indicator that slides on the container.
*/ */
@objcMembers open class VDSToggle: VDSControl, Modelable, Changable {
public typealias ModelType = VDSToggleModel final public class VDSToggle: VDSToggleBase<DefaultToggleModel>{
@Published public var model: ModelType = DefaultToggleModel() public convenience init() {
private var cancellable: AnyCancellable? self.init(with: DefaultToggleModel())
}
required public init(with model: ModelType) {
super.init(with: model)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
@objcMembers open class VDSToggleBase<ModelType: VDSToggleModel>: VDSControl<ModelType>, Changable {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Private Properties // MARK: - Private Properties
@ -76,8 +87,8 @@ import Combine
// MARK: - Static Properties // MARK: - Static Properties
//-------------------------------------------------- //--------------------------------------------------
// Sizes are from InVision design specs. // Sizes are from InVision design specs.
public static var toggleSize = CGSize(width: 52, height: 24) public let toggleSize = CGSize(width: 52, height: 24)
public static var knobSize = CGSize(width: 20, height: 20) public let knobSize = CGSize(width: 20, height: 20)
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Computed Properties // MARK: - Computed Properties
@ -103,7 +114,7 @@ import Combine
@Proxy(\.model.surface) @Proxy(\.model.surface)
public var surface: Surface public var surface: Surface
@Proxy(\VDSToggle.model.on) @Proxy(\.model.on)
open var isOn: Bool open var isOn: Bool
open override var isEnabled: Bool { open override var isEnabled: Bool {
@ -128,29 +139,6 @@ import Combine
private var toggleHeightConstraint: NSLayoutConstraint? private var toggleHeightConstraint: NSLayoutConstraint?
private var toggleWidthConstraint: NSLayoutConstraint? private var toggleWidthConstraint: NSLayoutConstraint?
//--------------------------------------------------
// MARK: - Initializers
//--------------------------------------------------
public required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
public convenience override init() {
self.init(frame: .zero)
setup()
}
func setup() {
cancellable = $model.debounce(for: .seconds(ModelStateDebounce), scheduler: RunLoop.main).sink { [weak self] viewModel in
self?.onStateChange(viewModel: viewModel)
}
}
//functions //functions
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Lifecycle // MARK: - Lifecycle
@ -159,9 +147,6 @@ import Combine
public override func updateView(_ size: CGFloat) { public override func updateView(_ size: CGFloat) {
super.updateView(size) super.updateView(size)
let toggleSize = Self.toggleSize
let knobSize = Self.knobSize
toggleHeightConstraint?.constant = toggleSize.height toggleHeightConstraint?.constant = toggleSize.height
toggleWidthConstraint?.constant = toggleSize.width toggleWidthConstraint?.constant = toggleSize.width
@ -185,9 +170,6 @@ import Combine
addSubview(stackView) addSubview(stackView)
let toggleSize = Self.toggleSize
let knobSize = Self.knobSize
toggleHeightConstraint = toggleView.heightAnchor.constraint(equalToConstant: toggleSize.height) toggleHeightConstraint = toggleView.heightAnchor.constraint(equalToConstant: toggleSize.height)
toggleHeightConstraint?.isActive = true toggleHeightConstraint?.isActive = true
@ -209,10 +191,6 @@ import Combine
knobView.topAnchor.constraint(greaterThanOrEqualTo: toggleView.topAnchor).isActive = true knobView.topAnchor.constraint(greaterThanOrEqualTo: toggleView.topAnchor).isActive = true
toggleView.bottomAnchor.constraint(greaterThanOrEqualTo: knobView.bottomAnchor).isActive = true toggleView.bottomAnchor.constraint(greaterThanOrEqualTo: knobView.bottomAnchor).isActive = true
//setup stackview
if showText {
stackView.addArrangedSubview(label)
}
ensureLabel() ensureLabel()
stackView.addArrangedSubview(toggleView) stackView.addArrangedSubview(toggleView)
stackView.topAnchor.constraint(equalTo: topAnchor).isActive = true stackView.topAnchor.constraint(equalTo: topAnchor).isActive = true
@ -310,14 +288,14 @@ import Combine
public func knobReformAnimation() { public func knobReformAnimation() {
UIView.animate(withDuration: 0.1, animations: { UIView.animate(withDuration: 0.1, animations: {
self.knobWidthConstraint?.constant = Self.knobSize.width self.knobWidthConstraint?.constant = self.knobSize.width
self.layoutIfNeeded() self.layoutIfNeeded()
}, completion: nil) }, completion: nil)
} }
/// Follow the SwiftUI View paradigm /// Follow the SwiftUI View paradigm
/// - Parameter viewModel: state /// - Parameter viewModel: state
private func onStateChange(viewModel: ModelType) { open override func onStateChange(viewModel: ModelType) {
let enabled = !viewModel.disabled let enabled = !viewModel.disabled
label.set(with: viewModel) label.set(with: viewModel)
@ -339,7 +317,7 @@ import Combine
} }
self.knobTrailingConstraint?.isActive = true self.knobTrailingConstraint?.isActive = true
self.knobLeadingConstraint?.isActive = true self.knobLeadingConstraint?.isActive = true
self.knobWidthConstraint?.constant = Self.knobSize.width self.knobWidthConstraint?.constant = self.knobSize.width
self.layoutIfNeeded() self.layoutIfNeeded()
} }
@ -374,9 +352,4 @@ import Combine
setNeedsLayout() setNeedsLayout()
layoutIfNeeded() layoutIfNeeded()
} }
// MARK:- Modable
open func set(with model: ModelType) {
self.model = model
}
} }

View File

@ -12,5 +12,6 @@ public let ModelStateDebounce = 0.02
public protocol Modelable { public protocol Modelable {
associatedtype ModelType associatedtype ModelType
var model: ModelType { get set } var model: ModelType { get set }
init(with model: ModelType)
func set(with model: ModelType) func set(with model: ModelType)
} }