From 0b8d5853549a6c685363acf4aaa633d929a690b1 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 3 Aug 2022 09:52:23 -0500 Subject: [PATCH] refactored checkbox coloring Signed-off-by: Matt Bruce --- VDS/Components/Checkbox/VDSCheckbox.swift | 227 +++++++++------------- 1 file changed, 92 insertions(+), 135 deletions(-) diff --git a/VDS/Components/Checkbox/VDSCheckbox.swift b/VDS/Components/Checkbox/VDSCheckbox.swift index 9f134a04..f8a16ac8 100644 --- a/VDS/Components/Checkbox/VDSCheckbox.swift +++ b/VDS/Components/Checkbox/VDSCheckbox.swift @@ -26,75 +26,58 @@ import Combine //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- - private func getCheckboxBackgroundColor(for disabled: Bool, surface: Surface) -> (on: UIColor, off: UIColor) { - if disabled { - if surface == .light { - return ( - on: VDSColor.interactiveDisabledOnlight, - off: .clear) + private func getCheckboxBackgroundColor(viewModel: ModelType) -> UIColor { + var colors: (on: UIColor, off: UIColor) + if viewModel.disabled { + if viewModel.surface == .light { + colors = (on: VDSColor.interactiveDisabledOnlight, off: .clear) } else { - return ( - on: VDSColor.interactiveDisabledOndark, - off: .clear - ) + colors = (on: VDSColor.interactiveDisabledOndark, off: .clear) } } else { - if surface == .light { - return ( - on: VDSColor.elementsPrimaryOnlight, - off: .clear - ) + if viewModel.surface == .light { + colors = (on: VDSColor.elementsPrimaryOnlight, off: .clear) } else { - return ( - on: VDSColor.elementsPrimaryOndark, - off: .clear - ) + colors = (on: VDSColor.elementsPrimaryOndark, off: .clear) } } + return viewModel.on ? colors.on : colors.off } - private func getCheckboxBorderColor(for disabled: Bool, surface: Surface) -> (on: UIColor, off: UIColor) { - if disabled { - if surface == .light { - return ( - on: VDSColor.interactiveDisabledOnlight, - off: VDSColor.interactiveDisabledOnlight - ) + private func getCheckboxBorderColor(viewModel: ModelType) -> UIColor { + var colors: (on: UIColor, off: UIColor) + if viewModel.disabled { + if viewModel.surface == .light { + colors = (on: VDSColor.interactiveDisabledOnlight, off: VDSColor.interactiveDisabledOnlight) } else { - return ( - on: VDSColor.interactiveDisabledOndark, - off: VDSColor.interactiveDisabledOnlight - ) + colors = (on: VDSColor.interactiveDisabledOndark, off: VDSColor.interactiveDisabledOnlight) } } else { - if surface == .light { - return ( - on: VDSColor.elementsPrimaryOnlight, - off: VDSFormControlsColor.borderOnlight - ) + if viewModel.surface == .light { + colors = (on: VDSColor.elementsPrimaryOnlight, off: VDSFormControlsColor.borderOnlight) } else { - return ( - on: VDSColor.elementsPrimaryOndark, - off: VDSFormControlsColor.borderOndark - ) + colors = (on: VDSColor.elementsPrimaryOndark, off: VDSFormControlsColor.borderOndark) } } + return viewModel.on ? colors.on : colors.off } - private func getCheckboxCheckColor(for disabled: Bool, surface: Surface) -> UIColor { + private func getCheckboxCheckColor(viewModel: ModelType) -> UIColor { + var color: UIColor if disabled { if surface == .light { - return VDSColor.interactiveDisabledOndark + color = VDSColor.interactiveDisabledOndark } else { - return VDSColor.interactiveDisabledOnlight + color = VDSColor.interactiveDisabledOnlight } } else { if surface == .light { - return VDSColor.elementsPrimaryOndark + color = VDSColor.elementsPrimaryOndark } else { - return VDSColor.elementsPrimaryOnlight + color = VDSColor.elementsPrimaryOnlight } } + return viewModel.on ? color : .clear } private var mainStackView: UIStackView = { @@ -138,8 +121,8 @@ import Combine return label }() - private var checkboxView: CheckBoxView = { - let view = CheckBoxView() + private var checkboxView: UIView = { + let view = UIView() view.translatesAutoresizingMaskIntoConstraints = false return view }() @@ -235,7 +218,7 @@ import Combine checkboxHeightConstraint?.constant = checkboxSize.height checkboxWidthConstraint?.constant = checkboxSize.width - setCheckboxColor(viewModel: model) + updateCheckbox(viewModel: model) } public override func setupView() { @@ -259,7 +242,7 @@ import Combine checkboxWidthConstraint = checkboxView.widthAnchor.constraint(equalToConstant: checkboxSize.width) checkboxWidthConstraint?.isActive = true - setCheckboxColor(viewModel: model) + updateCheckbox(viewModel: model) mainStackView.topAnchor.constraint(equalTo: topAnchor).isActive = true mainStackView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true @@ -267,20 +250,7 @@ import Combine mainStackView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true } - - /// Creates the check mark layer. - private func setCheckboxColor(viewModel: ModelType) { - - let background = getCheckboxBackgroundColor(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 backgroundColor = viewModel.on ? background.on : background.off - checkboxView.backgroundColor = backgroundColor - checkboxView.borderColor = viewModel.on ? border.on : border.off - checkboxView.checkColor = viewModel.on ? checkColor : backgroundColor - checkboxView.isSelected = viewModel.on - } - + func ensureLabel(viewModel: ModelType) { //deal with labels @@ -325,10 +295,68 @@ import Combine public override func reset() { super.reset() - setCheckboxColor(viewModel: model) + updateCheckbox(viewModel: model) setAccessibilityLabel() onChange = nil } + + //-------------------------------------------------- + // MARK: - Checkbox View + //-------------------------------------------------- + /// Manages the appearance of the checkbox. + private var shapeLayer: CAShapeLayer? + + private func updateCheckbox(viewModel: ModelType) { + //get the colors + let backgroundColor = getCheckboxBackgroundColor(viewModel: viewModel) + let borderColor = getCheckboxBorderColor(viewModel: viewModel) + let checkColor = getCheckboxCheckColor(viewModel: viewModel) + + if let shapeLayer = shapeLayer, let sublayers = layer.sublayers, sublayers.contains(shapeLayer) { + shapeLayer.removeFromSuperlayer() + self.shapeLayer = nil + } + + checkboxView.backgroundColor = backgroundColor + checkboxView.layer.borderColor = borderColor.cgColor + checkboxView.layer.cornerRadius = 2.0 + checkboxView.layer.borderWidth = 1.0 + + if shapeLayer == nil { + let bounds = checkboxView.bounds + let length = max(bounds.size.height, bounds.size.width) + guard length > 0.0, shapeLayer == nil else { return } + + //draw the checkmark layer + 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) + + let shapeLayer = CAShapeLayer() + self.shapeLayer = shapeLayer + shapeLayer.frame = bounds + layer.addSublayer(shapeLayer) + shapeLayer.strokeColor = checkColor.cgColor + shapeLayer.fillColor = UIColor.clear.cgColor + shapeLayer.path = bezierPath.cgPath + shapeLayer.lineJoin = .miter + shapeLayer.lineWidth = 2 + CATransaction.withDisabledAnimations { + shapeLayer.strokeEnd = model.on ? 1 : 0 + } + } + + } //-------------------------------------------------- // MARK: - Actions @@ -375,7 +403,7 @@ import Combine let enabled = !viewModel.disabled ensureLabel(viewModel: viewModel) - setCheckboxColor(viewModel: viewModel) + updateCheckbox(viewModel: viewModel) setAccessibilityHint(enabled) setAccessibilityValue(viewModel.on) setAccessibilityLabel(viewModel.on) @@ -384,75 +412,4 @@ import Combine layoutIfNeeded() } - private class CheckBoxView: UIView { - public var borderColor: UIColor = .black - public var checkColor: UIColor = .white - public var isSelected: Bool = false { - didSet { - if let shapeLayer = shapeLayer, let sublayers = layer.sublayers, sublayers.contains(shapeLayer) { - shapeLayer.removeFromSuperlayer() - self.shapeLayer = nil - } - } - } - - /// 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 - layer.borderWidth = 1.0 - shapeLayer?.strokeColor = checkColor.cgColor - - guard let path = try? checkMarkPath(), shapeLayer == nil 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 - } - } } -