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:
parent
482d3854a1
commit
49e84c5255
@ -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() {
|
||||||
|
|||||||
@ -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()
|
||||||
|
|||||||
@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user