updated toggle
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
parent
74cbd6a2fe
commit
6c0a0460e8
@ -8,6 +8,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import UIKit
|
import UIKit
|
||||||
import VDSColorTokens
|
import VDSColorTokens
|
||||||
|
import Combine
|
||||||
/**
|
/**
|
||||||
A custom implementation of Apple's UISwitch.
|
A custom implementation of Apple's UISwitch.
|
||||||
|
|
||||||
@ -16,60 +17,53 @@ import VDSColorTokens
|
|||||||
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: Control<VDSToggleModel>, Changable {
|
|
||||||
|
public class DefaultToggleModel: DefaultLabelModel, VDSToggleModel, ObservableObject {
|
||||||
|
public var id: String?
|
||||||
|
public var inputId: String?
|
||||||
|
public var disabled: Bool = false
|
||||||
|
public var showText: Bool = false
|
||||||
|
public var on: Bool = false
|
||||||
|
public var offText: String = "Off"
|
||||||
|
public var onText: String = "On"
|
||||||
|
public var value: AnyHashable? = true
|
||||||
|
public var dataAnalyticsTrack: String?
|
||||||
|
public var dataClickStream: String?
|
||||||
|
public var dataTrack: String?
|
||||||
|
public var accessibilityHintEnabled: String?
|
||||||
|
public var accessibilityHintDisabled: String?
|
||||||
|
public var accessibilityValueEnabled: String?
|
||||||
|
public var accessibilityValueDisabled: String?
|
||||||
|
public var accessibilityLabelEnabled: String?
|
||||||
|
public var accessibilityLabelDisabled: String?
|
||||||
|
|
||||||
|
public required init() {
|
||||||
|
super.init()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objcMembers open class VDSToggle: VDSControl, Modelable, Changable {
|
||||||
|
|
||||||
|
public typealias ModelType = VDSToggleModel
|
||||||
|
@Published public var model: ModelType = DefaultToggleModel()
|
||||||
|
private var cancellable: AnyCancellable?
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Properties
|
// MARK: - Private Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
/// Holds the on and off colors for the container.
|
/// Holds the on and off colors for the container.
|
||||||
public var containerTintColor: (on: UIColor, off: UIColor) = (on: VDSColor.paletteGreen26, off: VDSColor.paletteGray44)
|
private var containerTintColor: (on: UIColor, off: UIColor) = (on: VDSColor.paletteGreen26, off: VDSColor.paletteGray44)
|
||||||
|
|
||||||
/// Holds the on and off colors for the knob.
|
/// Holds the on and off colors for the knob.
|
||||||
public var knobTintColor: (on: UIColor, off: UIColor) = (on: VDSColor.paletteWhite, off: VDSColor.paletteWhite)
|
private var knobTintColor: (on: UIColor, off: UIColor) = (on: VDSColor.paletteWhite, off: VDSColor.paletteWhite)
|
||||||
|
|
||||||
/// Holds the on and off colors for the disabled state..
|
/// Holds the on and off colors for the disabled state..
|
||||||
public var disabledTintColor: (container: UIColor, knob: UIColor) = (container: VDSColor.paletteGray11, knob: VDSColor.paletteWhite)
|
private var disabledTintColor: (container: UIColor, knob: UIColor) = (container: VDSColor.paletteGray11, knob: VDSColor.paletteWhite)
|
||||||
|
|
||||||
/// Set this flag to false if you do not want to animate state changes.
|
|
||||||
public var isAnimated = true
|
|
||||||
|
|
||||||
public var onChange: Blocks.ActionBlock?
|
|
||||||
|
|
||||||
private var showText: Bool {
|
|
||||||
return model?.showText ?? false
|
|
||||||
}
|
|
||||||
|
|
||||||
private var onText: String {
|
|
||||||
return model?.onText ?? "On"
|
|
||||||
}
|
|
||||||
|
|
||||||
private var offText: String {
|
|
||||||
return model?.offText ?? "off"
|
|
||||||
}
|
|
||||||
|
|
||||||
private var showTextSpacing: CGFloat {
|
private var showTextSpacing: CGFloat {
|
||||||
showText ? 12 : 0
|
showText ? 12 : 0
|
||||||
}
|
}
|
||||||
|
|
||||||
private var textPosition: VDSTextPosition {
|
|
||||||
return model?.textPosition ?? .left
|
|
||||||
}
|
|
||||||
|
|
||||||
private var fontSize: VDSFontSize {
|
|
||||||
return model?.fontSize ?? .small
|
|
||||||
}
|
|
||||||
|
|
||||||
private var fontWeight: VDSFontWeight {
|
|
||||||
return model?.fontWeight ?? .regular
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sizes are from InVision design specs.
|
|
||||||
public static var toggleSize = CGSize(width: 52, height: 24)
|
|
||||||
open class func getToggleScaledSize() -> CGSize { return Self.toggleSize }
|
|
||||||
|
|
||||||
public static var knobSize = CGSize(width: 20, height: 20)
|
|
||||||
open class func getKnobScaledSize() -> CGSize { return Self.knobSize }
|
|
||||||
|
|
||||||
private var stackView: UIStackView = {
|
private var stackView: UIStackView = {
|
||||||
let stackView = UIStackView()
|
let stackView = UIStackView()
|
||||||
stackView.translatesAutoresizingMaskIntoConstraints = false
|
stackView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
@ -78,8 +72,8 @@ import VDSColorTokens
|
|||||||
return stackView
|
return stackView
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private var label: UILabel = {
|
private var label: VDSLabel = {
|
||||||
let label = UILabel()
|
let label = VDSLabel()
|
||||||
label.translatesAutoresizingMaskIntoConstraints = false
|
label.translatesAutoresizingMaskIntoConstraints = false
|
||||||
return label
|
return label
|
||||||
}()
|
}()
|
||||||
@ -97,16 +91,105 @@ import VDSColorTokens
|
|||||||
return view
|
return view
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Public Properties
|
||||||
|
//--------------------------------------------------
|
||||||
|
/// Set this flag to false if you do not want to animate state changes.
|
||||||
|
public var isAnimated = true
|
||||||
|
|
||||||
|
public var onChange: Blocks.ActionBlock?
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Static Properties
|
||||||
|
//--------------------------------------------------
|
||||||
|
// Sizes are from InVision design specs.
|
||||||
|
public static var toggleSize = CGSize(width: 52, height: 24)
|
||||||
|
open class func getToggleScaledSize() -> CGSize { return Self.toggleSize }
|
||||||
|
|
||||||
|
public static var knobSize = CGSize(width: 20, height: 20)
|
||||||
|
open class func getKnobScaledSize() -> CGSize { return Self.knobSize }
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Computed Properties
|
// MARK: - Computed Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
public var showText: Bool {
|
||||||
|
get { model.showText }
|
||||||
|
set {
|
||||||
|
if model.showText != newValue {
|
||||||
|
model.showText = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var onText: String {
|
||||||
|
get { model.onText }
|
||||||
|
set {
|
||||||
|
if model.onText != newValue {
|
||||||
|
model.onText = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var offText: String {
|
||||||
|
get { model.offText }
|
||||||
|
set {
|
||||||
|
if model.offText != newValue {
|
||||||
|
model.offText = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var textPosition: VDSTextPosition {
|
||||||
|
get { model.textPosition }
|
||||||
|
set {
|
||||||
|
if model.textPosition != newValue {
|
||||||
|
model.textPosition = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var fontSize: VDSFontSize {
|
||||||
|
get { model.fontSize }
|
||||||
|
set {
|
||||||
|
if model.fontSize != newValue {
|
||||||
|
model.fontSize = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var fontWeight: VDSFontWeight {
|
||||||
|
get { model.fontWeight }
|
||||||
|
set {
|
||||||
|
if model.fontWeight != newValue {
|
||||||
|
model.fontWeight = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var surface: Surface {
|
||||||
|
get { model.surface }
|
||||||
|
set {
|
||||||
|
if model.surface != newValue {
|
||||||
|
model.surface = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
open override var isEnabled: Bool {
|
open override var isEnabled: Bool {
|
||||||
didSet {
|
get { !model.disabled }
|
||||||
isUserInteractionEnabled = isEnabled
|
set {
|
||||||
changeStateNoAnimation(isEnabled ? isOn : false)
|
//create local vars for clear coding
|
||||||
|
let enabled = newValue
|
||||||
|
let disabled = !newValue
|
||||||
|
if model.disabled != disabled {
|
||||||
|
model.disabled = disabled
|
||||||
|
}
|
||||||
|
|
||||||
|
isUserInteractionEnabled = enabled
|
||||||
|
changeStateNoAnimation(enabled ? isOn : false)
|
||||||
setToggleAppearanceFromState()
|
setToggleAppearanceFromState()
|
||||||
setAccessibilityHint(isEnabled)
|
setAccessibilityHint(enabled)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,37 +199,37 @@ import VDSColorTokens
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The state on the toggle. Default value: false.
|
/// The state on the toggle. Default value: false.
|
||||||
open var isOn: Bool = false {
|
open var isOn: Bool {
|
||||||
didSet {
|
get { model.on }
|
||||||
label.text = isOn ? onText : offText
|
set {
|
||||||
|
if model.on != newValue {
|
||||||
if isAnimated {
|
model.on = newValue
|
||||||
UIView.animate(withDuration: 0.2, delay: 0.0, options: .curveEaseIn, animations: {
|
setAccessibilityValue(model.on)
|
||||||
if self.isOn {
|
if isAnimated {
|
||||||
self.knobView.backgroundColor = self.knobTintColor.on
|
UIView.animate(withDuration: 0.2, delay: 0.0, options: .curveEaseIn, animations: {
|
||||||
self.toggleView.backgroundColor = self.containerTintColor.on
|
if newValue {
|
||||||
|
self.knobView.backgroundColor = self.knobTintColor.on
|
||||||
} else {
|
self.toggleView.backgroundColor = self.containerTintColor.on
|
||||||
self.knobView.backgroundColor = self.knobTintColor.off
|
|
||||||
self.toggleView.backgroundColor = self.containerTintColor.off
|
} else {
|
||||||
}
|
self.knobView.backgroundColor = self.knobTintColor.off
|
||||||
}, completion: nil)
|
self.toggleView.backgroundColor = self.containerTintColor.off
|
||||||
|
}
|
||||||
UIView.animate(withDuration: 0.33, delay: 0, usingSpringWithDamping: 0.6, initialSpringVelocity: 0.2, options: [], animations: {
|
}, completion: nil)
|
||||||
|
|
||||||
|
UIView.animate(withDuration: 0.33, delay: 0, usingSpringWithDamping: 0.6, initialSpringVelocity: 0.2, options: [], animations: {
|
||||||
|
self.constrainKnob()
|
||||||
|
self.knobWidthConstraint?.constant = Self.getKnobScaledSize().width
|
||||||
|
self.layoutIfNeeded()
|
||||||
|
}, completion: nil)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
setToggleAppearanceFromState()
|
||||||
self.constrainKnob()
|
self.constrainKnob()
|
||||||
self.knobWidthConstraint?.constant = Self.getKnobScaledSize().width
|
}
|
||||||
self.layoutIfNeeded()
|
setNeedsLayout()
|
||||||
}, completion: nil)
|
layoutIfNeeded()
|
||||||
|
|
||||||
} else {
|
|
||||||
setToggleAppearanceFromState()
|
|
||||||
self.constrainKnob()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
model?.on = isOn
|
|
||||||
setAccessibilityValue(isOn)
|
|
||||||
setNeedsLayout()
|
|
||||||
layoutIfNeeded()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,12 +276,21 @@ import VDSColorTokens
|
|||||||
|
|
||||||
public override init(frame: CGRect) {
|
public override init(frame: CGRect) {
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
|
setup()
|
||||||
}
|
}
|
||||||
|
|
||||||
public convenience override init() {
|
public convenience override init() {
|
||||||
self.init(frame: .zero)
|
self.init(frame: .zero)
|
||||||
|
setup()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setup() {
|
||||||
|
cancellable = $model.sink { [weak self] viewModel in
|
||||||
|
self?.onStateChange(viewModel: viewModel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//functions
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Lifecycle
|
// MARK: - Lifecycle
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -392,19 +484,19 @@ import VDSColorTokens
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK:- MoleculeViewProtocol
|
|
||||||
open override func set(with model: ModelType) {
|
private func onStateChange(viewModel: ModelType) {
|
||||||
self.model = model
|
|
||||||
isOn = model.on
|
|
||||||
changeStateNoAnimation(isOn)
|
|
||||||
isAnimated = true
|
isAnimated = true
|
||||||
isEnabled = !model.disabled
|
isOn = viewModel.on
|
||||||
|
isEnabled = !viewModel.disabled
|
||||||
guard let font = try? VDSFontStyle.font(for: .body, fontWeight: model.fontWeight, fontSize: model.fontSize) else {
|
changeStateNoAnimation(viewModel.on)
|
||||||
return
|
backgroundColor = viewModel.surface == .dark ? VDSColor.backgroundPrimaryDark : .clear
|
||||||
}
|
label.set(with: viewModel)
|
||||||
|
label.text = viewModel.on ? viewModel.onText : viewModel.offText
|
||||||
label.font = font
|
}
|
||||||
|
|
||||||
|
// MARK:- Modable
|
||||||
|
open func set(with model: ModelType) {
|
||||||
|
self.model = model
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import UIKit
|
import UIKit
|
||||||
import VDSTypographyTokens
|
|
||||||
|
|
||||||
extension VDSToggle {
|
extension VDSToggle {
|
||||||
public enum TextPosition: String, Codable {
|
public enum TextPosition: String, Codable {
|
||||||
@ -15,13 +14,17 @@ extension VDSToggle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public protocol VDSToggleModel: Surfaceable, FormFieldable, DataTrackable, Disabling, Accessable {
|
public protocol VDSToggleModel: VDSLabelModel, FormFieldable, DataTrackable, Disabling, Accessable {
|
||||||
var id: String? { get set }
|
var id: String? { get set }
|
||||||
var showText: Bool { get set }
|
var showText: Bool { get set }
|
||||||
var on: Bool { get set }
|
var on: Bool { get set }
|
||||||
var fontSize: VDSFontSize { get set }
|
|
||||||
var textPosition: VDSTextPosition { get set }
|
|
||||||
var fontWeight: VDSFontWeight { get set }
|
|
||||||
var offText: String { get set }
|
var offText: String { get set }
|
||||||
var onText: String { get set }
|
var onText: String { get set }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension VDSToggleModel {
|
||||||
|
public var fontCategory: VDSFontCategory {
|
||||||
|
get { return .body }
|
||||||
|
set { return }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user