updated checkbox

Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
Matt Bruce 2022-08-02 16:22:55 -05:00
parent ef5cb2ae84
commit d8a55d87bc
2 changed files with 127 additions and 79 deletions

View File

@ -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
}
}
} }

View File

@ -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?