Constraints mended. more commenting.
This commit is contained in:
parent
5d1c87819d
commit
44e420218d
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user