diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index a080e482..1e3bac12 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -18,6 +18,9 @@ 01DF567021FA5AB300CC099B /* TextFieldListFormViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01DF566F21FA5AB300CC099B /* TextFieldListFormViewController.swift */; }; 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 */; }; 0A41BA7F23453A6400D4C0BC /* TextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A41BA7E23453A6400D4C0BC /* TextField.swift */; }; 948DB67E2326DCD90011F916 /* MultiProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 948DB67D2326DCD90011F916 /* MultiProgress.swift */; }; B8200E152280C4CF007245F4 /* ProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8200E142280C4CF007245F4 /* ProgressBar.swift */; }; @@ -204,6 +207,9 @@ 01DF55DF21F8FAA800CC099B /* MFTextFieldListView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MFTextFieldListView.swift; sourceTree = ""; }; 01DF566F21FA5AB300CC099B /* TextFieldListFormViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextFieldListFormViewController.swift; sourceTree = ""; }; 0A12149F22C11A17007C7030 /* ActionDetailWithImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionDetailWithImage.swift; sourceTree = ""; }; + 0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CATransaction+Extension.swift"; sourceTree = ""; }; + 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Checkbox.swift; sourceTree = ""; }; + 0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxWithLabelView.swift; sourceTree = ""; }; 0A41BA7E23453A6400D4C0BC /* TextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextField.swift; sourceTree = ""; }; 948DB67D2326DCD90011F916 /* MultiProgress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiProgress.swift; sourceTree = ""; }; B8200E142280C4CF007245F4 /* ProgressBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressBar.swift; sourceTree = ""; }; @@ -652,6 +658,7 @@ D29DF2A021E7AF4E003B2FB9 /* MVMCoreUIUtility.m */, D29DF2A721E7B2F9003B2FB9 /* MVMCoreUIConstants.h */, D29DF2A821E7B2F9003B2FB9 /* MVMCoreUIConstants.m */, + 0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */, ); path = Utility; sourceTree = ""; @@ -724,6 +731,8 @@ DB891E822253FA8500022516 /* Label.swift */, 0198F7A02256A80A0066C936 /* MFRadioButton.h */, 0198F7A22256A80A0066C936 /* MFRadioButton.m */, + 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */, + 0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */, ); path = Views; sourceTree = ""; @@ -1039,6 +1048,7 @@ D2D6CD4022E78C1A00D701B8 /* Scroller.swift in Sources */, D29DF13021E6851E003B2FB9 /* MVMCoreUITopAlertShortView.m in Sources */, D28B4F8B21FF967C00712C7A /* MVMCoreUIObject.m in Sources */, + 0A1B4A96233BB18F005B3FB4 /* CheckboxWithLabelView.swift in Sources */, D260D7B222D65BDD007E7233 /* MVMCoreUIPageControl.m in Sources */, D29DF26D21E6AA0B003B2FB9 /* FLAnimatedImageView.m in Sources */, D29DF2EF21ECEAE1003B2FB9 /* MFFonts.m in Sources */, @@ -1046,6 +1056,7 @@ D282AACB2243C61700C46919 /* ButtonView.swift in Sources */, D2D6CD4222E78FAB00D701B8 /* ThreeLayerTemplate.swift in Sources */, 0105618F224BBE7700E1557D /* FormValidator+FormParams.swift in Sources */, + 0A7BAFA1232BE61800FB8E22 /* Checkbox.swift in Sources */, D22479962316AF6E003FCCF9 /* HeadlineBodyTextButton.swift in Sources */, D2E1FADD2268B25E00AEFD8C /* MoleculeTableViewCell.swift in Sources */, D29DF2AE21E7B3A4003B2FB9 /* MFTextView.m in Sources */, @@ -1091,6 +1102,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 */, diff --git a/MVMCoreUI/Atoms/Views/Checkbox.swift b/MVMCoreUI/Atoms/Views/Checkbox.swift new file mode 100644 index 00000000..bf8407be --- /dev/null +++ b/MVMCoreUI/Atoms/Views/Checkbox.swift @@ -0,0 +1,418 @@ +// +// Checkbox.swift +// MVMCoreUI +// +// Created by Kevin Christiano on 9/13/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +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. + */ +@objcMembers open class Checkbox: UIControl, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + // Form Validation + var isRequired = false + var fieldKey: String? + var delegateObject: DelegateObject? + + public static let defaultHeightWidth: CGFloat = 18.0 + + /// If true the border of this checkbox will be circular. + public var isRound: Bool = false + + /// 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 + } + } + } + + /// The color of the background when unChecked. + public var unCheckedBackgroundColor: UIColor = .white { + didSet { + if !isSelected { + backgroundColor = unCheckedBackgroundColor + } + } + } + + /// Retrieves ideeal radius value to curve square into a circle. + public var cornerRadiusValue: CGFloat { + return bounds.size.height / 2 + } + + /// Action Block called when the switch is selected. + public var actionBlock: ActionBlock? + + /// Manages the appearance of the checkbox. + private var shapeLayer: CAShapeLayer? + + /// Width of the check mark. + public var checkWidth: CGFloat = 2 { + didSet { + CATransaction.withDisabledAnimations { + shapeLayer?.lineWidth = checkWidth + } + } + } + + /// Color of the check mark. + public var checkColor: UIColor = .black { + didSet { + CATransaction.withDisabledAnimations { + shapeLayer?.strokeColor = checkColor.cgColor + } + } + } + + /// 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(isSelected: isSelected, isAnimated: isAnimated) + + if let delegate = delegateObject as? FormValidationProtocol { + delegate.formValidatorModel?()?.enableByValidation() + } + + updateAccessibilityLabel() + } + } + } + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + + override public init(frame: CGRect) { + super.init(frame: frame) + + accessibilityTraits = .button + accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "checkbox_action_hint") + updateAccessibilityLabel() + + setupView() + } + + /// There is currently no intention on using xib files. + required public init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + fatalError("xib file is not implemented for Checkbox.") + } + + public convenience init() { + self.init(frame:.zero) + } + + public convenience init(isChecked: Bool) { + self.init(frame: .zero) + updateSelectionOnly = true + isSelected = isChecked + updateSelectionOnly = false + } + + public convenience init(checkedBackgroundColor: UIColor, unCheckedBackgroundColor: UIColor, isChecked: Bool = false) { + self.init(frame: .zero) + updateSelectionOnly = true + isSelected = isChecked + updateSelectionOnly = false + self.checkedBackgroundColor = checkedBackgroundColor + self.unCheckedBackgroundColor = unCheckedBackgroundColor + } + + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + + override open func layoutSubviews() { + super.layoutSubviews() + + drawCheck() + layer.cornerRadius = isRound ? cornerRadiusValue : 0 + layer.borderWidth = borderWidth + layer.borderColor = borderColor.cgColor + } + + public func setupView() { + + isUserInteractionEnabled = true + translatesAutoresizingMaskIntoConstraints = false + backgroundColor = .white + } + + //-------------------------------------------------- + // MARK: - Actions + //-------------------------------------------------- + + open override func sendAction(_ action: Selector, to target: Any?, for event: UIEvent?) { + super.sendAction(action, to: target, for: event) + 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?() + } + + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- + + /// Creates the check mark used for the checkbox. + private func drawCheck() { + + 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 = checkMarkPath() + shapeLayer.lineJoin = .bevel + shapeLayer.lineWidth = checkWidth + + CATransaction.withDisabledAnimations { + shapeLayer.strokeEnd = isSelected ? 1 : 0 + } + } + } + + /// Returns a UIBezierPath detailing the path of a checkmark + 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 bezierPath = UIBezierPath() + bezierPath.move(to: startPoint) + bezierPath.addLine(to: pivotOffSet) + bezierPath.addLine(to: endOffset) + + return bezierPath.cgPath + } + + /// 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(isSelected: selected, isAnimated: animated) + } + } + + /// 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") + animateStrokeEnd.timingFunction = CAMediaTimingFunction(name: .linear) + animateStrokeEnd.duration = 0.3 + animateStrokeEnd.fillMode = .both + animateStrokeEnd.isRemovedOnCompletion = false + 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 = isSelected ? self.checkedBackgroundColor : self.unCheckedBackgroundColor + }) + } else { + CATransaction.withDisabledAnimations { + self.shapeLayer?.strokeEnd = isSelected ? 1 : 0 + } + + 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) + } + } + + //-------------------------------------------------- + // MARK: - UITouch + //-------------------------------------------------- + + open override func touchesEnded(_ touches: Set, with event: UIEvent?) { + + sendActions(for: touchIsAcceptablyOutside(touches.first) ? .touchUpOutside : .touchUpInside) + } + + func touchIsAcceptablyOutside(_ touch: UITouch?) -> Bool { + let endLocation = touch?.location(in: self) + let x = endLocation?.x ?? 0.0 + let y = endLocation?.y ?? 0.0 + let faultTolerance: CGFloat = 20.0 + let widthLimit = CGFloat(bounds.size.width + faultTolerance) + let heightLimt = CGFloat(bounds.size.height + faultTolerance) + + return x < -faultTolerance || y < -faultTolerance || x > widthLimit || y > heightLimt + } + + override open func accessibilityActivate() -> Bool { + sendActions(for: .touchUpInside) + return true + } + + //-------------------------------------------------- + // MARK: - Molecular + //-------------------------------------------------- + + open func needsToBeConstrained() -> Bool { + return true + } + + open func reset() { + + backgroundColor = nil + shapeLayer = nil + isSelected = false + } + + open func setAsMolecule() { + setupView() + } + + 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 + } + + if let borderWidth = dictionary["borderWidth"] as? CGFloat { + layer.borderWidth = borderWidth + } + + if let isChecked = dictionary["isChecked"] as? Bool, isChecked { + updateSelectionOnly = true + isSelected = isChecked + updateSelectionOnly = false + } + + if let checkColorHex = dictionary["checkColor"] as? String { + checkColor = UIColor.mfGet(forHex: checkColorHex) + } + + 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 { + self.isRound = isRound + } + + if let actionMap = dictionary.optionalDictionaryForKey("actionMap") { + actionBlock = { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) } + } + } +} + +// MARK:- FormValidationProtocol +extension Checkbox: FormValidationProtocol { + + public func isValidField() -> Bool { + return isRequired ? isSelected : true + } + + public func formFieldName() -> String? { + return fieldKey + } + + public func formFieldValue() -> Any? { + return NSNumber(value: isSelected) + } +} diff --git a/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift b/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift new file mode 100644 index 00000000..55339e61 --- /dev/null +++ b/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift @@ -0,0 +1,200 @@ +// +// CheckboxWithLabelView.swift +// MVMCoreUI +// +// Created by Kevin Christiano on 9/13/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + + +@objcMembers open class CheckboxWithLabelView: ViewConstrainingView { + //-------------------------------------------------- + // MARK: - Outlets + //-------------------------------------------------- + + public let checkbox = Checkbox() + public let label = Label.commonLabelB2(true) + + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + var sizeObject: MFSizeObject? = MFSizeObject(standardSize: Checkbox.defaultHeightWidth, standardiPadPortraitSize: Checkbox.defaultHeightWidth + 6.0) + + var checkboxPosition: CheckboxPosition = .center + + public enum CheckboxPosition: String { + case center + case top + case bottom + } + + //-------------------------------------------------- + // MARK: - Constraints + //-------------------------------------------------- + + var checkboxWidthConstraint: NSLayoutConstraint? + var checkboxHeightConstraint: NSLayoutConstraint? + var checkboxTopConstraint: NSLayoutConstraint? + var checkboxBottomConstraint: NSLayoutConstraint? + var checkboxCenterYConstraint: NSLayoutConstraint? + + //-------------------------------------------------- + // MARK: - Life Cycle + //-------------------------------------------------- + + override open func setupView() { + super.setupView() + + guard subviews.isEmpty else { return } + + translatesAutoresizingMaskIntoConstraints = false + + addSubview(checkbox) + addSubview(label) + + label.text = "" + + 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.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor).isActive = true + + let generalTop = checkbox.topAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.topAnchor) + generalTop.priority = UILayoutPriority(800) + generalTop.isActive = true + + let generalBottom = checkbox.bottomAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.bottomAnchor) + generalBottom.priority = UILayoutPriority(800) + generalBottom.isActive = true + + let checboxBottom = checkbox.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor) + checboxBottom.priority = UILayoutPriority(249) + checboxBottom.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 + + let bottomLabelConstraint = layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: label.bottomAnchor) + bottomLabelConstraint.priority = UILayoutPriority(500) + bottomLabelConstraint.isActive = true + + // layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: label.bottomAnchor).isActive = true + // let labelBottom = label.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor) + // labelBottom.priority = UILayoutPriority(249) + // labelBottom.isActive = true + + alignCheckbox(.center) + } + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + + required public init?(coder: NSCoder) { + super.init(coder: coder) + fatalError("xib file is not implemented for CheckboxWithLabelView") + } + + override public init(frame: CGRect) { + super.init(frame: frame) + setupView() + } + + public convenience init() { + self.init(frame: .zero) + } + + public convenience init(position: CheckboxPosition) { + self.init(frame: .zero) + + alignCheckbox(position) + } + + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- + + /// Aligns Checkbox and Label relative to the desired position of the Checkbox. + private func alignCheckbox(_ position: CheckboxPosition) { + checkboxPosition = position + + switch position { + case .center: + checkboxCenterYConstraint?.isActive = true + checkboxBottomConstraint?.isActive = false + checkboxTopConstraint?.isActive = false + + case .top: + checkboxBottomConstraint?.isActive = false + checkboxTopConstraint?.isActive = true + checkboxCenterYConstraint?.isActive = false + + case .bottom: + checkboxBottomConstraint?.isActive = true + checkboxTopConstraint?.isActive = false + checkboxCenterYConstraint?.isActive = false + } + } +} + +/// MARK: - Molecular +extension CheckboxWithLabelView { + + override open class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + return CGFloat(Checkbox.defaultHeightWidth) + } + + @objc override open func updateView(_ size: CGFloat) { + super.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() + } + + open override func reset() { + super.reset() + + label.text = "" + checkbox.reset() + } + + override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + + guard let dictionary = json else { return } + + if let checkboxAlignment = dictionary["checkboxAlignment"] as? String, let position = CheckboxPosition(rawValue: checkboxAlignment) { + alignCheckbox(position) + } + + checkbox.setWithJSON(dictionary.dictionaryForKey("checkbox"), delegateObject: delegateObject, additionalData: additionalData) + label.setWithJSON(dictionary.dictionaryForKey("label"), delegateObject: delegateObject, additionalData: additionalData) + } +} diff --git a/MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m b/MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m index 2cde1bc3..d219f2f3 100644 --- a/MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m +++ b/MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m @@ -306,6 +306,8 @@ static const CGFloat CheckBoxHeightWidth = 18.0; self.checkedSquare.layer.cornerRadius = self.isRoundRectCheckMark ? 5.0f : 0; } +// TODO:..................................... + #pragma mark - XIB Helpers - (instancetype)awakeAfterUsingCoder:(NSCoder *)aDecoder { diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index 945eb7bf..aeaf6b8d 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -38,11 +38,11 @@ @"caretButton": CaretButton.class, @"textField" : MFTextField.class, @"digitTextField" : MFDigitTextField.class, - @"checkbox" : MVMCoreUICheckBox.class, + @"checkbox" : Checkbox.class, @"cornerLabels" : CornerLabels.class, @"progressBar": ProgressBar.class, @"multiProgressBar": MultiProgress.class, - @"checkbox": MVMCoreUICheckBox.class, + @"checkboxWithLabelView": CheckboxWithLabelView.class, @"listItem": MoleculeTableViewCell.class, @"accordionListItem": AccordionMoleculeTableViewCell.class, @"switch": MVMCoreUISwitch.class, diff --git a/MVMCoreUI/Utility/CATransaction+Extension.swift b/MVMCoreUI/Utility/CATransaction+Extension.swift new file mode 100644 index 00000000..66bb33d3 --- /dev/null +++ b/MVMCoreUI/Utility/CATransaction+Extension.swift @@ -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() + } +}