Constraints mended. more commenting.

This commit is contained in:
Kevin G Christiano 2019-10-03 13:22:15 -04:00
parent 5d1c87819d
commit 44e420218d
2 changed files with 90 additions and 110 deletions

View File

@ -66,6 +66,7 @@ import MVMCore
}
}
/// Retrieves ideeal radius value to curve square into a circle.
public var cornerRadiusValue: CGFloat {
return bounds.size.height / 2
}
@ -76,6 +77,7 @@ import MVMCore
/// Manages the appearance of the checkbox.
private var shapeLayer: CAShapeLayer?
/// Width of the check mark.
public var checkWidth: CGFloat = 2 {
didSet {
CATransaction.withDisabledAnimations {
@ -83,6 +85,8 @@ import MVMCore
}
}
}
/// Color of the check mark.
public var checkColor: UIColor = .black {
didSet {
CATransaction.withDisabledAnimations {
@ -91,24 +95,35 @@ import MVMCore
}
}
public var borderWidth: CGFloat = 1
public var borderColor: UIColor = .black
/// Border width of the checkbox
public var borderWidth: CGFloat = 1 {
didSet {
layer.borderWidth = borderWidth
}
}
/// border color of the Checkbox
public var borderColor: UIColor = .black {
didSet {
layer.borderColor = borderColor.cgColor
}
}
/// The represented state of the Checkbox.
/// If updateSelectionOnly = true, then this will behave only as a stored property.
override open var isSelected: Bool {
didSet {
if !updateSelectionOnly {
layoutIfNeeded()
shapeLayer?.removeAllAnimations()
updateCheckboxUI(selection: isSelected, isAnimated: isAnimated)
updateCheckboxUI(isSelected: isSelected, isAnimated: isAnimated)
if let delegate = delegateObject as? FormValidationProtocol {
delegate.formValidatorModel?()?.enableByValidation()
}
if let state = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "checkbox_checked_state" : "checkbox_unchecked_state") {
accessibilityLabel = String(format: MVMCoreUIUtility.hardcodedString(withKey: "checkbox_desc_state") ?? "%@", state)
}
updateAccessibilityLabel()
}
}
}
@ -122,9 +137,7 @@ import MVMCore
accessibilityTraits = .button
accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "checkbox_action_hint")
if let state = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "checkbox_checked_state" : "checkbox_unchecked_state") {
accessibilityLabel = String(format: MVMCoreUIUtility.hardcodedString(withKey: "checkbox_desc_state") ?? "%@", state)
}
updateAccessibilityLabel()
setupView()
}
@ -181,13 +194,16 @@ import MVMCore
open override func sendAction(_ action: Selector, to target: Any?, for event: UIEvent?) {
super.sendAction(action, to: target, for: event)
isSelected.toggle()
actionBlock?()
toggleAndAction()
}
open override func sendActions(for controlEvents: UIControl.Event) {
super.sendActions(for: controlEvents)
toggleAndAction()
}
/// This will toggle the state of the Checkbox and execute the actionBlock if provided.
public func toggleAndAction() {
isSelected.toggle()
actionBlock?()
@ -208,7 +224,7 @@ import MVMCore
layer.addSublayer(shapeLayer)
shapeLayer.strokeColor = checkColor.cgColor
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.path = checkMarkPath.cgPath
shapeLayer.path = checkMarkPath()
shapeLayer.lineJoin = .bevel
shapeLayer.lineWidth = checkWidth
@ -219,19 +235,19 @@ import MVMCore
}
/// Returns a UIBezierPath detailing the path of a checkmark
var checkMarkPath: UIBezierPath {
func checkMarkPath() -> CGPath {
let sideLength = bounds.size.height
let startPoint = CGPoint(x: 0.33871 * sideLength, y: 0.53225 * sideLength)
let pivotOffSet = CGPoint(x: 0.46774 * sideLength, y: 0.64516 * sideLength)
let endOffset = CGPoint(x: 0.66935 * sideLength , y: 0.37097 * sideLength)
let path = UIBezierPath()
path.move(to: startPoint)
path.addLine(to: pivotOffSet)
path.addLine(to: endOffset)
let bezierPath = UIBezierPath()
bezierPath.move(to: startPoint)
bezierPath.addLine(to: pivotOffSet)
bezierPath.addLine(to: endOffset)
return path
return bezierPath.cgPath
}
/// Programmatic means to check/uncheck the box.
@ -246,11 +262,14 @@ import MVMCore
self.updateSelectionOnly = false
self.drawCheck()
self.shapeLayer?.removeAllAnimations()
self.updateCheckboxUI(selection: selected, isAnimated: animated)
self.updateCheckboxUI(isSelected: selected, isAnimated: animated)
}
}
func updateCheckboxUI(selection: Bool, isAnimated: Bool) {
/// updates the visuals of the check mark and background.
/// - parameter isSelection: the check state of the checkbox.
/// - parameter isAnimated: determines of the changes should animate or immediately refelect.
public func updateCheckboxUI(isSelected: Bool, isAnimated: Bool) {
if isAnimated {
let animateStrokeEnd = CABasicAnimation(keyPath: "strokeEnd")
@ -258,19 +277,27 @@ import MVMCore
animateStrokeEnd.duration = 0.3
animateStrokeEnd.fillMode = .both
animateStrokeEnd.isRemovedOnCompletion = false
animateStrokeEnd.fromValue = !selection ? 1 : 0
animateStrokeEnd.toValue = selection ? 1 : 0
animateStrokeEnd.fromValue = !isSelected ? 1 : 0
animateStrokeEnd.toValue = isSelected ? 1 : 0
self.shapeLayer?.add(animateStrokeEnd, forKey: "strokeEnd")
UIView.animate(withDuration: 0.2, delay: 0.1, options: .curveEaseOut, animations: {
self.backgroundColor = selection ? self.checkedBackgroundColor : self.unCheckedBackgroundColor
self.backgroundColor = isSelected ? self.checkedBackgroundColor : self.unCheckedBackgroundColor
})
} else {
CATransaction.withDisabledAnimations {
self.shapeLayer?.strokeEnd = selection ? 1 : 0
self.shapeLayer?.strokeEnd = isSelected ? 1 : 0
}
self.backgroundColor = selection ? self.checkedBackgroundColor : self.unCheckedBackgroundColor
self.backgroundColor = isSelected ? self.checkedBackgroundColor : self.unCheckedBackgroundColor
}
}
/// Adjust accessibility label based on state of Checkbox.
func updateAccessibilityLabel() {
// Attention: This needs to be addressed with the accessibility team.
if let state = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "checkbox_checked_state" : "checkbox_unchecked_state") {
accessibilityLabel = String(format: MVMCoreUIUtility.hardcodedString(withKey: "checkbox_desc_state") ?? "%@", state)
}
}

View File

@ -21,12 +21,12 @@
var sizeObject: MFSizeObject? = MFSizeObject(standardSize: Checkbox.defaultHeightWidth, standardiPadPortraitSize: Checkbox.defaultHeightWidth + 6.0)
var checkboxPosition: CheckboxPosition = .centerLeft
var checkboxPosition: CheckboxPosition = .center
public enum CheckboxPosition: String {
case centerLeft
case topLeft
case bottomLeft
case center
case top
case bottom
}
//--------------------------------------------------
@ -35,20 +35,10 @@
var checkboxWidthConstraint: NSLayoutConstraint?
var checkboxHeightConstraint: NSLayoutConstraint?
var checkboxLeadingConstraint: NSLayoutConstraint?
var checkboxTrailingConstraint: NSLayoutConstraint?
var checkboxTopConstraint: NSLayoutConstraint?
var checkboxBottomConstraint: NSLayoutConstraint?
var checkboxCenterYConstraint: NSLayoutConstraint?
var checkboxLabelConstraint: NSLayoutConstraint?
var labelLeadingConstraint: NSLayoutConstraint?
var labelTrailingConstraint: NSLayoutConstraint?
var labelTopConstraint: NSLayoutConstraint?
var labelBottomConstraint: NSLayoutConstraint?
//--------------------------------------------------
// MARK: - Life Cycle
//--------------------------------------------------
@ -71,7 +61,27 @@
checkboxHeightConstraint = checkbox.widthAnchor.constraint(equalToConstant: dimension)
checkboxHeightConstraint?.isActive = true
alignSubviews(by: .centerLeft)
checkbox.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor).isActive = true
let generalTop = checkbox.topAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.topAnchor)
generalTop.priority = UILayoutPriority(rawValue: 750)
generalTop.isActive = true
let generalBottom = checkbox.bottomAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.bottomAnchor)
generalBottom.priority = UILayoutPriority(rawValue: 750)
generalBottom.isActive = true
// Allows various positions of checkbox.
checkboxBottomConstraint = layoutMarginsGuide.bottomAnchor.constraint(equalTo: checkbox.bottomAnchor)
checkboxTopConstraint = checkbox.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor)
checkboxCenterYConstraint = checkbox.centerYAnchor.constraint(equalTo: centerYAnchor)
label.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor).isActive = true
layoutMarginsGuide.trailingAnchor.constraint(equalTo: label.trailingAnchor).isActive = true
label.leadingAnchor.constraint(equalTo: checkbox.trailingAnchor, constant: PaddingTwo).isActive = true
layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: label.bottomAnchor).isActive = true
alignSubviews(by: .center)
}
//--------------------------------------------------
@ -95,7 +105,6 @@
public convenience init(position: CheckboxPosition) {
self.init(frame: .zero)
toggleSubviewConstraints(isActive: false)
alignSubviews(by: position)
}
@ -107,74 +116,21 @@
private func alignSubviews(by position: CheckboxPosition) {
checkboxPosition = position
label.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor).isActive = true
switch position {
case .centerLeft:
checkboxTopConstraint = checkbox.topAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.topAnchor)
checkboxBottomConstraint = layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: checkbox.bottomAnchor)
checkboxLeadingConstraint = checkbox.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor)
checkboxCenterYConstraint = checkbox.centerYAnchor.constraint(equalTo: centerYAnchor)
case .center:
checkboxCenterYConstraint?.isActive = true
checkboxBottomConstraint?.isActive = false
checkboxTopConstraint?.isActive = false
labelTopConstraint = label.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor)
labelTrailingConstraint = layoutMarginsGuide.trailingAnchor.constraint(equalTo: label.trailingAnchor)
checkboxLabelConstraint = label.leadingAnchor.constraint(equalTo: checkbox.trailingAnchor, constant: PaddingTwo)
case .top:
checkboxBottomConstraint?.isActive = false
checkboxTopConstraint?.isActive = true
checkboxCenterYConstraint?.isActive = false
case .topLeft:
checkboxTopConstraint = checkbox.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor)
checkboxBottomConstraint = layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: checkbox.bottomAnchor)
checkboxLeadingConstraint = checkbox.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor)
labelTopConstraint = label.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor)
labelTrailingConstraint = layoutMarginsGuide.trailingAnchor.constraint(equalTo: label.trailingAnchor)
checkboxLabelConstraint = label.leadingAnchor.constraint(equalTo: checkbox.trailingAnchor, constant: PaddingTwo)
case .bottomLeft:
checkboxTopConstraint = checkbox.topAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.topAnchor)
checkboxBottomConstraint = layoutMarginsGuide.bottomAnchor.constraint(equalTo: checkbox.bottomAnchor)
checkboxLeadingConstraint = checkbox.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor)
labelTopConstraint = label.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor)
labelTrailingConstraint = layoutMarginsGuide.trailingAnchor.constraint(equalTo: label.trailingAnchor)
checkboxLabelConstraint = label.leadingAnchor.constraint(equalTo: checkbox.trailingAnchor, constant: PaddingTwo)
labelBottomConstraint = layoutMarginsGuide.bottomAnchor.constraint(equalTo: label.bottomAnchor)
}
if position != .bottomLeft {
layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: label.bottomAnchor).isActive = true
let labelBottom = label.bottomAnchor.constraint(equalTo: bottomAnchor)
labelBottomConstraint = labelBottom
labelBottom.priority = UILayoutPriority(249)
labelBottom.isActive = true
}
toggleSubviewConstraints(isActive: true)
}
func toggleSubviewConstraints(isActive state: Bool) {
checkboxLeadingConstraint?.isActive = state
checkboxTrailingConstraint?.isActive = state
checkboxTopConstraint?.isActive = state
checkboxBottomConstraint?.isActive = state
checkboxCenterYConstraint?.isActive = state
labelLeadingConstraint?.isActive = state
labelTrailingConstraint?.isActive = state
labelTopConstraint?.isActive = state
labelBottomConstraint?.isActive = state
checkboxLabelConstraint?.isActive = state
if !state {
checkboxLeadingConstraint = nil
checkboxTrailingConstraint = nil
checkboxTopConstraint = nil
checkboxBottomConstraint = nil
checkboxCenterYConstraint = nil
labelLeadingConstraint = nil
labelTrailingConstraint = nil
labelTopConstraint = nil
labelBottomConstraint = nil
checkboxLabelConstraint = nil
case .bottom:
checkboxBottomConstraint?.isActive = true
checkboxTopConstraint?.isActive = false
checkboxCenterYConstraint?.isActive = false
}
}
}
@ -208,8 +164,6 @@ extension CheckboxWithLabelView {
open override func resetConstraints() {
super.resetConstraints()
toggleSubviewConstraints(isActive: false)
}
override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
@ -218,7 +172,6 @@ extension CheckboxWithLabelView {
guard let dictionary = json else { return }
if let checkboxAlignment = dictionary["checkboxAlignment"] as? String, let position = CheckboxPosition(rawValue: checkboxAlignment) {
toggleSubviewConstraints(isActive: false)
alignSubviews(by: position)
}