Added a new extension file for CATransaction. Implementation of Checkbox and CheckboxWithLabel.
This commit is contained in:
parent
5f8780614f
commit
77a3eb8827
@ -19,6 +19,7 @@
|
||||
01E569D3223FFFA500327251 /* ThreeLayerViewController.swift in Headers */ = {isa = PBXBuildFile; fileRef = D2A5146A2214905000345BFB /* ThreeLayerViewController.swift */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
0A1214A022C11A18007C7030 /* ActionDetailWithImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A12149F22C11A17007C7030 /* ActionDetailWithImage.swift */; };
|
||||
0A1B4A96233BB18F005B3FB4 /* CheckboxWithLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */; };
|
||||
0A41BA6E2344FCD400D4C0BC /* CATransaction+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */; };
|
||||
0A7BAFA1232BE61800FB8E22 /* Checkbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */; };
|
||||
948DB67E2326DCD90011F916 /* MultiProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 948DB67D2326DCD90011F916 /* MultiProgress.swift */; };
|
||||
B8200E152280C4CF007245F4 /* ProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8200E142280C4CF007245F4 /* ProgressBar.swift */; };
|
||||
@ -205,6 +206,7 @@
|
||||
01DF55DF21F8FAA800CC099B /* MFTextFieldListView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MFTextFieldListView.swift; sourceTree = "<group>"; };
|
||||
01DF566F21FA5AB300CC099B /* TextFieldListFormViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextFieldListFormViewController.swift; sourceTree = "<group>"; };
|
||||
0A12149F22C11A17007C7030 /* ActionDetailWithImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionDetailWithImage.swift; sourceTree = "<group>"; };
|
||||
0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CATransaction+Extension.swift"; sourceTree = "<group>"; };
|
||||
0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Checkbox.swift; sourceTree = "<group>"; };
|
||||
0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxWithLabelView.swift; sourceTree = "<group>"; };
|
||||
948DB67D2326DCD90011F916 /* MultiProgress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiProgress.swift; sourceTree = "<group>"; };
|
||||
@ -654,6 +656,7 @@
|
||||
D29DF2A021E7AF4E003B2FB9 /* MVMCoreUIUtility.m */,
|
||||
D29DF2A721E7B2F9003B2FB9 /* MVMCoreUIConstants.h */,
|
||||
D29DF2A821E7B2F9003B2FB9 /* MVMCoreUIConstants.m */,
|
||||
0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */,
|
||||
);
|
||||
path = Utility;
|
||||
sourceTree = "<group>";
|
||||
@ -1095,6 +1098,7 @@
|
||||
D29DF25121E6A177003B2FB9 /* MFDigitTextBox.m in Sources */,
|
||||
DBC4391B224421A0001AB423 /* CaretButton.swift in Sources */,
|
||||
0198F7A82256A80B0066C936 /* MFRadioButton.m in Sources */,
|
||||
0A41BA6E2344FCD400D4C0BC /* CATransaction+Extension.swift in Sources */,
|
||||
D29DF13221E6851E003B2FB9 /* MVMCoreUITopAlertBaseView.m in Sources */,
|
||||
D29DF29C21E7ADB9003B2FB9 /* MFProgrammaticTableViewController.m in Sources */,
|
||||
0105618E224BBE7700E1557D /* FormValidator+TextFields.swift in Sources */,
|
||||
|
||||
@ -8,6 +8,22 @@
|
||||
|
||||
import MVMCore
|
||||
|
||||
/*
|
||||
!!! -- DO NOT REMOVE -- !!!
|
||||
(Unless Design changes the appearance of the checkmark).
|
||||
|
||||
// Offsets based on the 124x124 example checkmark
|
||||
let startXOffset: Float = 42.0 / 124.0 ~~ 0.33871
|
||||
let startYOffset: Float = 66.0 / 124.0 ~~ 0.53225
|
||||
let pivotXOffset: Float = 58.0 / 124.0 ~~ 0.46774
|
||||
let pivotYOffset: Float = 80.0 / 124.0 ~~ 0.64516
|
||||
let endXOffset: Float = 83.0 / 124.0 ~~ 0.66935
|
||||
let endYOffset: Float = 46.0 / 124.0 ~~ 0.37097
|
||||
let pivotPercentage: Float = 0.34
|
||||
let endPercentage = 1.0 - pivotPercentage
|
||||
let animationInterval: Float = 0.01
|
||||
*/
|
||||
|
||||
/**
|
||||
This class expects its height and width to be equal.
|
||||
*/
|
||||
@ -16,85 +32,82 @@ import MVMCore
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
// Form Validation
|
||||
var isRequired = false
|
||||
var fieldKey: String?
|
||||
var delegateObject: DelegateObject?
|
||||
|
||||
public static let defaultHeightWidth: CGFloat = 18.0
|
||||
|
||||
/// The color of the background when checked.
|
||||
public var checkedBackgroundColor: UIColor?
|
||||
/// If true the border of this checkbox will be circular.
|
||||
public var isRound: Bool = false
|
||||
|
||||
/// The color of the background when unChecked. Will change of the view's background color.
|
||||
public var unCheckedBackgroundColor: UIColor = .white {
|
||||
didSet (newColor) {
|
||||
backgroundColor = newColor
|
||||
/// Determined if the checkbox's UI should animated when selected.
|
||||
public var isAnimated: Bool = true
|
||||
|
||||
/// Disables all selection logic when setting the value of isSelected, reducing it to a stored property.
|
||||
public var updateSelectionOnly: Bool = false
|
||||
|
||||
/// The color of the background when checked.
|
||||
public var checkedBackgroundColor: UIColor = .white {
|
||||
didSet {
|
||||
if isSelected {
|
||||
backgroundColor = checkedBackgroundColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// If true the border of this checkbox will be circular.
|
||||
public var hasRoundedCorners = false
|
||||
|
||||
// Action Block called when the switch is selected.
|
||||
public var actionBlock: ActionBlock?
|
||||
|
||||
// Internal values to manage the appearance of the checkbox.
|
||||
private var shapeLayer: CAShapeLayer?
|
||||
/// The color of the background when unChecked.
|
||||
public var unCheckedBackgroundColor: UIColor = .white {
|
||||
didSet {
|
||||
if !isSelected {
|
||||
backgroundColor = unCheckedBackgroundColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var cornerRadiusValue: CGFloat {
|
||||
return bounds.size.height / 2
|
||||
}
|
||||
|
||||
public var lineWidth: CGFloat = 2
|
||||
public var lineColor: UIColor = .black
|
||||
public var borderColor: UIColor = .black
|
||||
/// Action Block called when the switch is selected.
|
||||
public var actionBlock: ActionBlock?
|
||||
|
||||
/// Manages the appearance of the checkbox.
|
||||
private var shapeLayer: CAShapeLayer?
|
||||
|
||||
public var checkWidth: CGFloat = 2
|
||||
public var checkColor: UIColor = .black
|
||||
public var borderWidth: CGFloat = 1
|
||||
public var borderColor: UIColor = .black
|
||||
|
||||
override open var isSelected: Bool {
|
||||
didSet {
|
||||
shapeLayer?.removeAllAnimations()
|
||||
if isSelected {
|
||||
if checkedBackgroundColor != nil {
|
||||
UIView.animate(withDuration: 0.2, delay: 0.1, options: .curveEaseOut, animations: {
|
||||
self.backgroundColor = self.checkedBackgroundColor
|
||||
})
|
||||
if !updateSelectionOnly {
|
||||
layoutIfNeeded()
|
||||
shapeLayer?.removeAllAnimations()
|
||||
|
||||
updateCheckboxUI(selection: isSelected, isAnimated: isAnimated)
|
||||
|
||||
if let delegate = delegateObject as? FormValidationProtocol {
|
||||
delegate.formValidatorModel?()?.enableByValidation()
|
||||
}
|
||||
shapeLayer?.add(checkedAnimation, forKey: "strokeEnd")
|
||||
} else {
|
||||
if checkedBackgroundColor != nil {
|
||||
UIView.animate(withDuration: 0.2, delay: 0.1, options: .curveEaseOut, animations: {
|
||||
self.backgroundColor = self.unCheckedBackgroundColor
|
||||
})
|
||||
}
|
||||
shapeLayer?.add(uncheckedAnimation, forKey: "strokeEnd")
|
||||
|
||||
accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "checkbox_checked_state" : "checkbox_unchecked_state")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lazy private var checkedAnimation: CABasicAnimation = {
|
||||
let check = CABasicAnimation(keyPath: "strokeEnd")
|
||||
check.timingFunction = CAMediaTimingFunction(name: .linear)
|
||||
check.isRemovedOnCompletion = false
|
||||
check.fillMode = .both
|
||||
check.duration = 0.3
|
||||
check.fromValue = 0
|
||||
check.toValue = 1
|
||||
return check
|
||||
}()
|
||||
|
||||
lazy private var uncheckedAnimation: CABasicAnimation = {
|
||||
let unCheck = CABasicAnimation(keyPath: "strokeEnd")
|
||||
unCheck.timingFunction = CAMediaTimingFunction(name: .linear)
|
||||
unCheck.isRemovedOnCompletion = false
|
||||
unCheck.fillMode = .both
|
||||
unCheck.duration = 0.3
|
||||
unCheck.fromValue = 1
|
||||
unCheck.toValue = 0
|
||||
return unCheck
|
||||
}()
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializers
|
||||
//--------------------------------------------------
|
||||
|
||||
override public init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
accessibilityTraits = .none
|
||||
accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "checkbox_action_hint")
|
||||
|
||||
setupView()
|
||||
}
|
||||
|
||||
@ -108,16 +121,20 @@ import MVMCore
|
||||
self.init(frame:.zero)
|
||||
}
|
||||
|
||||
public convenience init(checkedColor: UIColor, unCheckedColor: UIColor, isChecked: Bool = false) {
|
||||
public convenience init(isChecked: Bool) {
|
||||
self.init(frame: .zero)
|
||||
updateSelectionOnly = true
|
||||
isSelected = isChecked
|
||||
self.checkedBackgroundColor = checkedColor
|
||||
self.unCheckedBackgroundColor = unCheckedColor
|
||||
updateSelectionOnly = false
|
||||
}
|
||||
|
||||
public convenience init(isCircular: Bool) {
|
||||
public convenience init(checkedBackgroundColor: UIColor, unCheckedBackgroundColor: UIColor, isChecked: Bool = false) {
|
||||
self.init(frame: .zero)
|
||||
hasRoundedCorners = isCircular
|
||||
updateSelectionOnly = true
|
||||
isSelected = isChecked
|
||||
updateSelectionOnly = false
|
||||
self.checkedBackgroundColor = checkedBackgroundColor
|
||||
self.unCheckedBackgroundColor = unCheckedBackgroundColor
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
@ -126,9 +143,9 @@ import MVMCore
|
||||
|
||||
override open func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
drawCheck()
|
||||
layer.cornerRadius = hasRoundedCorners ? cornerRadiusValue : 0
|
||||
backgroundColor = unCheckedBackgroundColor
|
||||
layer.cornerRadius = isRound ? cornerRadiusValue : 0
|
||||
layer.borderWidth = borderWidth
|
||||
layer.borderColor = borderColor.cgColor
|
||||
}
|
||||
@ -137,6 +154,7 @@ import MVMCore
|
||||
|
||||
isUserInteractionEnabled = true
|
||||
translatesAutoresizingMaskIntoConstraints = false
|
||||
backgroundColor = .white
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
@ -161,6 +179,7 @@ import MVMCore
|
||||
// MARK: - Methods
|
||||
//--------------------------------------------------
|
||||
|
||||
/// Creates the check mark used for the checkbox.
|
||||
private func drawCheck() {
|
||||
|
||||
if shapeLayer == nil {
|
||||
@ -169,36 +188,20 @@ import MVMCore
|
||||
self.shapeLayer = shapeLayer
|
||||
shapeLayer.frame = bounds
|
||||
layer.addSublayer(shapeLayer)
|
||||
shapeLayer.strokeColor = lineColor.cgColor
|
||||
shapeLayer.strokeColor = checkColor.cgColor
|
||||
shapeLayer.fillColor = UIColor.clear.cgColor
|
||||
shapeLayer.path = checkMarkBezierPath.cgPath
|
||||
shapeLayer.path = checkMarkPath.cgPath
|
||||
shapeLayer.lineJoin = .bevel
|
||||
shapeLayer.lineWidth = lineWidth
|
||||
shapeLayer.lineWidth = checkWidth
|
||||
|
||||
CATransaction.withDisabledAnimations {
|
||||
shapeLayer.strokeEnd = 0.0
|
||||
shapeLayer.strokeEnd = isSelected ? 1 : 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
!!! -- DO NOT REMOVE -- !!!
|
||||
(Unless Design changes the appearance of the checkmark).
|
||||
|
||||
// Offsets based on the 124x124 example checkmark
|
||||
let startXOffset: Float = 42.0 / 124.0 ~~ 0.33871
|
||||
let startYOffset: Float = 66.0 / 124.0 ~~ 0.53225
|
||||
let pivotXOffset: Float = 58.0 / 124.0 ~~ 0.46774
|
||||
let pivotYOffset: Float = 80.0 / 124.0 ~~ 0.64516
|
||||
let endXOffset: Float = 83.0 / 124.0 ~~ 0.66935
|
||||
let endYOffset: Float = 46.0 / 124.0 ~~ 0.37097
|
||||
let pivotPercentage: Float = 0.34
|
||||
let endPercentage = 1.0 - pivotPercentage
|
||||
let animationInterval: Float = 0.01
|
||||
*/
|
||||
|
||||
/// Returns a UIBezierPath detailing the path of a checkmark
|
||||
var checkMarkBezierPath: UIBezierPath {
|
||||
var checkMarkPath: UIBezierPath {
|
||||
|
||||
let sideLength = bounds.size.height
|
||||
let startPoint = CGPoint(x: 0.33871 * sideLength, y: 0.53225 * sideLength)
|
||||
@ -213,38 +216,43 @@ import MVMCore
|
||||
return path
|
||||
}
|
||||
|
||||
public func updateSelection(_ selected: Bool, animated: Bool) {
|
||||
|
||||
// shapeLayer?.removeFromSuperlayer()
|
||||
// shapeLayer = nil
|
||||
/// Programmatic means to check/uncheck the box.
|
||||
/// - parameter selected: state of the check box: true = checked OR false = unchecked.
|
||||
/// - parameter animated: allows the state of the checkbox to change with or without animation.
|
||||
public func updateSelection(to selected: Bool, animated: Bool) {
|
||||
|
||||
DispatchQueue.main.async {
|
||||
|
||||
self.updateSelectionOnly = true
|
||||
self.isSelected = selected
|
||||
self.updateSelectionOnly = false
|
||||
self.drawCheck()
|
||||
self.shapeLayer?.removeAllAnimations()
|
||||
self.updateCheckboxUI(selection: selected, isAnimated: animated)
|
||||
}
|
||||
}
|
||||
|
||||
func updateCheckboxUI(selection: Bool, isAnimated: Bool) {
|
||||
|
||||
if isAnimated {
|
||||
let animateStrokeEnd = CABasicAnimation(keyPath: "strokeEnd")
|
||||
animateStrokeEnd.timingFunction = CAMediaTimingFunction(name: .linear)
|
||||
animateStrokeEnd.duration = 0.3
|
||||
animateStrokeEnd.fillMode = .both
|
||||
animateStrokeEnd.isRemovedOnCompletion = false
|
||||
animateStrokeEnd.fromValue = !selection ? 1 : 0
|
||||
animateStrokeEnd.toValue = selection ? 1 : 0
|
||||
self.shapeLayer?.add(animateStrokeEnd, forKey: "strokeEnd")
|
||||
|
||||
var layer: CAShapeLayer?
|
||||
if let presentation = self.shapeLayer?.presentation(), animated {
|
||||
layer = presentation
|
||||
} else {
|
||||
layer = self.shapeLayer
|
||||
UIView.animate(withDuration: 0.2, delay: 0.1, options: .curveEaseOut, animations: {
|
||||
self.backgroundColor = selection ? self.checkedBackgroundColor : self.unCheckedBackgroundColor
|
||||
})
|
||||
} else {
|
||||
CATransaction.withDisabledAnimations {
|
||||
self.shapeLayer?.strokeEnd = selection ? 1 : 0
|
||||
}
|
||||
|
||||
if animated {
|
||||
let animateStrokeEnd = CABasicAnimation(keyPath: "strokeEnd")
|
||||
animateStrokeEnd.timingFunction = CAMediaTimingFunction(name: .linear)
|
||||
animateStrokeEnd.duration = 0.33
|
||||
animateStrokeEnd.fillMode = .both
|
||||
animateStrokeEnd.isRemovedOnCompletion = false
|
||||
animateStrokeEnd.fromValue = layer?.strokeEnd ?? 0
|
||||
animateStrokeEnd.toValue = selected ? 1 : 0
|
||||
layer?.add(animateStrokeEnd, forKey: "strokeEndAnimation")
|
||||
} else {
|
||||
layer?.removeAllAnimations()
|
||||
CATransaction.withDisabledAnimations {
|
||||
layer?.strokeEnd = selected ? 1 : 0
|
||||
}
|
||||
}
|
||||
self.backgroundColor = selection ? self.checkedBackgroundColor : self.unCheckedBackgroundColor
|
||||
}
|
||||
}
|
||||
|
||||
@ -254,11 +262,7 @@ import MVMCore
|
||||
|
||||
open override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
|
||||
if touchIsAcceptablyOutside(touches.first) {
|
||||
sendActions(for: .touchUpOutside)
|
||||
} else {
|
||||
sendActions(for: .touchUpInside)
|
||||
}
|
||||
sendActions(for: touchIsAcceptablyOutside(touches.first) ? .touchUpOutside : .touchUpInside)
|
||||
}
|
||||
|
||||
func touchIsAcceptablyOutside(_ touch: UITouch?) -> Bool {
|
||||
@ -272,40 +276,6 @@ import MVMCore
|
||||
return x < -faultTolerance || y < -faultTolerance || x > widthLimit || y > heightLimt
|
||||
}
|
||||
|
||||
// if delegate && delegate.responds(to: #selector(formValidationProtocol)) && delegate.perform(#selector(formValidationProtocol)).responds(to: #selector(Unmanaged<AnyObject>.formValidatorModel)) {
|
||||
// let formValidator = delegate.perform(#selector(formValidationProtocol)).perform(#selector(Unmanaged<AnyObject>.formValidatorModel)) as? FormValidator
|
||||
// formValidator?.enableByValidation()
|
||||
// }
|
||||
|
||||
|
||||
/*
|
||||
|
||||
- (void)setSelected:(BOOL)selected animated:(BOOL)animated runBlock:(BOOL)runBlock {
|
||||
[self addAccessibilityLabel:selected];
|
||||
|
||||
self.isSelected = selected;
|
||||
if (self.switchSelected && runBlock) {
|
||||
self.switchSelected(selected);
|
||||
}
|
||||
if (selected) {
|
||||
[UIView animateWithDuration:0.2 delay:0.1 options:UIViewAnimationOptionCurveEaseOut animations:^{
|
||||
self.checkedSquare.backgroundColor = self.checkedColor;
|
||||
} completion:nil];
|
||||
[self.checkMark updateCheckSelected:YES animated:animated];
|
||||
} else {
|
||||
[UIView animateWithDuration:0.2 delay:0.1 options:UIViewAnimationOptionCurveEaseOut animations:^{
|
||||
self.checkedSquare.backgroundColor = self.unCheckedColor;
|
||||
} completion:nil];
|
||||
[self.checkMark updateCheckSelected:NO animated:animated];
|
||||
}
|
||||
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(formValidationProtocol)] && [[self.delegate performSelector:@selector(formValidationProtocol)] respondsToSelector:@selector(formValidatorModel)]) {
|
||||
FormValidator *formValidator = [[self.delegate performSelector:@selector(formValidationProtocol)] performSelector:@selector(formValidatorModel)];
|
||||
[formValidator enableByValidation];
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Molecular
|
||||
//--------------------------------------------------
|
||||
@ -322,15 +292,22 @@ import MVMCore
|
||||
setupView()
|
||||
}
|
||||
|
||||
public func updateView(_ size: CGFloat) {
|
||||
|
||||
// TODO: Ensure the check logic is resized.
|
||||
}
|
||||
public func updateView(_ size: CGFloat) { }
|
||||
|
||||
public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
self.delegateObject = delegateObject
|
||||
FormValidator.setupValidation(molecule: self, delegate: delegateObject?.formValidationProtocol)
|
||||
|
||||
guard let dictionary = json else { return }
|
||||
|
||||
if let fieldKey = dictionary[KeyFieldKey] as? String {
|
||||
self.fieldKey = fieldKey
|
||||
}
|
||||
|
||||
if let isRequired = dictionary[KeyRequired] as? Bool {
|
||||
self.isRequired = isRequired
|
||||
}
|
||||
|
||||
if let borderColorHex = dictionary["borderColor"] as? String {
|
||||
layer.borderColor = UIColor.mfGet(forHex: borderColorHex).cgColor
|
||||
}
|
||||
@ -339,36 +316,50 @@ import MVMCore
|
||||
layer.borderWidth = borderWidth
|
||||
}
|
||||
|
||||
if let checkColorHex = dictionary["lineColor"] as? String {
|
||||
lineColor = UIColor.mfGet(forHex: checkColorHex)
|
||||
if let isChecked = dictionary["isChecked"] as? Bool, isChecked {
|
||||
updateSelectionOnly = true
|
||||
isSelected = isChecked
|
||||
updateSelectionOnly = false
|
||||
}
|
||||
|
||||
if let checkColorHex = dictionary["checkedColor"] as? String {
|
||||
checkedBackgroundColor = UIColor.mfGet(forHex: checkColorHex)
|
||||
if let checkColorHex = dictionary["checkColor"] as? String {
|
||||
checkColor = UIColor.mfGet(forHex: checkColorHex)
|
||||
}
|
||||
|
||||
if let unCheckedColorHex = dictionary["unCheckedColor"] as? String {
|
||||
unCheckedBackgroundColor = UIColor.mfGet(forHex: unCheckedColorHex)
|
||||
if let unCheckedBackgroundColorHex = dictionary["unCheckedBackgroundColor"] as? String {
|
||||
unCheckedBackgroundColor = UIColor.mfGet(forHex: unCheckedBackgroundColorHex)
|
||||
}
|
||||
|
||||
if let checkedBackgroundColorHex = dictionary["checkedBackgroundColor"] as? String {
|
||||
checkedBackgroundColor = UIColor.mfGet(forHex: checkedBackgroundColorHex)
|
||||
}
|
||||
|
||||
if let isAnimated = dictionary["isAnimated"] as? Bool {
|
||||
self.isAnimated = isAnimated
|
||||
}
|
||||
|
||||
if let isRound = dictionary["isRound"] as? Bool {
|
||||
hasRoundedCorners = isRound
|
||||
self.isRound = isRound
|
||||
}
|
||||
|
||||
// if let actionMap = dictionary.optionalDictionaryForKey("actionMap") {
|
||||
// actionBlock = { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) }
|
||||
// }
|
||||
if let actionMap = dictionary.optionalDictionaryForKey("actionMap") {
|
||||
actionBlock = { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Move to its own extension file.
|
||||
extension CATransaction {
|
||||
// MARK:- FormValidationProtocol
|
||||
extension Checkbox: FormValidationProtocol {
|
||||
|
||||
/// Performs changes without activating animation actions.
|
||||
static func withDisabledAnimations(_ actionBlock: ActionBlock) {
|
||||
CATransaction.begin()
|
||||
CATransaction.setDisableActions(true)
|
||||
actionBlock()
|
||||
CATransaction.commit()
|
||||
public func isValidField() -> Bool {
|
||||
return isRequired ? isSelected : true
|
||||
}
|
||||
|
||||
public func formFieldName() -> String? {
|
||||
return fieldKey
|
||||
}
|
||||
|
||||
public func formFieldValue() -> Any? {
|
||||
return NSNumber(value: isSelected)
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,8 +12,8 @@
|
||||
// MARK: - Outlets
|
||||
//--------------------------------------------------
|
||||
|
||||
let checkbox = Checkbox()
|
||||
let label = Label.commonLabelB2(true)
|
||||
public let checkbox = Checkbox()
|
||||
public let label = Label.commonLabelB2(true)
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
@ -21,17 +21,34 @@
|
||||
|
||||
var sizeObject: MFSizeObject? = MFSizeObject(standardSize: Checkbox.defaultHeightWidth, standardiPadPortraitSize: Checkbox.defaultHeightWidth + 6.0)
|
||||
|
||||
var isRequired = false
|
||||
var fieldKey: String?
|
||||
var delegate: DelegateObject?
|
||||
var checkboxPosition: CheckboxPosition = .centerLeft
|
||||
|
||||
public enum CheckboxPosition: String {
|
||||
case centerLeft
|
||||
case topLeft
|
||||
case bottomLeft
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Constraints
|
||||
//--------------------------------------------------
|
||||
|
||||
|
||||
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
|
||||
//--------------------------------------------------
|
||||
@ -46,23 +63,24 @@
|
||||
addSubview(checkbox)
|
||||
addSubview(label)
|
||||
|
||||
label.setContentCompressionResistancePriority(.required, for: .vertical)
|
||||
|
||||
let dimension = sizeObject?.getValueBasedOnApplicationWidth() ?? Checkbox.defaultHeightWidth
|
||||
checkboxWidthConstraint = checkbox.heightAnchor.constraint(equalToConstant: dimension)
|
||||
checkboxWidthConstraint?.isActive = true
|
||||
checkboxHeightConstraint = checkbox.widthAnchor.constraint(equalToConstant: dimension)
|
||||
checkboxHeightConstraint?.isActive = true
|
||||
|
||||
checkbox.topAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.topAnchor).isActive = true
|
||||
layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: checkbox.bottomAnchor).isActive = true
|
||||
checkbox.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor).isActive = true
|
||||
checkbox.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
|
||||
checkbox.checkedBackgroundColor = .yellow
|
||||
alignSubviews(by: .centerLeft)
|
||||
|
||||
label.topAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.topAnchor).isActive = true
|
||||
layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: label.bottomAnchor).isActive = true
|
||||
label.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
|
||||
layoutMarginsGuide.trailingAnchor.constraint(equalTo: label.trailingAnchor).isActive = true
|
||||
label.leadingAnchor.constraint(equalTo: checkbox.trailingAnchor, constant: PaddingTwo).isActive = true
|
||||
label.isUserInteractionEnabled = true
|
||||
let tap = UITapGestureRecognizer(target: self, action: #selector(tapped))
|
||||
tap.numberOfTapsRequired = 1
|
||||
label.addGestureRecognizer(tap)
|
||||
}
|
||||
|
||||
@objc func tapped() {
|
||||
checkbox.updateSelection(to: !checkbox.isSelected, animated: true)
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
@ -77,13 +95,18 @@
|
||||
override public init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
setupView()
|
||||
// addAccessibleProperties()
|
||||
}
|
||||
|
||||
public convenience init() {
|
||||
self.init(frame: .zero)
|
||||
}
|
||||
|
||||
public convenience init(position: CheckboxPosition) {
|
||||
self.init(frame: .zero)
|
||||
|
||||
alignSubviews(by: position)
|
||||
}
|
||||
|
||||
public convenience init(checkedColor: UIColor, unCheckedColor: UIColor, text: String?, isChecked: Bool = false) {
|
||||
self.init(frame: .zero)
|
||||
|
||||
@ -99,79 +122,130 @@
|
||||
checkbox.unCheckedBackgroundColor = unCheckedColor
|
||||
label.attributedText = attributedText
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Methods
|
||||
//--------------------------------------------------
|
||||
|
||||
/// Aligns Checkbox and Label relative to the desired position of the Checkbox.
|
||||
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)
|
||||
|
||||
labelTopConstraint = label.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor)
|
||||
labelTrailingConstraint = layoutMarginsGuide.trailingAnchor.constraint(equalTo: label.trailingAnchor)
|
||||
checkboxLabelConstraint = label.leadingAnchor.constraint(equalTo: checkbox.trailingAnchor, constant: PaddingTwo)
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Molecular
|
||||
/// MARK: - Molecular
|
||||
extension CheckboxWithLabelView {
|
||||
|
||||
override open class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
|
||||
override open class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
|
||||
return CGFloat(Checkbox.defaultHeightWidth)
|
||||
}
|
||||
|
||||
@objc override open func updateView(_ size: CGFloat) {
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.label.updateView(size)
|
||||
if self.checkbox.responds(to: #selector(self.updateView(_:))) {
|
||||
if let dimension = self.sizeObject?.getValueBased(onSize: size) {
|
||||
self.checkboxWidthConstraint?.constant = dimension
|
||||
self.checkboxHeightConstraint?.constant = dimension
|
||||
self.checkbox.updateView(size)
|
||||
}
|
||||
label.updateView(size)
|
||||
|
||||
if self.checkbox.responds(to: #selector(self.updateView(_:))) {
|
||||
if let dimension = sizeObject?.getValueBased(onSize: size) {
|
||||
checkboxWidthConstraint?.constant = dimension
|
||||
checkboxHeightConstraint?.constant = dimension
|
||||
checkbox.updateView(size)
|
||||
}
|
||||
}
|
||||
|
||||
layoutIfNeeded()
|
||||
}
|
||||
|
||||
override open func alignment() -> UIStackView.Alignment {
|
||||
return .leading
|
||||
}
|
||||
|
||||
open override func resetConstraints() {
|
||||
super.resetConstraints()
|
||||
|
||||
toggleSubviewConstraints(isActive: false)
|
||||
}
|
||||
|
||||
override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
|
||||
delegate = delegateObject
|
||||
FormValidator.setupValidation(molecule: self, delegate: delegateObject?.formValidationProtocol)
|
||||
|
||||
guard let dictionary = json else { return }
|
||||
|
||||
if let fieldKey = dictionary[KeyFieldKey] as? String {
|
||||
self.fieldKey = fieldKey
|
||||
}
|
||||
|
||||
if let isRequired = dictionary[KeyRequired] as? Bool {
|
||||
self.isRequired = isRequired
|
||||
if let checkboxAlignment = dictionary["checkboxAlignment"] as? String, let position = CheckboxPosition(rawValue: checkboxAlignment) {
|
||||
toggleSubviewConstraints(isActive: false)
|
||||
alignSubviews(by: position)
|
||||
}
|
||||
|
||||
checkbox.setWithJSON(dictionary.dictionaryForKey("checkbox"), delegateObject: delegateObject, additionalData: additionalData)
|
||||
label.setWithJSON(dictionary.dictionaryForKey("label"), delegateObject: delegateObject, additionalData: additionalData)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK:- FormValidationProtocol
|
||||
extension CheckboxWithLabelView: FormValidationProtocol {
|
||||
|
||||
public func isValidField() -> Bool {
|
||||
return isRequired ? checkbox.isSelected : true
|
||||
}
|
||||
|
||||
public func formFieldName() -> String? {
|
||||
return fieldKey
|
||||
}
|
||||
|
||||
public func formFieldValue() -> Any? {
|
||||
return NSNumber(value: checkbox.isSelected)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK:- Accessibility
|
||||
extension CheckboxWithLabelView {
|
||||
|
||||
func addAccessibleProperties() {
|
||||
// accessibilityTraits = .none
|
||||
// accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "checkbox_action_hint")
|
||||
}
|
||||
|
||||
func addAccessibilityLabel(_ selected: Bool) {
|
||||
// let state = selected ? MVMCoreUIUtility.hardcodedString(withKey: "checkbox_checked_state") : MVMCoreUIUtility.hardcodedString(withKey: "checkbox_unchecked_state")
|
||||
// accessibilityLabel = String(format: MVMCoreUIUtility.hardcodedString(withKey: "checkbox_desc_state"), label.text ?? "", state)
|
||||
}
|
||||
}
|
||||
|
||||
21
MVMCoreUI/Utility/CATransaction+Extension.swift
Normal file
21
MVMCoreUI/Utility/CATransaction+Extension.swift
Normal file
@ -0,0 +1,21 @@
|
||||
//
|
||||
// CATransaction+Extension.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Kevin Christiano on 10/2/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
extension CATransaction {
|
||||
|
||||
/// Performs changes without activating animation actions.
|
||||
static func withDisabledAnimations(_ actionBlock: ActionBlock) {
|
||||
CATransaction.begin()
|
||||
CATransaction.setDisableActions(true)
|
||||
actionBlock()
|
||||
CATransaction.commit()
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user