updated checkbox
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
parent
ef5cb2ae84
commit
d8a55d87bc
@ -26,18 +26,29 @@ import Combine
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Private Properties
|
// MARK: - Private Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
private func getCheckboxTintColor(for disabled: Bool, surface: Surface) -> (on: UIColor, off: UIColor) {
|
private func getCheckboxBackgroundColor(for disabled: Bool, surface: Surface) -> (on: UIColor, off: UIColor) {
|
||||||
if disabled {
|
if disabled {
|
||||||
if surface == .light {
|
if surface == .light {
|
||||||
return (on: VDSColor.interactiveDisabledOnlight, off: UIColor.clear)
|
return (
|
||||||
|
on: VDSColor.interactiveDisabledOnlight,
|
||||||
|
off: .clear)
|
||||||
} else {
|
} else {
|
||||||
return (on: VDSColor.interactiveDisabledOndark, off: UIColor.clear)
|
return (
|
||||||
|
on: VDSColor.interactiveDisabledOndark,
|
||||||
|
off: .clear
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if surface == .light {
|
if surface == .light {
|
||||||
return (on: VDSColor.elementsPrimaryOnlight, off: UIColor.clear)
|
return (
|
||||||
|
on: VDSColor.elementsPrimaryOnlight,
|
||||||
|
off: .clear
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
return (on: VDSColor.elementsPrimaryOndark, off: UIColor.clear)
|
return (
|
||||||
|
on: VDSColor.elementsPrimaryOndark,
|
||||||
|
off: .clear
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -45,15 +56,27 @@ import Combine
|
|||||||
private func getCheckboxBorderColor(for disabled: Bool, surface: Surface) -> (on: UIColor, off: UIColor) {
|
private func getCheckboxBorderColor(for disabled: Bool, surface: Surface) -> (on: UIColor, off: UIColor) {
|
||||||
if disabled {
|
if disabled {
|
||||||
if surface == .light {
|
if surface == .light {
|
||||||
return (on: VDSColor.interactiveDisabledOnlight, off: VDSColor.interactiveDisabledOnlight)
|
return (
|
||||||
|
on: VDSColor.interactiveDisabledOnlight,
|
||||||
|
off: VDSColor.interactiveDisabledOnlight
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
return (on: VDSColor.interactiveDisabledOnlight, off: VDSColor.interactiveDisabledOnlight)
|
return (
|
||||||
|
on: VDSColor.interactiveDisabledOndark,
|
||||||
|
off: VDSColor.interactiveDisabledOnlight
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if surface == .light {
|
if surface == .light {
|
||||||
return (on: VDSColor.elementsPrimaryOndark, off: VDSFormControlsColor.borderOnlight)
|
return (
|
||||||
|
on: VDSColor.elementsPrimaryOnlight,
|
||||||
|
off: VDSFormControlsColor.borderOnlight
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
return (on: VDSColor.elementsPrimaryOndark, off: VDSFormControlsColor.borderOndark)
|
return (
|
||||||
|
on: VDSColor.elementsPrimaryOndark,
|
||||||
|
off: VDSFormControlsColor.borderOndark
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -61,7 +84,7 @@ import Combine
|
|||||||
private func getCheckboxCheckColor(for disabled: Bool, surface: Surface) -> UIColor {
|
private func getCheckboxCheckColor(for disabled: Bool, surface: Surface) -> UIColor {
|
||||||
if disabled {
|
if disabled {
|
||||||
if surface == .light {
|
if surface == .light {
|
||||||
return VDSColor.interactiveDisabledOnlight
|
return VDSColor.interactiveDisabledOndark
|
||||||
} else {
|
} else {
|
||||||
return VDSColor.interactiveDisabledOnlight
|
return VDSColor.interactiveDisabledOnlight
|
||||||
}
|
}
|
||||||
@ -69,7 +92,7 @@ import Combine
|
|||||||
if surface == .light {
|
if surface == .light {
|
||||||
return VDSColor.elementsPrimaryOndark
|
return VDSColor.elementsPrimaryOndark
|
||||||
} else {
|
} else {
|
||||||
return VDSColor.elementsPrimaryOndark
|
return VDSColor.elementsPrimaryOnlight
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -78,7 +101,7 @@ import Combine
|
|||||||
let stackView = UIStackView()
|
let stackView = UIStackView()
|
||||||
stackView.translatesAutoresizingMaskIntoConstraints = false
|
stackView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
stackView.axis = .vertical
|
stackView.axis = .vertical
|
||||||
stackView.distribution = .fillProportionally
|
stackView.distribution = .fill
|
||||||
return stackView
|
return stackView
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -117,8 +140,8 @@ import Combine
|
|||||||
return label
|
return label
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private var checkboxView: UIView = {
|
private var checkboxView: CheckBoxView = {
|
||||||
let view = UIView()
|
let view = CheckBoxView()
|
||||||
view.translatesAutoresizingMaskIntoConstraints = false
|
view.translatesAutoresizingMaskIntoConstraints = false
|
||||||
return view
|
return view
|
||||||
}()
|
}()
|
||||||
@ -132,7 +155,7 @@ import Combine
|
|||||||
// MARK: - Static Properties
|
// MARK: - Static Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// Sizes are from InVision design specs.
|
// Sizes are from InVision design specs.
|
||||||
public let checkboxSize = CGSize(width: 52, height: 24)
|
public let checkboxSize = CGSize(width: 20, height: 20)
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Computed Properties
|
// MARK: - Computed Properties
|
||||||
@ -161,7 +184,7 @@ import Combine
|
|||||||
@Proxy(\.model.surface)
|
@Proxy(\.model.surface)
|
||||||
public var surface: Surface
|
public var surface: Surface
|
||||||
|
|
||||||
@Proxy(\.model.selected)
|
@Proxy(\.model.on)
|
||||||
open var isOn: Bool
|
open var isOn: Bool
|
||||||
|
|
||||||
@Proxy(\.model.disabled)
|
@Proxy(\.model.disabled)
|
||||||
@ -242,61 +265,20 @@ import Combine
|
|||||||
mainStackView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
|
mainStackView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Manages the appearance of the checkbox.
|
|
||||||
private var shapeLayer: CAShapeLayer?
|
|
||||||
|
|
||||||
/// Creates the check mark layer.
|
/// Creates the check mark layer.
|
||||||
private func setCheckboxColor(viewModel: ModelType) {
|
private func setCheckboxColor(viewModel: ModelType) {
|
||||||
|
|
||||||
let background = getCheckboxTintColor(for: viewModel.disabled, surface: viewModel.surface)
|
let background = getCheckboxBackgroundColor(for: viewModel.disabled, surface: viewModel.surface)
|
||||||
let border = getCheckboxBorderColor(for: viewModel.disabled, surface: viewModel.surface)
|
let border = getCheckboxBorderColor(for: viewModel.disabled, surface: viewModel.surface)
|
||||||
let checkColor = getCheckboxCheckColor(for: viewModel.disabled, surface: viewModel.surface)
|
let checkColor = getCheckboxCheckColor(for: viewModel.disabled, surface: viewModel.surface)
|
||||||
|
|
||||||
checkboxView.backgroundColor = viewModel.disabled ? background.off : background.on
|
checkboxView.backgroundColor = viewModel.on ? background.on : background.off
|
||||||
checkboxView.layer.borderColor = (viewModel.disabled ? border.off : border.on).cgColor
|
checkboxView.borderColor = viewModel.on ? border.on : border.off
|
||||||
|
checkboxView.checkColor = checkColor
|
||||||
|
checkboxView.isSelected = viewModel.on
|
||||||
|
}
|
||||||
|
|
||||||
if shapeLayer == nil {
|
|
||||||
|
|
||||||
let shapeLayer = CAShapeLayer()
|
|
||||||
self.shapeLayer = shapeLayer
|
|
||||||
shapeLayer.frame = bounds
|
|
||||||
checkboxView.layer.addSublayer(shapeLayer)
|
|
||||||
shapeLayer.strokeColor = checkColor.cgColor
|
|
||||||
shapeLayer.fillColor = UIColor.clear.cgColor
|
|
||||||
shapeLayer.path = checkMarkPath()
|
|
||||||
shapeLayer.lineJoin = .miter
|
|
||||||
shapeLayer.lineWidth = 2
|
|
||||||
|
|
||||||
CATransaction.withDisabledAnimations {
|
|
||||||
shapeLayer.strokeEnd = viewModel.selected ? 1 : 0
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
shapeLayer?.strokeColor = checkColor.cgColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// - returns: The CGPath of a UIBezierPath detailing the path of a checkmark
|
|
||||||
func checkMarkPath() -> CGPath {
|
|
||||||
|
|
||||||
let length = max(bounds.size.height, bounds.size.width)
|
|
||||||
let xInsetLeft = length * 0.25
|
|
||||||
let yInsetTop = length * 0.3
|
|
||||||
let innerWidth = length - (xInsetLeft + length * 0.25) // + Right X Inset
|
|
||||||
let innerHeight = length - (yInsetTop + length * 0.35) // + Bottom Y Inset
|
|
||||||
|
|
||||||
let startPoint = CGPoint(x: xInsetLeft, y: yInsetTop + (innerHeight / 2))
|
|
||||||
let pivotOffSet = CGPoint(x: xInsetLeft + (innerWidth * 0.33), y: yInsetTop + innerHeight)
|
|
||||||
let endOffset = CGPoint(x: xInsetLeft + innerWidth, y: yInsetTop)
|
|
||||||
|
|
||||||
let bezierPath = UIBezierPath()
|
|
||||||
bezierPath.move(to: startPoint)
|
|
||||||
bezierPath.addLine(to: pivotOffSet)
|
|
||||||
bezierPath.addLine(to: endOffset)
|
|
||||||
|
|
||||||
return bezierPath.cgPath
|
|
||||||
}
|
|
||||||
|
|
||||||
func ensureLabel(viewModel: ModelType) {
|
func ensureLabel(viewModel: ModelType) {
|
||||||
|
|
||||||
//deal with labels
|
//deal with labels
|
||||||
@ -308,8 +290,8 @@ import Combine
|
|||||||
//top label
|
//top label
|
||||||
if let labelModel = viewModel.labelModel {
|
if let labelModel = viewModel.labelModel {
|
||||||
primaryLabel.set(with: labelModel)
|
primaryLabel.set(with: labelModel)
|
||||||
if checkBoxStackView.subviews.contains(primaryLabel) == false {
|
if checkboxLabelStackView.subviews.contains(primaryLabel) == false {
|
||||||
checkBoxStackView.insertArrangedSubview(primaryLabel, at: 0)
|
checkboxLabelStackView.insertArrangedSubview(primaryLabel, at: 0)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
primaryLabel.removeFromSuperview()
|
primaryLabel.removeFromSuperview()
|
||||||
@ -317,8 +299,8 @@ import Combine
|
|||||||
//bottom label
|
//bottom label
|
||||||
if let childModel = viewModel.childModel {
|
if let childModel = viewModel.childModel {
|
||||||
secondaryLabel.set(with: childModel)
|
secondaryLabel.set(with: childModel)
|
||||||
if checkBoxStackView.subviews.contains(secondaryLabel) == false {
|
if checkboxLabelStackView.subviews.contains(secondaryLabel) == false {
|
||||||
checkBoxStackView.addArrangedSubview(secondaryLabel)
|
checkboxLabelStackView.addArrangedSubview(secondaryLabel)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
secondaryLabel.removeFromSuperview()
|
secondaryLabel.removeFromSuperview()
|
||||||
@ -328,9 +310,12 @@ import Combine
|
|||||||
}
|
}
|
||||||
|
|
||||||
//either add/remove the error from the main stack
|
//either add/remove the error from the main stack
|
||||||
if model.shouldShowError {
|
if let errorModel = model.errorModel, model.shouldShowError {
|
||||||
mainStackView.spacing = 12
|
errorLabel.set(with: errorModel)
|
||||||
mainStackView.addArrangedSubview(errorLabel)
|
if mainStackView.subviews.contains(errorLabel) == false {
|
||||||
|
mainStackView.spacing = 12
|
||||||
|
mainStackView.addArrangedSubview(errorLabel)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
mainStackView.spacing = 0
|
mainStackView.spacing = 0
|
||||||
errorLabel.removeFromSuperview()
|
errorLabel.removeFromSuperview()
|
||||||
@ -380,10 +365,6 @@ import Combine
|
|||||||
sendActions(for: .touchUpInside)
|
sendActions(for: .touchUpInside)
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
|
|
||||||
sendActions(for: .touchCancel)
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Animations
|
// MARK: - Animations
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -396,11 +377,78 @@ import Combine
|
|||||||
ensureLabel(viewModel: viewModel)
|
ensureLabel(viewModel: viewModel)
|
||||||
setCheckboxColor(viewModel: viewModel)
|
setCheckboxColor(viewModel: viewModel)
|
||||||
setAccessibilityHint(enabled)
|
setAccessibilityHint(enabled)
|
||||||
setAccessibilityValue(viewModel.selected)
|
setAccessibilityValue(viewModel.on)
|
||||||
setAccessibilityLabel(viewModel.selected)
|
setAccessibilityLabel(viewModel.on)
|
||||||
isUserInteractionEnabled = !viewModel.disabled
|
isUserInteractionEnabled = !viewModel.disabled
|
||||||
setNeedsLayout()
|
setNeedsLayout()
|
||||||
layoutIfNeeded()
|
layoutIfNeeded()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class CheckBoxView: UIView {
|
||||||
|
public var borderColor: UIColor = .black
|
||||||
|
public var checkColor: UIColor = .white
|
||||||
|
public var isSelected: Bool = false {
|
||||||
|
didSet {
|
||||||
|
drawShapeLayer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Manages the appearance of the checkbox.
|
||||||
|
private var shapeLayer: CAShapeLayer?
|
||||||
|
|
||||||
|
/// Creates the check mark layer.
|
||||||
|
private func drawShapeLayer() {
|
||||||
|
layer.borderColor = borderColor.cgColor
|
||||||
|
layer.cornerRadius = 2.0
|
||||||
|
shapeLayer?.strokeColor = checkColor.cgColor
|
||||||
|
|
||||||
|
guard let path = try? checkMarkPath() else { return }
|
||||||
|
|
||||||
|
if shapeLayer == nil {
|
||||||
|
let shapeLayer = CAShapeLayer()
|
||||||
|
self.shapeLayer = shapeLayer
|
||||||
|
shapeLayer.frame = bounds
|
||||||
|
layer.addSublayer(shapeLayer)
|
||||||
|
shapeLayer.strokeColor = checkColor.cgColor
|
||||||
|
shapeLayer.fillColor = UIColor.clear.cgColor
|
||||||
|
shapeLayer.path = path
|
||||||
|
shapeLayer.lineJoin = .miter
|
||||||
|
shapeLayer.lineWidth = 2
|
||||||
|
CATransaction.withDisabledAnimations {
|
||||||
|
shapeLayer.strokeEnd = isSelected ? 1 : 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func layoutSubviews() {
|
||||||
|
super.layoutSubviews()
|
||||||
|
drawShapeLayer()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// - returns: The CGPath of a UIBezierPath detailing the path of a checkmark
|
||||||
|
private func checkMarkPath() throws -> CGPath {
|
||||||
|
let length = max(bounds.size.height, bounds.size.width)
|
||||||
|
guard length > 0.0 else { throw Error.boundsNotSet }
|
||||||
|
let xInsetLeft = length * 0.25
|
||||||
|
let yInsetTop = length * 0.3
|
||||||
|
let innerWidth = length - (xInsetLeft + length * 0.25) // + Right X Inset
|
||||||
|
let innerHeight = length - (yInsetTop + length * 0.35) // + Bottom Y Inset
|
||||||
|
|
||||||
|
let startPoint = CGPoint(x: xInsetLeft, y: yInsetTop + (innerHeight / 2))
|
||||||
|
let pivotOffSet = CGPoint(x: xInsetLeft + (innerWidth * 0.33), y: yInsetTop + innerHeight)
|
||||||
|
let endOffset = CGPoint(x: xInsetLeft + innerWidth, y: yInsetTop)
|
||||||
|
|
||||||
|
let bezierPath = UIBezierPath()
|
||||||
|
bezierPath.move(to: startPoint)
|
||||||
|
bezierPath.addLine(to: pivotOffSet)
|
||||||
|
bezierPath.addLine(to: endOffset)
|
||||||
|
|
||||||
|
return bezierPath.cgPath
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Error: Swift.Error {
|
||||||
|
case boundsNotSet
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,7 @@ public protocol Errorable {
|
|||||||
|
|
||||||
public protocol VDSCheckboxModel: FormFieldable, Errorable, DataTrackable, Accessable, Surfaceable, Disabling, Initable {
|
public protocol VDSCheckboxModel: FormFieldable, Errorable, DataTrackable, Accessable, Surfaceable, Disabling, Initable {
|
||||||
var id: String? { get set }
|
var id: String? { get set }
|
||||||
var selected: Bool { get set }
|
var on: Bool { get set }
|
||||||
var labelText: String? { get set }
|
var labelText: String? { get set }
|
||||||
var childText: String? { get set }
|
var childText: String? { get set }
|
||||||
}
|
}
|
||||||
@ -32,7 +32,7 @@ extension VDSCheckboxModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public var shouldShowLabels: Bool {
|
public var shouldShowLabels: Bool {
|
||||||
guard labelText?.isEmpty == false && childText?.isEmpty == false else { return false }
|
guard labelText?.isEmpty == false || childText?.isEmpty == false else { return false }
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ extension VDSCheckboxModel {
|
|||||||
|
|
||||||
public class DefaultCheckboxModel: VDSCheckboxModel {
|
public class DefaultCheckboxModel: VDSCheckboxModel {
|
||||||
public var id: String?
|
public var id: String?
|
||||||
public var selected: Bool = false
|
public var on: Bool = false
|
||||||
|
|
||||||
public var labelText: String?
|
public var labelText: String?
|
||||||
public var childText: String?
|
public var childText: String?
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user